diff --git a/Changes.md b/Changes.md index 03aff6d322f..112de480124 100644 --- a/Changes.md +++ b/Changes.md @@ -8,6 +8,7 @@ Improvements - OSLShader : - Improved loading of spline parameters with additional duplicate endpoints. - Added support for loading splines from RenderMan shaders. +- Rendering : Improved detection of static objects. This fixes messages from Arnold like "discarded X duplicate deformation keys", and may improve performance in some cases. Fixes ----- diff --git a/python/GafferSceneTest/RendererAlgoTest.py b/python/GafferSceneTest/RendererAlgoTest.py index 4e832e2a146..fb576bf3a65 100644 --- a/python/GafferSceneTest/RendererAlgoTest.py +++ b/python/GafferSceneTest/RendererAlgoTest.py @@ -61,6 +61,42 @@ def testObjectSamples( self ) : self.assertEqual( [ s.radius() for s in samples ], [ 0.75, 1.25 ] ) + def testObjectSamplesMatchingHash( self ) : + + # If the input object has a hash that doesn't vary with time, then we just get one sample when + # we try to sample over time. + sphere = GafferScene.Sphere() + sphere["type"].setValue( sphere.Type.Primitive ) + + with Gaffer.Context() as c : + c["scene:path"] = IECore.InternedStringVectorData( [ "sphere" ] ) + samples = GafferScene.Private.RendererAlgo.objectSamples( sphere["out"]["object"], [ 0.75, 1.25 ] ) + + self.assertEqual( [ s.radius() for s in samples ], [ 1.0 ] ) + + def testObjectSamplesMatchingResult( self ) : + + # If the input object has different hashes at different times, but still produces identical objects, + # then we should still only output one sample. + + frame = GafferTest.FrameNode() + + # Quick hack to produce a value that doesn't vary, but has a hash that does: feed the frame + # through an integer multiply node, which will truncate the value to an integer + multiply = GafferTest.MultiplyNode() + multiply["op1"].setValue( 1 ) + multiply["op2"].setInput( frame["output"] ) + + sphere = GafferScene.Sphere() + sphere["type"].setValue( sphere.Type.Primitive ) + sphere["radius"].setInput( multiply["product"] ) + + with Gaffer.Context() as c : + c["scene:path"] = IECore.InternedStringVectorData( [ "sphere" ] ) + samples = GafferScene.Private.RendererAlgo.objectSamples( sphere["out"]["object"], [ 1.25, 1.75 ] ) + + self.assertEqual( [ s.radius() for s in samples ], [ 1.0 ] ) + def testNonInterpolableObjectSamples( self ) : frame = GafferTest.FrameNode() diff --git a/src/GafferScene/RendererAlgo.cpp b/src/GafferScene/RendererAlgo.cpp index 2b17d82a9d8..301e321314d 100644 --- a/src/GafferScene/RendererAlgo.cpp +++ b/src/GafferScene/RendererAlgo.cpp @@ -556,6 +556,9 @@ bool objectSamples( const ObjectPlug *objectPlug, const std::vector &samp const Context *frameContext = Context::current(); Context::EditableScope timeContext( frameContext ); + bool actualMovement = false; + IECore::MurmurHash actualHash; + samples.reserve( sampleTimes.size() ); for( size_t i = 0; i < sampleTimes.size(); i++ ) { @@ -568,6 +571,15 @@ bool objectSamples( const ObjectPlug *objectPlug, const std::vector &samp runTimeCast( object.get() ) ) { + if( i == 0 ) + { + actualHash = object->hash(); + } + else if( !actualMovement ) + { + actualMovement = object->hash() != actualHash; + } + samples.push_back( object.get() ); } else if( @@ -608,6 +620,11 @@ bool objectSamples( const ObjectPlug *objectPlug, const std::vector &samp break; } } + + if( !actualMovement && samples.size() > 1 ) + { + samples.resize( 1 ); + } } if( hash )