From ddad2014d2cf8178b73de50adf43a18081fc1312 Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Thu, 14 Sep 2017 12:03:15 -0700 Subject: [PATCH 01/26] IE Config : Add solidangle_LICENSE to ENV_VARS_TO_IMPORT This is needed for some of the Arnold tests to pass. --- config/ie/options | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/ie/options b/config/ie/options index 07dff23cb9..f4d227d783 100644 --- a/config/ie/options +++ b/config/ie/options @@ -387,7 +387,7 @@ HDF5_LIB_PATH = os.path.join( "/software/apps/hdf5", hdf5Version, platform, comp DOXYGEN = os.path.join( "/software/apps/doxygen", os.environ["DOXYGEN_VERSION"], platform, "bin", "doxygen" ) # import vars we need to get our doxygen and python wrappers working -ENV_VARS_TO_IMPORT="PATH COMPILER COMPILER_VERSION PYTHONPATH IEENV_ROOT IEENV_WORKING_PATH IEENV_LIBRARY_PREFIX_PATH DOXYGEN_VERSION IEENV_DEBUG IEENV_DEBUG_PYTHON IEENV_DEBUGGER IEENV_DEBUGGER_ARGS DELIGHT_CONF SCONS_VERSION DL_VERSION DL_SHADERS_PATH DL_DISPLAYS_PATH" +ENV_VARS_TO_IMPORT="PATH COMPILER COMPILER_VERSION PYTHONPATH IEENV_ROOT IEENV_WORKING_PATH IEENV_LIBRARY_PREFIX_PATH DOXYGEN_VERSION IEENV_DEBUG IEENV_DEBUG_PYTHON IEENV_DEBUGGER IEENV_DEBUGGER_ARGS DELIGHT_CONF SCONS_VERSION DL_VERSION DL_SHADERS_PATH DL_DISPLAYS_PATH solidangle_LICENSE" # make sure the tests can run testLibs = [ pythonReg["location"] + "/" + compiler + "/" + compilerVersion + "/lib", compilerReg["location"] + "/lib" ] From ba7a2f9d95e5a4ccef3a252b2b884d6732826ecf Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Thu, 21 Sep 2017 13:43:53 -0700 Subject: [PATCH 02/26] SConstruct : Arnold tests depend on Arnold lib --- SConstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index 38a0e392a6..b25a1fb4d5 100644 --- a/SConstruct +++ b/SConstruct @@ -3101,7 +3101,7 @@ if doConfigure : arnoldTestEnv["ENV"]["ARNOLD_PLUGIN_PATH"] = "contrib/IECoreArnold/test/IECoreArnold/plugins" arnoldTest = arnoldTestEnv.Command( "contrib/IECoreArnold/test/IECoreArnold/results.txt", arnoldPythonModule, pythonExecutable + " $TEST_ARNOLD_SCRIPT" ) NoCache( arnoldTest ) - arnoldTestEnv.Depends( arnoldTest, [ arnoldPythonModule + arnoldProceduralForTest + arnoldDriverForTest ] ) + arnoldTestEnv.Depends( arnoldTest, [ arnoldPythonModule + arnoldProceduralForTest + arnoldDriverForTest + arnoldLibrary ] ) arnoldTestEnv.Depends( arnoldTest, glob.glob( "contrib/IECoreArnold/test/IECoreArnold/*.py" ) ) arnoldTestEnv.Alias( "testArnold", arnoldTest ) From 6e51598501990cbc8fbb4d138abfd2b8cc26fb01 Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Thu, 14 Sep 2017 12:05:26 -0700 Subject: [PATCH 03/26] IECoreArnold::PointsTest and RenderTest : This test could never have passed due to substantial watermarks baked into the bright values of the comparison image. We now assume tests will run with a license, and I've regenerated the comparison image --- .../test/IECoreArnold/PointsTest.py | 3 --- .../test/IECoreArnold/RendererTest.py | 3 --- .../IECoreArnold/data/curveImages/bSpline.exr | Bin 68669 -> 102971 bytes .../IECoreArnold/data/curveImages/bezier.exr | Bin 134237 -> 177761 bytes .../IECoreArnold/data/pointsImages/points.tif | Bin 19392 -> 15688 bytes 5 files changed, 6 deletions(-) diff --git a/contrib/IECoreArnold/test/IECoreArnold/PointsTest.py b/contrib/IECoreArnold/test/IECoreArnold/PointsTest.py index b2fa525fa8..5e29b18389 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/PointsTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/PointsTest.py @@ -106,9 +106,6 @@ def testDiskRendering( self ) : image = IECore.ImageDisplayDriver.removeStoredImage( "testHandle" ) del image["A"] - # raise blackPoint massively to remove possible watermark - IECore.Grade()( input = image, copyInput = False, blackPoint = IECore.Color3f( 0.9 ) ) - expectedImage = IECore.Reader.create( os.path.dirname( __file__ ) + "/data/pointsImages/points.tif" ).read() IECore.ImageWriter.create( image, "/tmp/test.tif" ).write() diff --git a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py index 63c1bb7df9..56edef31e5 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py @@ -487,9 +487,6 @@ def performCurvesTest( self, curvesPrimitive, expectedImage ) : image = IECore.ImageDisplayDriver.removeStoredImage( "test" ) del image["A"] - # raise blackPoint massively to remove possible watermark - IECore.Grade()( input = image, copyInput = False, blackPoint = IECore.Color3f( 0.9 ) ) - expectedImage = IECore.Reader.create( expectedImage ).read() self.assertEqual( IECore.ImageDiffOp()( imageA=image, imageB=expectedImage, maxError=0.01 ), IECore.BoolData( False ) ) diff --git a/contrib/IECoreArnold/test/IECoreArnold/data/curveImages/bSpline.exr b/contrib/IECoreArnold/test/IECoreArnold/data/curveImages/bSpline.exr index 56c02244cc398d94fc874b622846c6eceb8fd383..b229dd26ab9152418783945f8d5e313a6f197a75 100644 GIT binary patch literal 102971 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%0D$Z02+AnR{=jC;H~}WNf{w* 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 literal 68669 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 QNfwYS01}b~Bn!a*16`TnmH+?% diff --git a/contrib/IECoreArnold/test/IECoreArnold/data/curveImages/bezier.exr b/contrib/IECoreArnold/test/IECoreArnold/data/curveImages/bezier.exr index ea675b78d46599ca8149e2f6a55713f821c7316d..036f125b96ad96ab2e215d80d4470ecb98edf3e7 100644 GIT binary patch literal 177761 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௿@EtK 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 literal 134237 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 diff --git a/contrib/IECoreArnold/test/IECoreArnold/data/pointsImages/points.tif b/contrib/IECoreArnold/test/IECoreArnold/data/pointsImages/points.tif index d84412347eaa15e337f1e01f37782bbb7a5de4e7..dc2967a30d41b1afd5c01296291f0dc4618766e1 100644 GIT binary patch literal 15688 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:iDAjeN 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 literal 19392 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 GgZ>W&qZg Date: Thu, 14 Sep 2017 14:44:09 -0700 Subject: [PATCH 04/26] IECoreArnold::ProceduralTest : Need a tiny tolerance when checking values of rendered pixels, to account for floating point error in filter normalization. ( I couldn't figure out any easy way to swtich to a box filter ) --- contrib/IECoreArnold/test/IECoreArnold/ProceduralTest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/IECoreArnold/test/IECoreArnold/ProceduralTest.py b/contrib/IECoreArnold/test/IECoreArnold/ProceduralTest.py index f5ae55cf93..0be1956b9e 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/ProceduralTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/ProceduralTest.py @@ -186,9 +186,9 @@ def testProceduralInheritsShader( self ) : e = IECore.ImagePrimitiveEvaluator( i ) r = e.createResult() self.assertEqual( e.pointAtUV( IECore.V2f( 0.5 ), r ), True ) - self.assertEqual( r.floatPrimVar( e.R() ), 0 ) - self.assertEqual( r.floatPrimVar( e.G() ), 1 ) - self.assertEqual( r.floatPrimVar( e.B() ), 0 ) + self.assertAlmostEqual( r.floatPrimVar( e.R() ), 0, 6 ) + self.assertAlmostEqual( r.floatPrimVar( e.G() ), 1, 6 ) + self.assertAlmostEqual( r.floatPrimVar( e.B() ), 0, 6 ) def testEmptyProceduralIsIgnored( self ) : From 574ec8a6b967165e8cf14e76d6982735d1db1bcf Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Mon, 18 Sep 2017 11:13:01 -0700 Subject: [PATCH 05/26] IECoreArnold::RendererTest : With extreme motion blur, we need more samples to reliably converge ( in Arnold 4, the tests were passing due to good luck ) --- contrib/IECoreArnold/test/IECoreArnold/RendererTest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py index 56edef31e5..72881c7189 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py @@ -783,7 +783,7 @@ def testDeformationMotionBlur( self ) : r = IECoreArnold.Renderer() r.display( "test", "ieDisplay", "rgba", { "driverType" : "ImageDisplayDriver", "handle" : "test" } ) - r.setOption( "ai:AA_samples", IECore.IntData( 10 ) ) + r.setOption( "ai:AA_samples", IECore.IntData( 20 ) ) r.camera( "main", { "resolution" : IECore.V2i( 128, 128 ), "shutter" : IECore.V2f( 0, 1 ) } ) @@ -811,7 +811,7 @@ def testTransformationMotionBlur( self ) : r = IECoreArnold.Renderer() r.display( "test", "ieDisplay", "rgba", { "driverType" : "ImageDisplayDriver", "handle" : "test" } ) - r.setOption( "ai:AA_samples", IECore.IntData( 10 ) ) + r.setOption( "ai:AA_samples", IECore.IntData( 20 ) ) r.camera( "main", { "resolution" : IECore.V2i( 128, 128 ), "shutter" : IECore.V2f( 0, 1 ) } ) From cacc56c56b9a21085c78d77fd316b6f42c0908e7 Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Thu, 21 Sep 2017 18:46:21 -0700 Subject: [PATCH 06/26] IECoreArnold tests : Don't dump render output during tests --- contrib/IECoreArnold/test/IECoreArnold/OutputDriverTest.py | 4 ++-- contrib/IECoreArnold/test/IECoreArnold/ProceduralDSOTest.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/IECoreArnold/test/IECoreArnold/OutputDriverTest.py b/contrib/IECoreArnold/test/IECoreArnold/OutputDriverTest.py index 60a9962b83..9c5e7ce16c 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/OutputDriverTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/OutputDriverTest.py @@ -48,7 +48,7 @@ def testMergedDisplays( self ) : server = IECore.DisplayDriverServer( 1559 ) time.sleep( 2 ) - os.system( "kick -dw -dp contrib/IECoreArnold/test/IECoreArnold/data/assFiles/mergedDisplays.ass" ) + os.system( "kick -v 0 -dw -dp contrib/IECoreArnold/test/IECoreArnold/data/assFiles/mergedDisplays.ass" ) image = IECore.ImageDisplayDriver.removeStoredImage( "mergedImage" ) channelNames = image.keys() @@ -67,7 +67,7 @@ def testVectorAndPointDisplays( self ) : server = IECore.DisplayDriverServer( 1559 ) time.sleep( 2 ) - os.system( "kick -dw -dp contrib/IECoreArnold/test/IECoreArnold/data/assFiles/vectorAndPointDisplays.ass" ) + os.system( "kick -v 0 -dw -dp contrib/IECoreArnold/test/IECoreArnold/data/assFiles/vectorAndPointDisplays.ass" ) image = IECore.ImageDisplayDriver.removeStoredImage( "vectorAndPointImage" ) channelNames = image.keys() diff --git a/contrib/IECoreArnold/test/IECoreArnold/ProceduralDSOTest.py b/contrib/IECoreArnold/test/IECoreArnold/ProceduralDSOTest.py index 0ac2758efd..e36885b165 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/ProceduralDSOTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/ProceduralDSOTest.py @@ -44,7 +44,7 @@ class ProceduralDSOTest( unittest.TestCase ) : def test( self ) : - os.system( "kick -dw -dp contrib/IECoreArnold/test/IECoreArnold/data/assFiles/proceduralDSO.ass" ) + os.system( "kick -v 0 -dw -dp contrib/IECoreArnold/test/IECoreArnold/data/assFiles/proceduralDSO.ass" ) image = IECore.EXRImageReader( "testProceduralDSO.exr" ).read() evaluator = IECore.ImagePrimitiveEvaluator( image ) From 5320eae510ac30f18ada54f49f038f26c65af7d2 Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Fri, 15 Sep 2017 16:14:59 -0700 Subject: [PATCH 07/26] IECoreArnold tests : When testing example parameters, use ones that still exist in Arnold 5 --- .../test/IECoreArnold/ParameterAlgoTest.py | 27 ++++++++++--------- .../test/IECoreArnold/RendererTest.py | 16 +++++------ 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/contrib/IECoreArnold/test/IECoreArnold/ParameterAlgoTest.py b/contrib/IECoreArnold/test/IECoreArnold/ParameterAlgoTest.py index 98b9a306b2..86b3c3b70d 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/ParameterAlgoTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/ParameterAlgoTest.py @@ -53,38 +53,39 @@ def testSetParameter( self ) : with IECoreArnold.UniverseBlock( writable = True ) : - n = arnold.AiNode( "standard" ) + n = arnold.AiNode( "standard_surface" ) - IECoreArnold.ParameterAlgo.setParameter( n, "Kd", IECore.FloatData( 0.25 ) ) - IECoreArnold.ParameterAlgo.setParameter( n, "aov_emission", IECore.StringData( "test" ) ) + IECoreArnold.ParameterAlgo.setParameter( n, "base", IECore.FloatData( 0.25 ) ) + IECoreArnold.ParameterAlgo.setParameter( n, "customString", IECore.StringData( "test" ) ) - self.assertEqual( arnold.AiNodeGetFlt( n, "Kd" ), 0.25 ) - self.assertEqual( arnold.AiNodeGetStr( n, "aov_emission" ), "test" ) + self.assertEqual( arnold.AiNodeGetFlt( n, "base" ), 0.25 ) + self.assertEqual( arnold.AiNodeGetStr( n, "customString" ), "test" ) def testGetParameter( self ) : with IECoreArnold.UniverseBlock( writable = True ) : - n = arnold.AiNode( "standard" ) + n = arnold.AiNode( "standard_surface" ) self.assertEqual( - IECoreArnold.ParameterAlgo.getParameter( n, "Kd" ), - IECore.FloatData( arnold.AiNodeGetFlt( n, "Kd" ) ) + IECoreArnold.ParameterAlgo.getParameter( n, "base" ), + IECore.FloatData( arnold.AiNodeGetFlt( n, "base" ) ) ) + IECore.FloatData( arnold.AiNodeSetStr( n, "name", "testString" ) ) self.assertEqual( - IECoreArnold.ParameterAlgo.getParameter( n, "aov_emission" ), - IECore.StringData( "emission" ), + IECoreArnold.ParameterAlgo.getParameter( n, "name" ), + IECore.StringData( "testString" ), ) def testDoubleData( self ) : with IECoreArnold.UniverseBlock( writable = True ) : - n = arnold.AiNode( "standard" ) + n = arnold.AiNode( "standard_surface" ) - IECoreArnold.ParameterAlgo.setParameter( n, "Kd", IECore.DoubleData( 0.25 ) ) - self.assertEqual( arnold.AiNodeGetFlt( n, "Kd" ), 0.25 ) + IECoreArnold.ParameterAlgo.setParameter( n, "base", IECore.DoubleData( 0.25 ) ) + self.assertEqual( arnold.AiNodeGetFlt( n, "base" ), 0.25 ) IECoreArnold.ParameterAlgo.setParameter( n, "customFloat", IECore.DoubleData( 0.25 ) ) self.assertEqual( arnold.AiNodeGetFlt( n, "customFloat" ), 0.25 ) diff --git a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py index 72881c7189..ac552973ed 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py @@ -68,9 +68,9 @@ def testOptions( self ) : self.assertEqual( r.getOption( "ai:AA_samples" ), IECore.IntData( 11 ) ) # check we can set an already existing float - self.assertEqual( r.getOption( "ai:auto_transparency_threshold" ), IECore.FloatData( .99 ) ) - r.setOption( "ai:auto_transparency_threshold", IECore.FloatData( .9 ) ) - self.assertEqual( r.getOption( "ai:auto_transparency_threshold" ), IECore.FloatData( .9 ) ) + self.assertEqual( r.getOption( "ai:texture_max_sharpen" ), IECore.FloatData( 1.5 ) ) + r.setOption( "ai:texture_max_sharpen", IECore.FloatData( .9 ) ) + self.assertEqual( r.getOption( "ai:texture_max_sharpen" ), IECore.FloatData( .9 ) ) # check tbat trying to set nonexistent options yields a message m = IECore.CapturingMessageHandler() @@ -166,7 +166,7 @@ def testShader( self ) : with IECore.WorldBlock( r ) : - r.shader( "surface", "standard", { "emission" : 1.0, "emission_color" : IECore.Color3f( 1, 0, 0 ) } ) + r.shader( "surface", "standard_surface", { "emission" : 1.0, "emission_color" : IECore.Color3f( 1, 0, 0 ) } ) r.sphere( 1, -1, 1, 360, {} ) image = IECore.ImageDisplayDriver.removeStoredImage( "test" ) @@ -187,7 +187,7 @@ def testReferenceExistingShader( self ) : with IECore.WorldBlock( r ) : - shader = arnold.AiNode( "standard" ) + shader = arnold.AiNode( "standard_surface" ) arnold.AiNodeSetStr( shader, "name", "red_shader" ) arnold.AiNodeSetFlt( shader, "emission", 1 ) arnold.AiNodeSetRGB( shader, "emission_color", 1, 0, 0 ) @@ -654,7 +654,7 @@ def testShaderConnections( self ) : r.concatTransform( IECore.M44f.createTranslated( IECore.V3f( 0, 0, -5 ) ) ) r.shader( "shader", "flat", { "color" : IECore.Color3f( 1, 0, 0 ), "__handle" : "myInputShader" } ) - r.shader( "surface", "standard", { "emission" : 1.0, "emission_color" : "link:myInputShader" } ) + r.shader( "surface", "standard_surface", { "emission" : 1.0, "emission_color" : "link:myInputShader" } ) mesh = IECore.MeshPrimitive.createPlane( IECore.Box2f( IECore.V2f( -1 ), IECore.V2f( 1 ) ) ) mesh.render( r ) @@ -681,7 +681,7 @@ def testMissingShaderConnectionWarnings( self ) : m = IECore.CapturingMessageHandler() with m : r.shader( "shader", "flat", { "color" : IECore.Color3f( 1, 0, 0 ), "__handle" : "myInputShader" } ) - r.shader( "surface", "standard", { "emission" : 1.0, "emission_color" : "link:oopsWrongOne" } ) + r.shader( "surface", "standard_surface", { "emission" : 1.0, "emission_color" : "link:oopsWrongOne" } ) self.assertEqual( len( m.messages ), 1 ) self.assertEqual( m.messages[0].level, IECore.Msg.Level.Warning ) @@ -699,7 +699,7 @@ def testLight( self ) : r.concatTransform( IECore.M44f.createTranslated( IECore.V3f( 0, 0, -1 ) ) ) - r.shader( "surface", "standard", {} ) + r.shader( "surface", "standard_surface", {} ) mesh = IECore.MeshPrimitive.createPlane( IECore.Box2f( IECore.V2f( -1 ), IECore.V2f( 1 ) ) ) mesh.render( r ) From 86fcb480dee98f9b7f84f1c5cb2b1d7063d8c802 Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Fri, 15 Sep 2017 15:46:40 -0700 Subject: [PATCH 08/26] IECoreArnold tests : Signature of AiNodeGetMatrix changed in Arnold 5 --- .../IECoreArnold/test/IECoreArnold/ParameterAlgoTest.py | 7 +++---- contrib/IECoreArnold/test/IECoreArnold/SphereAlgoTest.py | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/contrib/IECoreArnold/test/IECoreArnold/ParameterAlgoTest.py b/contrib/IECoreArnold/test/IECoreArnold/ParameterAlgoTest.py index 86b3c3b70d..beb3436e84 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/ParameterAlgoTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/ParameterAlgoTest.py @@ -91,11 +91,10 @@ def testDoubleData( self ) : self.assertEqual( arnold.AiNodeGetFlt( n, "customFloat" ), 0.25 ) IECoreArnold.ParameterAlgo.setParameter( n, "customMatrix", IECore.M44dData( IECore.M44d( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ) ) ) - m = arnold.AtMatrix() - arnold.AiNodeGetMatrix( n, "customMatrix", m ) + m = arnold.AiNodeGetMatrix( n, "customMatrix" ) self.assertEqual( - [ getattr( m, f[0] ) for f in m._fields_ ], - [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ], + [ list( i ) for i in m.data ], + [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16] ], ) def testStringArray( self ) : diff --git a/contrib/IECoreArnold/test/IECoreArnold/SphereAlgoTest.py b/contrib/IECoreArnold/test/IECoreArnold/SphereAlgoTest.py index 56d7b94112..661a107033 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/SphereAlgoTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/SphereAlgoTest.py @@ -88,12 +88,12 @@ def testPrimitiveVariables( self ) : self.assertEqual( arnold.AiNodeGetBool( n, "b" ), True ) self.assertEqual( arnold.AiNodeGetFlt( n, "f" ), 2.5 ) - m = arnold.AtMatrix() - arnold.AiNodeGetMatrix( n, "m", m ) + m = arnold.AiNodeGetMatrix( n, "m" ) self.assertEqual( - [ getattr( m, f[0] ) for f in m._fields_ ], - [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ], + [ list( i ) for i in m.data ], + [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16] ], ) + if __name__ == "__main__": unittest.main() From efa1b5d81ecb592c55cc6db115c0c9d80723d2ab Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Mon, 18 Sep 2017 11:47:51 -0700 Subject: [PATCH 09/26] IECoreArnold::UniverseBlockTest : Accessing Metadata from Python now requires undocumented functions --- .../IECoreArnold/test/IECoreArnold/UniverseBlockTest.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/contrib/IECoreArnold/test/IECoreArnold/UniverseBlockTest.py b/contrib/IECoreArnold/test/IECoreArnold/UniverseBlockTest.py index 8dc082e7b3..532f9cf8ed 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/UniverseBlockTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/UniverseBlockTest.py @@ -91,17 +91,18 @@ def testMetadataLoading( self ) : e = arnold.AiNodeEntryLookUp( "options" ) - s = ctypes.c_char_p() + s = arnold.AtStringReturn() i = ctypes.c_int() + arnold.AiMetaDataGetStr( e, "", "cortex.testString", s ) - self.assertEqual( s.value, "test" ) + self.assertEqual( arnold.AtStringToStr( s ), "test" ) arnold.AiMetaDataGetInt( e, "", "cortex.testInt", i ) self.assertEqual( i.value, 25 ) arnold.AiMetaDataGetStr( e, "AA_samples", "cortex.testString", s ) - self.assertEqual( s.value, "test2" ) + self.assertEqual( arnold.AtStringToStr( s ), "test2" ) arnold.AiMetaDataGetInt( e, "AA_samples", "cortex.testInt", i ) self.assertEqual( i.value, 12 ) From 4130ffa88aa5474e0b503dbbcffd22929fd023d3 Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Thu, 21 Sep 2017 19:11:10 -0700 Subject: [PATCH 10/26] IECoreArnold::RendererTest : Use correct types so we don't get warnings --- contrib/IECoreArnold/test/IECoreArnold/RendererTest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py index ac552973ed..82740443a0 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py @@ -613,7 +613,7 @@ def testShapeAttributes( self ) : shapes = self.__allNodes( type = arnold.AI_NODE_SHAPE ) self.assertEqual( len( shapes ), 1 ) - self.assertEqual( arnold.AiNodeGetInt( shapes[0], "subdiv_iterations" ), 10 ) + self.assertEqual( arnold.AiNodeGetByte( shapes[0], "subdiv_iterations" ), 10 ) def testEnumAttributes( self ) : @@ -695,7 +695,7 @@ def testLight( self ) : with IECore.WorldBlock( r ) : - r.light( "point_light", "handle", { "intensity" : 1, "color" : IECore.Color3f( 1, 0.5, 0.25 ) } ) + r.light( "point_light", "handle", { "intensity" : 1.0, "color" : IECore.Color3f( 1, 0.5, 0.25 ) } ) r.concatTransform( IECore.M44f.createTranslated( IECore.V3f( 0, 0, -1 ) ) ) From 73f97986a2b303918d03afcdf922d7d3518ae96f Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Thu, 14 Sep 2017 16:08:56 -0700 Subject: [PATCH 11/26] IECoreArnold : Arnold 5 removes point types, and just uses vectors --- .../src/IECoreArnold/CameraAlgo.cpp | 4 +-- .../src/IECoreArnold/MeshAlgo.cpp | 12 ++++----- .../src/IECoreArnold/ParameterAlgo.cpp | 25 +++---------------- .../IECoreArnold/RendererImplementation.cpp | 4 +-- .../src/IECoreArnold/ShapeAlgo.cpp | 4 +-- .../ProceduralHolderTranslator.cpp | 4 +-- .../outputDriver/OutputDriver.cpp | 3 --- .../test/IECoreArnold/CameraAlgoTest.py | 4 +-- .../test/IECoreArnold/CurvesTest.py | 4 +-- .../test/IECoreArnold/MeshTest.py | 18 ++++++------- .../test/IECoreArnold/PointsTest.py | 4 +-- .../test/IECoreArnold/RendererTest.py | 4 +-- 12 files changed, 34 insertions(+), 56 deletions(-) diff --git a/contrib/IECoreArnold/src/IECoreArnold/CameraAlgo.cpp b/contrib/IECoreArnold/src/IECoreArnold/CameraAlgo.cpp index 64503f0906..6d2a3c14f2 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/CameraAlgo.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/CameraAlgo.cpp @@ -89,8 +89,8 @@ AtNode *CameraAlgo::convert( const IECore::Camera *camera ) const Imath::V2i &resolution = cameraCopy->parametersData()->member( "resolution", true )->readable(); const float pixelAspectRatio = cameraCopy->parametersData()->member( "pixelAspectRatio", true )->readable(); float aspect = pixelAspectRatio * (float)resolution.x / (float)resolution.y; - AiNodeSetPnt2( result, "screen_window_min", screenWindow.min.x, screenWindow.min.y * aspect ); - AiNodeSetPnt2( result, "screen_window_max", screenWindow.max.x, screenWindow.max.y * aspect ); + AiNodeSetVec2( result, "screen_window_min", screenWindow.min.x, screenWindow.min.y * aspect ); + AiNodeSetVec2( result, "screen_window_max", screenWindow.max.x, screenWindow.max.y * aspect ); // Set any Arnold-specific parameters diff --git a/contrib/IECoreArnold/src/IECoreArnold/MeshAlgo.cpp b/contrib/IECoreArnold/src/IECoreArnold/MeshAlgo.cpp index 60cd924784..a8397c881a 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/MeshAlgo.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/MeshAlgo.cpp @@ -126,13 +126,13 @@ void convertIndexedUVSet( const std::string &setName, PrimitiveVariableMap &vari const vector &indices = indicesData->readable(); int numUVs = 1 + *max_element( indices.begin(), indices.end() ); - AtArray *uvsArray = AiArrayAllocate( numUVs, 1, AI_TYPE_POINT2 ); + AtArray *uvsArray = AiArrayAllocate( numUVs, 1, AI_TYPE_VECTOR2 ); AtArray *indicesArray = AiArrayAllocate( indices.size(), 1, AI_TYPE_UINT ); for( size_t i = 0, e = indices.size(); i < e; ++i ) { - AtPoint2 uv = { s[i], 1.0f - t[i] }; - AiArraySetPnt2( uvsArray, indices[i], uv ); + AtVector2 uv( s[i], 1.0f - t[i] ); + AiArraySetVec2( uvsArray, indices[i], uv ); AiArraySetUInt( indicesArray, i, indices[i] ); } @@ -190,11 +190,11 @@ void convertUVSet( const std::string &setName, PrimitiveVariableMap &variables, const vector &s = sData->readable(); const vector &t = tData->readable(); - AtArray *uvsArray = AiArrayAllocate( s.size(), 1, AI_TYPE_POINT2 ); + AtArray *uvsArray = AiArrayAllocate( s.size(), 1, AI_TYPE_VECTOR2 ); for( size_t i = 0, e = s.size(); i < e; ++i ) { - AtPoint2 uv = { s[i], 1.0f - t[i] }; - AiArraySetPnt2( uvsArray, i, uv ); + AtVector2 uv( s[i], 1.0f - t[i] ); + AiArraySetVec2( uvsArray, i, uv ); } AtArray *indicesArray = NULL; diff --git a/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp b/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp index e08303d3cd..084135d8ac 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp @@ -148,11 +148,11 @@ void setParameterInternal( AtNode *node, const char *name, int parameterType, bo AiNodeSetBool( node, name, data->readable() ); } break; - case AI_TYPE_POINT2 : + case AI_TYPE_VECTOR2 : if( const V2fData *data = dataCast( name, value ) ) { const Imath::V2f &v = data->readable(); - AiNodeSetPnt2( node, name, v.x, v.y ); + AiNodeSetVec2( node, name, v.x, v.y ); } break; case AI_TYPE_VECTOR : @@ -162,13 +162,6 @@ void setParameterInternal( AtNode *node, const char *name, int parameterType, bo AiNodeSetVec( node, name, v.x, v.y, v.z ); } break; - case AI_TYPE_POINT : - if( const V3fData *data = dataCast( name, value ) ) - { - const Imath::V3f &v = data->readable(); - AiNodeSetPnt( node, name, v.x, v.y, v.z ); - } - break; case AI_TYPE_MATRIX : if( const M44dData *data = runTimeCast( value ) ) { @@ -460,19 +453,7 @@ int parameterType( IECore::TypeId dataType, bool &array ) int parameterType( const IECore::Data *data, bool &array ) { - int type = parameterType( data->typeId(), array ); - - // if we have data of type vector, its interpretation matters - if( type == AI_TYPE_VECTOR ) - { - GeometricData::Interpretation interpretation = getGeometricInterpretation( data ); - if( interpretation == GeometricData::Point ) - { - type = AI_TYPE_POINT; - } - } - - return type; + return parameterType( data->typeId(), array ); } AtArray *dataToArray( const IECore::Data *data, int aiType ) diff --git a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp index 65307a0d08..a016f522e4 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp @@ -724,8 +724,8 @@ void IECoreArnold::RendererImplementation::procedural( IECore::Renderer::Procedu if( bound != Procedural::noBound ) { - AiNodeSetPnt( node, "min", bound.min.x, bound.min.y, bound.min.z ); - AiNodeSetPnt( node, "max", bound.max.x, bound.max.y, bound.max.z ); + AiNodeSetVec( node, "min", bound.min.x, bound.min.y, bound.min.z ); + AiNodeSetVec( node, "max", bound.max.x, bound.max.y, bound.max.z ); } else { diff --git a/contrib/IECoreArnold/src/IECoreArnold/ShapeAlgo.cpp b/contrib/IECoreArnold/src/IECoreArnold/ShapeAlgo.cpp index 01cfb2ad99..11632bade1 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/ShapeAlgo.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/ShapeAlgo.cpp @@ -121,7 +121,7 @@ void convertP( const IECore::Primitive *primitive, AtNode *shape, const char *na AiNodeSetArray( shape, name, - AiArrayConvert( p->readable().size(), 1, AI_TYPE_POINT, (void *)&( p->readable()[0] ) ) + AiArrayConvert( p->readable().size(), 1, AI_TYPE_VECTOR, (void *)&( p->readable()[0] ) ) ); } @@ -140,7 +140,7 @@ void convertP( const std::vector &samples, AtNode *sh dataSamples.push_back( p ); } - AtArray *array = ParameterAlgo::dataToArray( dataSamples, AI_TYPE_POINT ); + AtArray *array = ParameterAlgo::dataToArray( dataSamples, AI_TYPE_VECTOR ); AiNodeSetArray( shape, name, array ); } diff --git a/contrib/IECoreArnold/src/IECoreArnold/mtoaExtension/ProceduralHolderTranslator.cpp b/contrib/IECoreArnold/src/IECoreArnold/mtoaExtension/ProceduralHolderTranslator.cpp index fbc1a82612..fa272b6e5f 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/mtoaExtension/ProceduralHolderTranslator.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/mtoaExtension/ProceduralHolderTranslator.cpp @@ -158,8 +158,8 @@ class ProceduralHolderTranslator : public CShapeTranslator MFnDagNode fnDagNode( m_dagPath ); MBoundingBox bound = fnDagNode.boundingBox(); - AiNodeSetPnt( node, "min", bound.min().x, bound.min().y, bound.min().z ); - AiNodeSetPnt( node, "max", bound.max().x, bound.max().y, bound.max().z ); + AiNodeSetVec( node, "min", bound.min().x, bound.min().y, bound.min().z ); + AiNodeSetVec( node, "max", bound.max().x, bound.max().y, bound.max().z ); const char *dsoPath = getenv( "IECOREARNOLD_PROCEDURAL_PATH" ); AiNodeSetStr( node, "dso", dsoPath ? dsoPath : "ieProcedural.so" ); diff --git a/contrib/IECoreArnold/src/IECoreArnold/outputDriver/OutputDriver.cpp b/contrib/IECoreArnold/src/IECoreArnold/outputDriver/OutputDriver.cpp index 99314d7ade..e60cfa5265 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/outputDriver/OutputDriver.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/outputDriver/OutputDriver.cpp @@ -115,7 +115,6 @@ bool driverSupportsPixelType( const AtNode *node, AtByte pixelType ) case AI_TYPE_RGBA : case AI_TYPE_FLOAT : case AI_TYPE_VECTOR : - case AI_TYPE_POINT : return true; default: return false; @@ -157,7 +156,6 @@ void driverOpen( AtNode *node, struct AtOutputIterator *iterator, AtBBox2 displa { case AI_TYPE_RGB : case AI_TYPE_VECTOR : - case AI_TYPE_POINT : channelNames.push_back( namePrefix + "R" ); channelNames.push_back( namePrefix + "G" ); channelNames.push_back( namePrefix + "B" ); @@ -283,7 +281,6 @@ void driverWriteBucket( AtNode *node, struct AtOutputIterator *iterator, struct { case AI_TYPE_RGB : case AI_TYPE_VECTOR : - case AI_TYPE_POINT : numChannels = 3; break; case AI_TYPE_RGBA : diff --git a/contrib/IECoreArnold/test/IECoreArnold/CameraAlgoTest.py b/contrib/IECoreArnold/test/IECoreArnold/CameraAlgoTest.py index 758f308abe..381f89ccbb 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/CameraAlgoTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/CameraAlgoTest.py @@ -59,8 +59,8 @@ def testConvertPerspective( self ) : self.assertTrue( arnold.AiNodeEntryGetName( arnold.AiNodeGetNodeEntry( n ) ), "persp_camera" ) self.assertEqual( arnold.AiNodeGetFlt( n, "fov" ), 45.0 ) - self.assertEqual( arnold.AiNodeGetPnt2( n, "screen_window_min" ), arnold.AtPoint2( -1, -0.5 ) ) - self.assertEqual( arnold.AiNodeGetPnt2( n, "screen_window_max" ), arnold.AtPoint2( 1, 0.5 ) ) + self.assertEqual( arnold.AiNodeGetVec2( n, "screen_window_min" ), arnold.AtVector2( -1, -0.5 ) ) + self.assertEqual( arnold.AiNodeGetVec2( n, "screen_window_max" ), arnold.AtVector2( 1, 0.5 ) ) def testConvertCustomProjection( self ) : diff --git a/contrib/IECoreArnold/test/IECoreArnold/CurvesTest.py b/contrib/IECoreArnold/test/IECoreArnold/CurvesTest.py index 2ec3ac4b41..6ed03ee018 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/CurvesTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/CurvesTest.py @@ -66,9 +66,9 @@ def testMotion( self ) : self.assertEqual( a.contents.nkeys, 2 ) for i in range( 0, 4 ) : - self.assertEqual( arnold.AiArrayGetPnt( a, i ), arnold.AtPoint( 1 ) ) + self.assertEqual( arnold.AiArrayGetVec( a, i ), arnold.AtVector( 1 ) ) for i in range( 4, 8 ) : - self.assertEqual( arnold.AiArrayGetPnt( a, i ), arnold.AtPoint( 2 ) ) + self.assertEqual( arnold.AiArrayGetVec( a, i ), arnold.AtVector( 2 ) ) a = arnold.AiNodeGetArray( n, "deform_time_samples" ) self.assertEqual( a.contents.nelements, 2 ) diff --git a/contrib/IECoreArnold/test/IECoreArnold/MeshTest.py b/contrib/IECoreArnold/test/IECoreArnold/MeshTest.py index c5b0bb6238..9f95744773 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/MeshTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/MeshTest.py @@ -61,8 +61,8 @@ def testUVs( self ) : self.assertEqual( uvIndices.contents.nelements, 4 ) for i in range( 0, 4 ) : - p = arnold.AiArrayGetPnt2( uvs, i ) - self.assertEqual( arnold.AiArrayGetPnt2( uvs, i ), arnold.AtPoint2( s[i], 1 - t[i] ) ) + p = arnold.AiArrayGetVec2( uvs, i ) + self.assertEqual( arnold.AiArrayGetVec2( uvs, i ), arnold.AtVector2( s[i], 1 - t[i] ) ) self.assertEqual( arnold.AiArrayGetInt( uvIndices, i ), i ) def testAdditionalUVs( self ) : @@ -84,8 +84,8 @@ def testAdditionalUVs( self ) : self.assertEqual( uvIndices.contents.nelements, 4 ) for i in range( 0, 4 ) : - p = arnold.AiArrayGetPnt2( uvs, i ) - self.assertEqual( arnold.AiArrayGetPnt2( uvs, i ), arnold.AtPoint2( s[i], 1 - t[i] ) ) + p = arnold.AiArrayGetVec2( uvs, i ) + self.assertEqual( arnold.AiArrayGetVec2( uvs, i ), arnold.AtVector2( s[i], 1 - t[i] ) ) self.assertEqual( arnold.AiArrayGetInt( uvIndices, i ), i ) @@ -186,15 +186,15 @@ def testMotion( self ) : self.assertEqual( nList.contents.nkeys, 2 ) for i in range( 0, 4 ) : - p = arnold.AiArrayGetPnt( vList, i ) + p = arnold.AiArrayGetVec( vList, i ) self.assertEqual( IECore.V3f( p.x, p.y, p.z ), m1["P"].data[i] ) - n = arnold.AiArrayGetPnt( nList, i ) + n = arnold.AiArrayGetVec( nList, i ) self.assertEqual( IECore.V3f( n.x, n.y, n.z ), m1["N"].data[i] ) for i in range( 4, 8 ) : - p = arnold.AiArrayGetPnt( vList, i ) + p = arnold.AiArrayGetVec( vList, i ) self.assertEqual( IECore.V3f( p.x, p.y, p.z ), m2["P"].data[i-4] ) - n = arnold.AiArrayGetPnt( nList, i ) + n = arnold.AiArrayGetVec( nList, i ) self.assertEqual( IECore.V3f( n.x, n.y, n.z ), m2["N"].data[i-4] ) a = arnold.AiNodeGetArray( node, "deform_time_samples" ) @@ -238,7 +238,7 @@ def testPointTypePrimitiveVariables( self ) : with IECoreArnold.UniverseBlock( writable = True ) : node = IECoreArnold.NodeAlgo.convert( m ) p = arnold.AiNodeGetArray( node, "points" ) - self.assertEqual( p.contents.type, arnold.AI_TYPE_POINT ) + self.assertEqual( p.contents.type, arnold.AI_TYPE_VECTOR ) v = arnold.AiNodeGetArray( node, "vectors" ) self.assertEqual( v.contents.type, arnold.AI_TYPE_VECTOR ) diff --git a/contrib/IECoreArnold/test/IECoreArnold/PointsTest.py b/contrib/IECoreArnold/test/IECoreArnold/PointsTest.py index 5e29b18389..ce1e5180f8 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/PointsTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/PointsTest.py @@ -201,10 +201,10 @@ def testMotion( self ) : self.assertEqual( a.contents.nkeys, 2 ) for i in range( 0, 10 ) : - self.assertEqual( arnold.AiArrayGetPnt( a, i ), arnold.AtPoint( 10 ) ) + self.assertEqual( arnold.AiArrayGetVec( a, i ), arnold.AtVector( 10 ) ) self.assertEqual( arnold.AiArrayGetFlt( r, i ), 0.5 ) for i in range( 11, 20 ) : - self.assertEqual( arnold.AiArrayGetPnt( a, i ), arnold.AtPoint( 20 ) ) + self.assertEqual( arnold.AiArrayGetVec( a, i ), arnold.AtVector( 20 ) ) self.assertEqual( arnold.AiArrayGetFlt( r, i ), 1 ) a = arnold.AiNodeGetArray( n, "deform_time_samples" ) diff --git a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py index 82740443a0..c63e411244 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py @@ -855,8 +855,8 @@ def testProcedural( self ) : volume = self.__allNodes( type = arnold.AI_NODE_SHAPE )[-1] self.assertEqual( arnold.AiNodeEntryGetName( arnold.AiNodeGetNodeEntry( volume ) ), "volume" ) - self.assertEqual( arnold.AiNodeGetPnt( volume, "min" ), arnold.AtPoint( -1, -2, -3 ) ) - self.assertEqual( arnold.AiNodeGetPnt( volume, "max" ), arnold.AtPoint( 4, 5, 6 ) ) + self.assertEqual( arnold.AiNodeGetVec( volume, "min" ), arnold.AtVector( -1, -2, -3 ) ) + self.assertEqual( arnold.AiNodeGetVec( volume, "max" ), arnold.AtVector( 4, 5, 6 ) ) self.assertEqual( arnold.AiNodeGetStr( volume, "dso" ), "someVolumeThing.so" ) self.assertEqual( arnold.AiNodeGetFlt( volume, "testFloat" ), 0.5 ) From f8d2571be2ece1b034d13b14add47f1a42909b4e Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Thu, 14 Sep 2017 16:13:42 -0700 Subject: [PATCH 12/26] IECoreArnold : Arnold 5 replaces AtByte with uint8_t --- .../IECoreArnold/src/IECoreArnold/RendererImplementation.cpp | 2 +- .../IECoreArnold/src/IECoreArnold/outputDriver/OutputDriver.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp index a016f522e4..3e85b07e9c 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp @@ -899,7 +899,7 @@ void IECoreArnold::RendererImplementation::applyTransformToNode( AtNode *node ) void IECoreArnold::RendererImplementation::applyVisibilityToNode( AtNode *node ) { - AtByte visibility = 0; + uint8_t visibility = 0; const BoolData *visData = m_attributeStack.top().attributes->member( "ai:visibility:camera" ); if( visData->readable() ) { diff --git a/contrib/IECoreArnold/src/IECoreArnold/outputDriver/OutputDriver.cpp b/contrib/IECoreArnold/src/IECoreArnold/outputDriver/OutputDriver.cpp index e60cfa5265..71986a069f 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/outputDriver/OutputDriver.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/outputDriver/OutputDriver.cpp @@ -107,7 +107,7 @@ void driverUpdate( AtNode *node, AtParamValue *parameters ) { } -bool driverSupportsPixelType( const AtNode *node, AtByte pixelType ) +bool driverSupportsPixelType( const AtNode *node, uint8_t pixelType ) { switch( pixelType ) { From c146478f8243dcf41dbfb3930ecdcd3850cc1ebe Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Fri, 15 Sep 2017 15:12:46 -0700 Subject: [PATCH 13/26] IECoreArnold : Arnold 5 replaces AtColor with AtRGB --- contrib/IECoreArnold/test/IECoreArnold/SphereAlgoTest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/IECoreArnold/test/IECoreArnold/SphereAlgoTest.py b/contrib/IECoreArnold/test/IECoreArnold/SphereAlgoTest.py index 661a107033..2c9bb6771f 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/SphereAlgoTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/SphereAlgoTest.py @@ -82,7 +82,7 @@ def testPrimitiveVariables( self ) : n = IECoreArnold.NodeAlgo.convert( s ) self.assertEqual( arnold.AiNodeGetVec( n, "v" ), arnold.AtVector( 1, 2, 3 ) ) - self.assertEqual( arnold.AiNodeGetRGB( n, "c" ), arnold.AtColor( 1, 2, 3 ) ) + self.assertEqual( arnold.AiNodeGetRGB( n, "c" ), arnold.AtRGB( 1, 2, 3 ) ) self.assertEqual( arnold.AiNodeGetStr( n, "s" ), "test" ) self.assertEqual( arnold.AiNodeGetInt( n, "i" ), 11 ) self.assertEqual( arnold.AiNodeGetBool( n, "b" ), True ) From 2f060537d0e3f310e19278f15875b75efb88fd60 Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Thu, 14 Sep 2017 16:38:32 -0700 Subject: [PATCH 14/26] IECoreArnold : In Arnold 5, member variables of AtArray are not declared in the header, and instead use accessor functions --- .../src/IECoreArnold/ParameterAlgo.cpp | 12 ++++---- .../src/IECoreArnold/ShapeAlgo.cpp | 2 +- .../IECoreArnold/procedural/Procedural.cpp | 2 +- .../test/IECoreArnold/CurvesTest.py | 12 ++++---- .../IECoreArnold/InstancingConverterTest.py | 6 ++-- .../test/IECoreArnold/MeshTest.py | 28 +++++++++---------- .../test/IECoreArnold/ParameterAlgoTest.py | 2 +- .../test/IECoreArnold/PointsTest.py | 12 ++++---- 8 files changed, 38 insertions(+), 38 deletions(-) diff --git a/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp b/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp index 084135d8ac..7e86b943d4 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp @@ -75,7 +75,7 @@ void setParameterInternal( AtNode *node, const char *name, int parameterType, bo msg( Msg::Warning, "setParameter", boost::format( "Unable to create array from data of type \"%s\" for parameter \"%s\"" ) % value->typeName() % name ); return; } - if( a->type != parameterType ) + if( AiArrayGetType( a ) != parameterType ) { msg( Msg::Warning, "setParameter", boost::format( "Unable to create array of type %s from data of type \"%s\" for parameter \"%s\"" ) % AiParamGetTypeName( parameterType ) % value->typeName() % name ); return; @@ -199,8 +199,8 @@ IECore::DataPtr arrayToDataInternal( AtArray *array, F f ) typename DataType::Ptr data = new DataType; VectorType &v = data->writable(); - v.reserve( array->nelements ); - for( size_t i = 0; i < array->nelements; ++i ) + v.reserve( AiArrayGetNumElements(array) ); + for( size_t i = 0; i < AiArrayGetNumElements(array); ++i ) { v.push_back( f( array, i, __AI_FILE__, __AI_LINE__ ) ); } @@ -212,7 +212,7 @@ IECore::DataPtr arrayToDataInternal( AtArray *array, F f ) /// consider exposing it in the public API. IECore::DataPtr arrayToData( AtArray *array ) { - if( array->nkeys > 1 ) + if( AiArrayGetNumKeys( array ) > 1 ) { /// \todo Decide how to deal with more /// than one key - is it more useful to return multiple Data @@ -220,7 +220,7 @@ IECore::DataPtr arrayToData( AtArray *array ) return NULL; } - switch( array->type ) + switch( AiArrayGetType( array ) ) { case AI_TYPE_BOOLEAN : return arrayToDataInternal( array, AiArrayGetBoolFunc ); @@ -286,7 +286,7 @@ void setParameter( AtNode *node, const AtParamEntry *parameter, const IECore::Da int type = AiParamGetType( parameter ); if( type == AI_TYPE_ARRAY ) { - type = AiParamGetDefault( parameter )->ARRAY->type; + type = AiArrayGetType( AiParamGetDefault( parameter )->ARRAY() ); isArray = true; } diff --git a/contrib/IECoreArnold/src/IECoreArnold/ShapeAlgo.cpp b/contrib/IECoreArnold/src/IECoreArnold/ShapeAlgo.cpp index 11632bade1..8408d03067 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/ShapeAlgo.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/ShapeAlgo.cpp @@ -282,7 +282,7 @@ void convertPrimitiveVariable( const IECore::Primitive *primitive, const Primiti AiNodeSetArray( shape, (name + string("idxs")).c_str(), - identityIndices( array->nelements ) + identityIndices( AiArrayGetNumElements( array ) ) ); } } diff --git a/contrib/IECoreArnold/src/IECoreArnold/procedural/Procedural.cpp b/contrib/IECoreArnold/src/IECoreArnold/procedural/Procedural.cpp index dbb4ec2a95..9af662ce3a 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/procedural/Procedural.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/procedural/Procedural.cpp @@ -120,7 +120,7 @@ static int procInit( AtNode *node, void **userPtr ) if( parameterValues ) { boost::python::list toParse; - for( unsigned i=0; inelements; i++ ) + for( unsigned i=0; i Date: Thu, 14 Sep 2017 17:37:58 -0700 Subject: [PATCH 15/26] IECoreArnold::OutputDriver : Many API changes in Arnold 5 --- .../outputDriver/OutputDriver.cpp | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/contrib/IECoreArnold/src/IECoreArnold/outputDriver/OutputDriver.cpp b/contrib/IECoreArnold/src/IECoreArnold/outputDriver/OutputDriver.cpp index 71986a069f..3b212cc235 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/outputDriver/OutputDriver.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/outputDriver/OutputDriver.cpp @@ -54,7 +54,7 @@ namespace // Stores a Cortex DisplayDriver and the parameters // used to create it. This forms the private data -// accessed via AiDriverGetLocalData. +// accessed via AiNodeGetLocalData. struct LocalData { @@ -89,21 +89,22 @@ struct LocalData }; -void driverParameters( AtList *params, AtMetaDataStore *metaData ) +void driverParameters( AtList *params, AtNodeEntry *nentry ) { - AiParameterSTR( "driverType", "" ); + AiParameterStr( "driverType", "" ); // we need to specify this metadata to keep MtoA happy. - AiMetaDataSetStr( metaData, 0, "maya.attr_prefix", "" ); - AiMetaDataSetStr( metaData, 0, "maya.translator", "ie" ); + AiMetaDataSetStr( nentry, 0, "maya.attr_prefix", "" ); + AiMetaDataSetStr( nentry, 0, "maya.translator", "ie" ); } -void driverInitialize( AtNode *node, AtParamValue *parameters ) +void driverInitialize( AtNode *node ) { - AiDriverInitialize( node, true, new LocalData ); + AiDriverInitialize( node, true ); + AiNodeSetLocalData( node, new LocalData ); } -void driverUpdate( AtNode *node, AtParamValue *parameters ) +void driverUpdate( AtNode *node ) { } @@ -128,7 +129,7 @@ const char **driverExtension() void driverOpen( AtNode *node, struct AtOutputIterator *iterator, AtBBox2 displayWindow, AtBBox2 dataWindow, int bucketSize ) { - LocalData *localData = (LocalData *)AiDriverGetLocalData( node ); + LocalData *localData = (LocalData *)AiNodeGetLocalData( node ); localData->numOutputs = 0; std::vector channelNames; @@ -195,7 +196,7 @@ void driverOpen( AtNode *node, struct AtOutputIterator *iterator, AtBBox2 displa 1.0f / AiNodeGetFlt( AiUniverseGetOptions(), "aspect_ratio" ) ); - const std::string driverType = AiNodeGetStr( node, "driverType" ); + const std::string driverType = AiNodeGetStr( node, "driverType" ).c_str(); // We reuse the previous driver if we can - this allows us to use // the same driver for every stage of a progressive render. @@ -233,22 +234,22 @@ void driverOpen( AtNode *node, struct AtOutputIterator *iterator, AtBBox2 displa } } -bool driverNeedsBucket( AtNode *node, int x, int y, int sx, int sy, int tId ) +bool driverNeedsBucket( AtNode *node, int x, int y, int sx, int sy, uint16_t tId ) { return true; } -void driverPrepareBucket( AtNode *node, int x, int y, int sx, int sy, int tId ) +void driverPrepareBucket( AtNode *node, int x, int y, int sx, int sy, uint16_t tId ) { } -void driverProcessBucket( AtNode *node, struct AtOutputIterator *iterator, struct AtAOVSampleIterator *sample_iterator, int x, int y, int sx, int sy, int tId ) +void driverProcessBucket( AtNode *node, struct AtOutputIterator *iterator, struct AtAOVSampleIterator *sample_iterator, int x, int y, int sx, int sy, uint16_t tId ) { } void driverWriteBucket( AtNode *node, struct AtOutputIterator *iterator, struct AtAOVSampleIterator *sampleIterator, int x, int y, int sx, int sy ) { - LocalData *localData = (LocalData *)AiDriverGetLocalData( node ); + LocalData *localData = (LocalData *)AiNodeGetLocalData( node ); if( !localData->displayDriver ) { return; @@ -330,7 +331,7 @@ void driverWriteBucket( AtNode *node, struct AtOutputIterator *iterator, struct void driverClose( AtNode *node, struct AtOutputIterator *iterator ) { - LocalData *localData = (LocalData *)AiDriverGetLocalData( node ); + LocalData *localData = (LocalData *)AiNodeGetLocalData( node ); // We only close the display immediately if it doesn't accept // repeated data (progressive renders). This is so we can reuse it in // driverOpen if it appears that a progressive render is taking place. @@ -342,11 +343,10 @@ void driverClose( AtNode *node, struct AtOutputIterator *iterator ) void driverFinish( AtNode *node ) { - LocalData *localData = (LocalData *)AiDriverGetLocalData( node ); + LocalData *localData = (LocalData *)AiNodeGetLocalData( node ); // Perform any pending close we may have deferred in driverClose(). localData->imageClose(); delete localData; - AiDriverDestroy( node ); } } // namespace @@ -356,6 +356,8 @@ AI_EXPORT_LIB bool NodeLoader( int i, AtNodeLib *node ) if( i==0 ) { static AtCommonMethods commonMethods = { + NULL, // Whole plugin init + NULL, // Whole plugin cleanup driverParameters, driverInitialize, driverUpdate, From 5765d459039787a4661215299449a9b857ebd561 Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Thu, 14 Sep 2017 17:50:26 -0700 Subject: [PATCH 16/26] IECoreAnold::Parameter Algo : Use AtString --- .../include/IECoreArnold/ParameterAlgo.h | 2 ++ .../src/IECoreArnold/ParameterAlgo.cpp | 26 +++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/contrib/IECoreArnold/include/IECoreArnold/ParameterAlgo.h b/contrib/IECoreArnold/include/IECoreArnold/ParameterAlgo.h index bcf9017f7a..778d7aed3b 100644 --- a/contrib/IECoreArnold/include/IECoreArnold/ParameterAlgo.h +++ b/contrib/IECoreArnold/include/IECoreArnold/ParameterAlgo.h @@ -49,11 +49,13 @@ namespace ParameterAlgo { IECOREARNOLD_API void setParameter( AtNode *node, const AtParamEntry *parameter, const IECore::Data *value ); +IECOREARNOLD_API void setParameter( AtNode *node, AtString name, const IECore::Data *value ); IECOREARNOLD_API void setParameter( AtNode *node, const char *name, const IECore::Data *value ); IECOREARNOLD_API void setParameters( AtNode *node, const IECore::CompoundDataMap &values ); IECOREARNOLD_API IECore::DataPtr getParameter( AtNode *node, const AtParamEntry *parameter ); IECOREARNOLD_API IECore::DataPtr getParameter( AtNode *node, const AtUserParamEntry *parameter ); +IECOREARNOLD_API IECore::DataPtr getParameter( AtNode *node, AtString name ); IECOREARNOLD_API IECore::DataPtr getParameter( AtNode *node, const char *name ); IECOREARNOLD_API void getParameters( AtNode *node, IECore::CompoundDataMap &values ); diff --git a/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp b/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp index 7e86b943d4..5181140fdd 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp @@ -65,7 +65,7 @@ inline const T *dataCast( const char *name, const IECore::Data *data ) return NULL; } -void setParameterInternal( AtNode *node, const char *name, int parameterType, bool array, const IECore::Data *value ) +void setParameterInternal( AtNode *node, AtString name, int parameterType, bool array, const IECore::Data *value ) { if( array ) { @@ -208,6 +208,12 @@ IECore::DataPtr arrayToDataInternal( AtArray *array, F f ) return data; } +const char* getStrWrapperFunc( const AtArray* a, uint32_t i, const char* file, int line ) +{ + return AiArrayGetStrFunc( a, i, file, line ).c_str(); +} + + /// \todo Flesh this out to support more types and then /// consider exposing it in the public API. IECore::DataPtr arrayToData( AtArray *array ) @@ -229,7 +235,7 @@ IECore::DataPtr arrayToData( AtArray *array ) case AI_TYPE_FLOAT : return arrayToDataInternal( array, AiArrayGetFltFunc ); case AI_TYPE_STRING : - return arrayToDataInternal( array, AiArrayGetStrFunc ); + return arrayToDataInternal( array, getStrWrapperFunc ); default : return NULL; } @@ -246,7 +252,7 @@ IECore::DataPtr getParameterInternal( AtNode *node, const char *name, int parame case AI_TYPE_FLOAT : return new FloatData( AiNodeGetFlt( node, name ) ); case AI_TYPE_STRING : - return new StringData( AiNodeGetStr( node, name ) ); + return new StringData( AiNodeGetStr( node, name ).c_str() ); case AI_TYPE_RGB : { AtRGB rgb = AiNodeGetRGB( node, name ); @@ -293,7 +299,7 @@ void setParameter( AtNode *node, const AtParamEntry *parameter, const IECore::Da setParameterInternal( node, AiParamGetName( parameter ), type, isArray, value ); } -void setParameter( AtNode *node, const char *name, const IECore::Data *value ) +void setParameter( AtNode *node, AtString name, const IECore::Data *value ) { const AtParamEntry *parameter = AiNodeEntryLookUpParameter( AiNodeGetNodeEntry( node ), name ); if( parameter ) @@ -329,6 +335,11 @@ void setParameter( AtNode *node, const char *name, const IECore::Data *value ) } } +void setParameter( AtNode *node, const char* name, const IECore::Data *value ) +{ + setParameter( node, AtString( name ), value ); +} + void setParameters( AtNode *node, const IECore::CompoundDataMap &values ) { for( CompoundDataMap::const_iterator it=values.begin(); it!=values.end(); it++ ) @@ -347,7 +358,7 @@ IECore::DataPtr getParameter( AtNode *node, const AtUserParamEntry *parameter ) return getParameterInternal( node, AiUserParamGetName( parameter ), AiUserParamGetType( parameter ) ); } -IECore::DataPtr getParameter( AtNode *node, const char *name ) +IECore::DataPtr getParameter( AtNode *node, AtString name ) { const AtParamEntry *parameter = AiNodeEntryLookUpParameter( AiNodeGetNodeEntry( node ), name ); if( parameter ) @@ -366,6 +377,11 @@ IECore::DataPtr getParameter( AtNode *node, const char *name ) return NULL; } +IECore::DataPtr getParameter( AtNode *node, const char *name ) +{ + return getParameter( node, AtString( name ) ); +} + void getParameters( AtNode *node, IECore::CompoundDataMap &values ) { /// \todo Non-user parameters From 3ab0ea5659bf058a12e42fc4d14f9386a5b81e5d Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Fri, 15 Sep 2017 10:20:35 -0700 Subject: [PATCH 17/26] IECoreArnold : Update M44f to AtMatrix casts for Arnold 5 --- contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp | 4 ++-- .../src/IECoreArnold/RendererImplementation.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp b/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp index 5181140fdd..73a7e4c30b 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp @@ -166,7 +166,7 @@ void setParameterInternal( AtNode *node, AtString name, int parameterType, bool if( const M44dData *data = runTimeCast( value ) ) { const Imath::M44f v( data->readable() ); - AiNodeSetMatrix( node, name, const_cast( v.x ) ); + AiNodeSetMatrix( node, name, *reinterpret_cast( const_cast( &v.x ) ) ); } else if( const M44fData *data = dataCast( name, value ) ) { @@ -174,7 +174,7 @@ void setParameterInternal( AtNode *node, AtString name, int parameterType, bool // Can't see any reason why AiNodeSetMatrix couldn't have been declared const, // this const_cast seems safe - AiNodeSetMatrix( node, name, const_cast( v.x ) ); + AiNodeSetMatrix( node, name, *reinterpret_cast( const_cast( &v.x ) ) ); } break; default : diff --git a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp index 3e85b07e9c..54ae8204bd 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp @@ -874,7 +874,8 @@ void IECoreArnold::RendererImplementation::applyTransformToNode( AtNode *node ) const size_t numSamples = m_transformStack.numSamples(); if( numSamples == 1 ) { - AiNodeSetMatrix( node, "matrix", m_transformStack.get().x ); + M44f m = m_transformStack.get(); + AiNodeSetMatrix( node, "matrix", *reinterpret_cast( (float(*)[4][4])&m.x ) ); } else { @@ -883,7 +884,8 @@ void IECoreArnold::RendererImplementation::applyTransformToNode( AtNode *node ) for( size_t i = 0; i < numSamples; ++i ) { AiArraySetFlt( times, i, m_transformStack.sampleTime( i ) ); - AiArraySetMtx( matrices, i, m_transformStack.sample( i ).x ); + M44f m = m_transformStack.sample( i ); + AiArraySetMtx( matrices, i, *reinterpret_cast( (float(*)[4][4])&m.x ) ); } AiNodeSetArray( node, "matrix", matrices ); if( AiNodeEntryLookUpParameter( AiNodeGetNodeEntry( node ), "transform_time_samples" ) ) From 4acc625efcc6bf2e7451ff1e771058b988080175 Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Fri, 15 Sep 2017 10:43:43 -0700 Subject: [PATCH 18/26] IECoreArnold::RendererImplementation : Updated visibility attributes for Arnold 5 --- .../include/IECoreArnold/Renderer.h | 10 +++-- .../IECoreArnold/RendererImplementation.cpp | 38 +++++++++++++------ .../test/IECoreArnold/RendererTest.py | 10 +++-- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/contrib/IECoreArnold/include/IECoreArnold/Renderer.h b/contrib/IECoreArnold/include/IECoreArnold/Renderer.h index 2d6e9aeee1..186eac2da5 100644 --- a/contrib/IECoreArnold/include/IECoreArnold/Renderer.h +++ b/contrib/IECoreArnold/include/IECoreArnold/Renderer.h @@ -95,10 +95,12 @@ class IECOREARNOLD_API Renderer : public IECore::Renderer /// /// \li "ai:visibility:camera" BoolData( true ) /// \li "ai:visibility:shadow" BoolData( true ) - /// \li "ai:visibility:reflected" BoolData( true ) - /// \li "ai:visibility:refracted" BoolData( true ) - /// \li "ai:visibility:diffuse" BoolData( true ) - /// \li "ai:visibility:glossy" BoolData( true ) + /// \li "ai:visibility:diffuseReflect" BoolData( true ) + /// \li "ai:visibility:specularReflect" BoolData( true ) + /// \li "ai:visibility:diffuseTransmit" BoolData( true ) + /// \li "ai:visibility:specularTransmit" BoolData( true ) + /// \li "ai:visibility:volume" BoolData( true ) + /// \li "ai:visibility:subsurface" BoolData( true ) /// /// \li "ai:*:*" Data
/// Mapped to shape node parameters, such that "ai:nodeType:parameterName" diff --git a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp index 54ae8204bd..289f9c03ef 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp @@ -81,10 +81,12 @@ RendererImplementation::AttributeState::AttributeState() attributes = new CompoundData; attributes->writable()["ai:visibility:camera"] = new BoolData( true ); attributes->writable()["ai:visibility:shadow"] = new BoolData( true ); - attributes->writable()["ai:visibility:reflected"] = new BoolData( true ); - attributes->writable()["ai:visibility:refracted"] = new BoolData( true ); - attributes->writable()["ai:visibility:diffuse"] = new BoolData( true ); - attributes->writable()["ai:visibility:glossy"] = new BoolData( true ); + attributes->writable()["ai:visibility:diffuseReflect"] = new BoolData( true ); + attributes->writable()["ai:visibility:specularReflect"] = new BoolData( true ); + attributes->writable()["ai:visibility:diffuseTransmit"] = new BoolData( true ); + attributes->writable()["ai:visibility:specularTransmit"] = new BoolData( true ); + attributes->writable()["ai:visibility:volume"] = new BoolData( true ); + attributes->writable()["ai:visibility:subsurface"] = new BoolData( true ); } RendererImplementation::AttributeState::AttributeState( const AttributeState &other ) @@ -914,28 +916,40 @@ void IECoreArnold::RendererImplementation::applyVisibilityToNode( AtNode *node ) visibility |= AI_RAY_SHADOW; } - visData = m_attributeStack.top().attributes->member( "ai:visibility:reflected" ); + visData = m_attributeStack.top().attributes->member( "ai:visibility:diffuseReflect" ); if( visData->readable() ) { - visibility |= AI_RAY_REFLECTED; + visibility |= AI_RAY_DIFFUSE_REFLECT; } - visData = m_attributeStack.top().attributes->member( "ai:visibility:refracted" ); + visData = m_attributeStack.top().attributes->member( "ai:visibility:specularReflect" ); if( visData->readable() ) { - visibility |= AI_RAY_REFRACTED; + visibility |= AI_RAY_SPECULAR_REFLECT; } - visData = m_attributeStack.top().attributes->member( "ai:visibility:diffuse" ); + visData = m_attributeStack.top().attributes->member( "ai:visibility:diffuseTransmit" ); if( visData->readable() ) { - visibility |= AI_RAY_DIFFUSE; + visibility |= AI_RAY_DIFFUSE_TRANSMIT; } - visData = m_attributeStack.top().attributes->member( "ai:visibility:glossy" ); + visData = m_attributeStack.top().attributes->member( "ai:visibility:specularTransmit" ); if( visData->readable() ) { - visibility |= AI_RAY_GLOSSY; + visibility |= AI_RAY_SPECULAR_TRANSMIT; + } + + visData = m_attributeStack.top().attributes->member( "ai:visibility:volume" ); + if( visData->readable() ) + { + visibility |= AI_RAY_VOLUME; + } + + visData = m_attributeStack.top().attributes->member( "ai:visibility:subsurface" ); + if( visData->readable() ) + { + visibility |= AI_RAY_SUBSURFACE; } AiNodeSetByte( node, "visibility", visibility ); diff --git a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py index c63e411244..3ebfee1fb9 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py @@ -538,10 +538,12 @@ def testVisibilityAttributes( self ) : r = IECoreArnold.Renderer() self.assertEqual( r.getAttribute( "ai:visibility:camera" ), IECore.BoolData( True ) ) self.assertEqual( r.getAttribute( "ai:visibility:shadow" ), IECore.BoolData( True ) ) - self.assertEqual( r.getAttribute( "ai:visibility:reflected" ), IECore.BoolData( True ) ) - self.assertEqual( r.getAttribute( "ai:visibility:refracted" ), IECore.BoolData( True ) ) - self.assertEqual( r.getAttribute( "ai:visibility:diffuse" ), IECore.BoolData( True ) ) - self.assertEqual( r.getAttribute( "ai:visibility:glossy" ), IECore.BoolData( True ) ) + self.assertEqual( r.getAttribute( "ai:visibility:diffuseReflect" ), IECore.BoolData( True ) ) + self.assertEqual( r.getAttribute( "ai:visibility:specularReflect" ), IECore.BoolData( True ) ) + self.assertEqual( r.getAttribute( "ai:visibility:diffuseTransmit" ), IECore.BoolData( True ) ) + self.assertEqual( r.getAttribute( "ai:visibility:specularTransmit" ), IECore.BoolData( True ) ) + self.assertEqual( r.getAttribute( "ai:visibility:volume" ), IECore.BoolData( True ) ) + self.assertEqual( r.getAttribute( "ai:visibility:subsurface" ), IECore.BoolData( True ) ) r.setAttribute( "ai:visibility:shadow", IECore.BoolData( False ) ) self.assertEqual( r.getAttribute( "ai:visibility:shadow" ), IECore.BoolData( False ) ) From 45a51566f7baf4dbb1bf2af9bd171cfe259f9d8b Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Fri, 15 Sep 2017 13:39:53 -0700 Subject: [PATCH 19/26] IECoreArnold : Arnold 5 doesn't support non-uniform sample times --- .../include/IECoreArnold/CurvesAlgo.h | 2 +- .../IECoreArnold/InstancingConverter.h | 4 +- .../include/IECoreArnold/MeshAlgo.h | 2 +- .../include/IECoreArnold/NodeAlgo.h | 11 +++- .../include/IECoreArnold/PointsAlgo.h | 2 +- .../include/IECoreArnold/SphereAlgo.h | 2 +- .../private/RendererImplementation.h | 4 +- .../src/IECoreArnold/CurvesAlgo.cpp | 5 +- .../src/IECoreArnold/InstancingConverter.cpp | 8 +-- .../src/IECoreArnold/MeshAlgo.cpp | 5 +- .../src/IECoreArnold/NodeAlgo.cpp | 40 ++++++++++++- .../src/IECoreArnold/PointsAlgo.cpp | 6 +- .../IECoreArnold/RendererImplementation.cpp | 59 ++++++++++--------- .../src/IECoreArnold/SphereAlgo.cpp | 5 +- .../bindings/InstancingConverterBinding.cpp | 12 ++-- .../IECoreArnold/bindings/NodeAlgoBinding.cpp | 6 +- .../test/IECoreArnold/CurvesTest.py | 11 ++-- .../IECoreArnold/InstancingConverterTest.py | 8 +-- .../test/IECoreArnold/MeshTest.py | 9 +-- .../test/IECoreArnold/PointsTest.py | 9 +-- .../test/IECoreArnold/RendererTest.py | 37 ++++++++++++ .../test/IECoreArnold/SphereAlgoTest.py | 7 +-- 22 files changed, 164 insertions(+), 90 deletions(-) diff --git a/contrib/IECoreArnold/include/IECoreArnold/CurvesAlgo.h b/contrib/IECoreArnold/include/IECoreArnold/CurvesAlgo.h index d7051ba4c3..ec0df2cfbe 100644 --- a/contrib/IECoreArnold/include/IECoreArnold/CurvesAlgo.h +++ b/contrib/IECoreArnold/include/IECoreArnold/CurvesAlgo.h @@ -46,7 +46,7 @@ namespace CurvesAlgo { AtNode *convert( const IECore::CurvesPrimitive *curves ); -AtNode *convert( const std::vector &samples, const std::vector &sampleTimes ); +AtNode *convert( const std::vector &samples, float motionStart, float motionEnd ); } // namespace CurvesAlgo diff --git a/contrib/IECoreArnold/include/IECoreArnold/InstancingConverter.h b/contrib/IECoreArnold/include/IECoreArnold/InstancingConverter.h index 1451f46a6b..25687af9ea 100644 --- a/contrib/IECoreArnold/include/IECoreArnold/InstancingConverter.h +++ b/contrib/IECoreArnold/include/IECoreArnold/InstancingConverter.h @@ -72,8 +72,8 @@ class IECOREARNOLD_API InstancingConverter : public IECore::RefCounted AtNode *convert( const IECore::Primitive *primitive, const IECore::MurmurHash &additionalHash ); /// Motion blurred versions of the above conversion functions. - AtNode *convert( const std::vector &samples, const std::vector &sampleTimes ); - AtNode *convert( const std::vector &samples, const std::vector &sampleTimes, const IECore::MurmurHash &additionalHash ); + AtNode *convert( const std::vector &samples, float motionStart, float motionEnd ); + AtNode *convert( const std::vector &samples, float motionStart, float motionEnd, const IECore::MurmurHash &additionalHash ); private : diff --git a/contrib/IECoreArnold/include/IECoreArnold/MeshAlgo.h b/contrib/IECoreArnold/include/IECoreArnold/MeshAlgo.h index 16d840ab25..4b23ee12bf 100644 --- a/contrib/IECoreArnold/include/IECoreArnold/MeshAlgo.h +++ b/contrib/IECoreArnold/include/IECoreArnold/MeshAlgo.h @@ -46,7 +46,7 @@ namespace MeshAlgo { AtNode *convert( const IECore::MeshPrimitive *mesh ); -AtNode *convert( const std::vector &samples, const std::vector &sampleTimes ); +AtNode *convert( const std::vector &samples, float motionStart, float motionEnd ); } // namespace MeshAlgo diff --git a/contrib/IECoreArnold/include/IECoreArnold/NodeAlgo.h b/contrib/IECoreArnold/include/IECoreArnold/NodeAlgo.h index 232fb5fe85..634fe9f442 100644 --- a/contrib/IECoreArnold/include/IECoreArnold/NodeAlgo.h +++ b/contrib/IECoreArnold/include/IECoreArnold/NodeAlgo.h @@ -53,14 +53,14 @@ AtNode *convert( const IECore::Object *object ); /// equivalent moving Arnold object. If no motion converter /// is available, then returns a standard conversion of the /// first sample. -AtNode *convert( const std::vector &samples, const std::vector &sampleTimes ); +AtNode *convert( const std::vector &samples, float motionStart, float motionEnd ); /// Signature of a function which can convert an IECore::Object /// into an Arnold object. typedef AtNode * (*Converter)( const IECore::Object * ); /// Signature of a function which can convert a series of IECore::Object /// samples into a moving Arnold object. -typedef AtNode * (*MotionConverter)( const std::vector &samples, const std::vector &sampleTimes ); +typedef AtNode * (*MotionConverter)( const std::vector &samples, float motionStart, float motionEnd ); /// Registers a converter for a specific type. /// Use the ConverterDescription utility class in preference to @@ -77,7 +77,7 @@ class ConverterDescription /// Type-specific conversion functions. typedef AtNode *(*Converter)( const T * ); - typedef AtNode *(*MotionConverter)( const std::vector &, const std::vector & ); + typedef AtNode *(*MotionConverter)( const std::vector &, float, float ); ConverterDescription( Converter converter, MotionConverter motionConverter = NULL ) { @@ -90,6 +90,11 @@ class ConverterDescription }; +/// Arnold does not support non-uniform sampling. It just takes a start and end time, and assume +/// the samples are distributed evenly between them. We need to throw an exception if given data +/// we can't render. +void ensureUniformTimeSamples( const std::vector × ); + } // namespace NodeAlgo } // namespace IECoreArnold diff --git a/contrib/IECoreArnold/include/IECoreArnold/PointsAlgo.h b/contrib/IECoreArnold/include/IECoreArnold/PointsAlgo.h index 76f1f46d3d..319011a1ad 100644 --- a/contrib/IECoreArnold/include/IECoreArnold/PointsAlgo.h +++ b/contrib/IECoreArnold/include/IECoreArnold/PointsAlgo.h @@ -46,7 +46,7 @@ namespace PointsAlgo { AtNode *convert( const IECore::PointsPrimitive *points ); -AtNode *convert( const std::vector &samples, const std::vector &sampleTimes ); +AtNode *convert( const std::vector &samples, float motionStart, float motionEnd ); } // namespace PointsAlgo diff --git a/contrib/IECoreArnold/include/IECoreArnold/SphereAlgo.h b/contrib/IECoreArnold/include/IECoreArnold/SphereAlgo.h index 3c1c1cf17f..66f5428a0a 100644 --- a/contrib/IECoreArnold/include/IECoreArnold/SphereAlgo.h +++ b/contrib/IECoreArnold/include/IECoreArnold/SphereAlgo.h @@ -46,7 +46,7 @@ namespace SphereAlgo { AtNode *convert( const IECore::SpherePrimitive *sphere ); -AtNode *convert( const std::vector &samples, const std::vector &sampleTimes ); +AtNode *convert( const std::vector &samples, float motionStart, float motionEnd ); } // namespace SphereAlgo diff --git a/contrib/IECoreArnold/include/IECoreArnold/private/RendererImplementation.h b/contrib/IECoreArnold/include/IECoreArnold/private/RendererImplementation.h index 154daa9913..b8751b3739 100644 --- a/contrib/IECoreArnold/include/IECoreArnold/private/RendererImplementation.h +++ b/contrib/IECoreArnold/include/IECoreArnold/private/RendererImplementation.h @@ -195,7 +195,9 @@ class RendererImplementation : public IECore::Renderer AttributeStack m_attributeStack; // motion blur stuff - std::vector m_motionTimes; + unsigned int m_motionBlockSize; + float m_motionStart; + float m_motionEnd; std::vector m_motionPrimitives; // list of nodes that have been output so far. we have diff --git a/contrib/IECoreArnold/src/IECoreArnold/CurvesAlgo.cpp b/contrib/IECoreArnold/src/IECoreArnold/CurvesAlgo.cpp index 820ae7730c..1a6a6bfcc4 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/CurvesAlgo.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/CurvesAlgo.cpp @@ -124,7 +124,7 @@ AtNode *CurvesAlgo::convert( const IECore::CurvesPrimitive *curves ) return result; } -AtNode *CurvesAlgo::convert( const std::vector &samples, const std::vector &sampleTimes ) +AtNode *CurvesAlgo::convert( const std::vector &samples, float motionStart, float motionEnd ) { AtNode *result = convertCommon( samples.front() ); @@ -155,7 +155,8 @@ AtNode *CurvesAlgo::convert( const std::vector IECore::msg( IECore::Msg::Warning, "CurvesAlgo::convert", "Missing sample for primitive variable \"N\" - not setting orientations." ); } - AiNodeSetArray( result, "deform_time_samples", AiArrayConvert( sampleTimes.size(), 1, AI_TYPE_FLOAT, &sampleTimes.front() ) ); + AiNodeSetFlt( result, "motion_start", motionStart ); + AiNodeSetFlt( result, "motion_end", motionEnd ); return result; } diff --git a/contrib/IECoreArnold/src/IECoreArnold/InstancingConverter.cpp b/contrib/IECoreArnold/src/IECoreArnold/InstancingConverter.cpp index 6cf30ae1d7..0ace65b14a 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/InstancingConverter.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/InstancingConverter.cpp @@ -88,12 +88,12 @@ AtNode *InstancingConverter::convert( const IECore::Primitive *primitive, const return NULL; } -AtNode *InstancingConverter::convert( const std::vector &samples, const std::vector &sampleTimes ) +AtNode *InstancingConverter::convert( const std::vector &samples, float motionStart, float motionEnd ) { - return convert( samples, sampleTimes, IECore::MurmurHash() ); + return convert( samples, motionStart, motionEnd, IECore::MurmurHash() ); } -AtNode *InstancingConverter::convert( const std::vector &samples, const std::vector &sampleTimes, const IECore::MurmurHash &additionalHash ) +AtNode *InstancingConverter::convert( const std::vector &samples, float motionStart, float motionEnd, const IECore::MurmurHash &additionalHash ) { IECore::MurmurHash h; for( std::vector::const_iterator it = samples.begin(), eIt = samples.end(); it != eIt; ++it ) @@ -106,7 +106,7 @@ AtNode *InstancingConverter::convert( const std::vectorcache.insert( a, h ) ) { std::vector objectSamples( samples.begin(), samples.end() ); - a->second = NodeAlgo::convert( objectSamples, sampleTimes ); + a->second = NodeAlgo::convert( objectSamples, motionStart, motionEnd ); return a->second; } else diff --git a/contrib/IECoreArnold/src/IECoreArnold/MeshAlgo.cpp b/contrib/IECoreArnold/src/IECoreArnold/MeshAlgo.cpp index a8397c881a..a57fbbd9e2 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/MeshAlgo.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/MeshAlgo.cpp @@ -391,7 +391,7 @@ AtNode *MeshAlgo::convert( const IECore::MeshPrimitive *mesh ) return result; } -AtNode *MeshAlgo::convert( const std::vector &samples, const std::vector &sampleTimes ) +AtNode *MeshAlgo::convert( const std::vector &samples, float motionStart, float motionEnd ) { AtNode *result = convertCommon( samples.front() ); @@ -428,7 +428,8 @@ AtNode *MeshAlgo::convert( const std::vector &sam // add time sampling - AiNodeSetArray( result, "deform_time_samples", AiArrayConvert( sampleTimes.size(), 1, AI_TYPE_FLOAT, &sampleTimes.front() ) ); + AiNodeSetFlt( result, "motion_start", motionStart ); + AiNodeSetFlt( result, "motion_end", motionEnd ); return result; } diff --git a/contrib/IECoreArnold/src/IECoreArnold/NodeAlgo.cpp b/contrib/IECoreArnold/src/IECoreArnold/NodeAlgo.cpp index 284a319d11..1d99d8be5b 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/NodeAlgo.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/NodeAlgo.cpp @@ -89,7 +89,7 @@ AtNode *convert( const IECore::Object *object ) return it->second.converter( object ); } -AtNode *convert( const std::vector &samples, const std::vector &sampleTimes ) +AtNode *convert( const std::vector &samples, float motionStart, float motionEnd ) { if( samples.empty() ) { @@ -115,7 +115,7 @@ AtNode *convert( const std::vector &samples, const std:: if( it->second.motionConverter ) { - return it->second.motionConverter( samples, sampleTimes ); + return it->second.motionConverter( samples, motionStart, motionEnd ); } else { @@ -128,6 +128,42 @@ void registerConverter( IECore::TypeId fromType, Converter converter, MotionConv registry().insert( Registry::value_type( fromType, Converters( converter, motionConverter ) ) ); } +void ensureUniformTimeSamples( const std::vector × ) +{ + if( times.size() == 0 ) + { + throw IECore::Exception( "Motion block times must not be empty" ); + } + + float motionStart = times[0]; + float motionEnd = times[ times.size() - 1 ]; + + for( unsigned int i = 0; i < times.size(); i++ ) + { + // Use a really coarse epsilon to check if the values are uniform - if someone is sloppy with + // floating point precision when computing their sample times, we don't want to stop them from rendering. + // But we should warn someone if they are actually trying to use a feature Arnold doesn't support. + const float uniformity_epsilon = 0.01; + float expectedTime = motionStart + ( motionEnd - motionStart ) / ( times.size() - 1 ) * i; + if( times[i] < expectedTime - uniformity_epsilon || times[i] > expectedTime + uniformity_epsilon ) + { + std::stringstream text; + text << "Arnold does not support non-uniform motion blocks.\n"; + text << "Invalid motion block: [ " << times[0]; + for( unsigned int j = 1; j < times.size(); j++ ) + { + text << ", " << times[j]; + } + text << " ]\n"; + text << "( sample " << i << ", with value " << times[i] << " does not match " << expectedTime << ")\n"; + throw IECore::Exception( text.str() ); + + + } + } + +} + } // namespace NodeAlgo } // namespace IECoreArnold diff --git a/contrib/IECoreArnold/src/IECoreArnold/PointsAlgo.cpp b/contrib/IECoreArnold/src/IECoreArnold/PointsAlgo.cpp index 263a5daab6..8cdbd8cda6 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/PointsAlgo.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/PointsAlgo.cpp @@ -116,7 +116,7 @@ AtNode *convert( const IECore::PointsPrimitive *points ) return result; } -AtNode *convert( const std::vector &samples, const std::vector &sampleTimes ) +AtNode *convert( const std::vector &samples, float motionStart, float motionEnd ) { AtNode *result = convertCommon( samples.front() ); @@ -124,7 +124,9 @@ AtNode *convert( const std::vector &samples, co ShapeAlgo::convertP( primitiveSamples, result, "points" ); ShapeAlgo::convertRadius( primitiveSamples, result ); - AiNodeSetArray( result, "deform_time_samples", AiArrayConvert( sampleTimes.size(), 1, AI_TYPE_FLOAT, &sampleTimes.front() ) ); + + AiNodeSetFlt( result, "motion_start", motionStart ); + AiNodeSetFlt( result, "motion_end", motionEnd ); /// \todo Aspect, rotation diff --git a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp index 289f9c03ef..786ba17636 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp @@ -104,20 +104,20 @@ RendererImplementation::AttributeState::AttributeState( const AttributeState &ot extern AtNodeMethods *ieDisplayDriverMethods; IECoreArnold::RendererImplementation::RendererImplementation() - : m_defaultFilter( 0 ) + : m_defaultFilter( 0 ), m_motionBlockSize( 0 ) { constructCommon( Render ); } IECoreArnold::RendererImplementation::RendererImplementation( const std::string &assFileName ) - : m_defaultFilter( 0 ) + : m_defaultFilter( 0 ), m_motionBlockSize( 0 ) { m_assFileName = assFileName; constructCommon( AssGen ); } IECoreArnold::RendererImplementation::RendererImplementation( const RendererImplementation &other ) - : m_transformStack( other.m_transformStack, /* flatten = */ true ) + : m_transformStack( other.m_transformStack, /* flatten = */ true ), m_motionBlockSize( 0 ) { constructCommon( Procedural ); m_instancingConverter = other.m_instancingConverter; @@ -125,6 +125,7 @@ IECoreArnold::RendererImplementation::RendererImplementation( const RendererImpl } IECoreArnold::RendererImplementation::RendererImplementation( const AtNode *proceduralNode ) + : m_motionBlockSize( 0 ) { constructCommon( Procedural ); m_instancingConverter = new InstancingConverter; @@ -357,9 +358,12 @@ void IECoreArnold::RendererImplementation::transformEnd() void IECoreArnold::RendererImplementation::setTransform( const Imath::M44f &m ) { - if( m_motionTimes.size() && !m_transformStack.inMotion() ) + if( m_motionBlockSize && !m_transformStack.inMotion() ) { - m_transformStack.motionBegin( m_motionTimes ); + // We call motionBegin on the transform stack with a dummy time sample vector of the correct size + // Arnold doesn't support non-uniform time sampling, so we won't use the actual values, we just + // need the transform block to know how many values to expect. + m_transformStack.motionBegin( std::vector( m_motionBlockSize, 0.0f ) ); } m_transformStack.set( m ); } @@ -382,9 +386,10 @@ Imath::M44f IECoreArnold::RendererImplementation::getTransform( const std::strin void IECoreArnold::RendererImplementation::concatTransform( const Imath::M44f &m ) { - if( m_motionTimes.size() && !m_transformStack.inMotion() ) + if( m_motionBlockSize && !m_transformStack.inMotion() ) { - m_transformStack.motionBegin( m_motionTimes ); + // See comment in setTransform + m_transformStack.motionBegin( std::vector( m_motionBlockSize, 0.0f ) ); } m_transformStack.concatenate( m ); } @@ -542,24 +547,32 @@ void IECoreArnold::RendererImplementation::illuminate( const std::string &lightH void IECoreArnold::RendererImplementation::motionBegin( const std::set × ) { - if( !m_motionTimes.empty() ) + if( m_motionBlockSize ) { msg( Msg::Error, "IECoreArnold::RendererImplementation::motionBegin", "Already in a motion block." ); return; } - m_motionTimes.insert( m_motionTimes.end(), times.begin(), times.end() ); + // TODO - is it worth the time to do this, in order to avoid silently giving incorrect results? + std::vector timesVector; + timesVector.insert( timesVector.end(), times.begin(), times.end() ); + NodeAlgo::ensureUniformTimeSamples( timesVector ); + + m_motionStart = *times.begin(); + m_motionEnd = *(--times.end()); + m_motionBlockSize = times.size(); + } void IECoreArnold::RendererImplementation::motionEnd() { - if( m_motionTimes.empty() ) + if( !m_motionBlockSize ) { msg( Msg::Error, "IECoreArnold::RendererImplementation::motionEnd", "Not in a motion block." ); return; } - m_motionTimes.clear(); + m_motionBlockSize = 0; m_motionPrimitives.clear(); if( m_transformStack.inMotion() ) { @@ -769,12 +782,12 @@ bool IECoreArnold::RendererImplementation::automaticInstancing() const void IECoreArnold::RendererImplementation::addPrimitive( const IECore::Primitive *primitive, const std::string &attributePrefix ) { - if( !m_motionTimes.empty() ) + if( m_motionBlockSize ) { // We're in a motion block. Just store samples // until we have all of them. m_motionPrimitives.push_back( primitive ); - if( m_motionPrimitives.size() != m_motionTimes.size() ) + if( m_motionPrimitives.size() != m_motionBlockSize ) { return; } @@ -797,14 +810,14 @@ void IECoreArnold::RendererImplementation::addPrimitive( const IECore::Primitive it->second->hash( hash ); } } - if( !m_motionTimes.empty() ) + if( m_motionBlockSize ) { vector prims; for( vector::const_iterator it = m_motionPrimitives.begin(), eIt = m_motionPrimitives.end(); it != eIt; ++it ) { prims.push_back( it->get() ); } - shape = m_instancingConverter->convert( prims, m_motionTimes, hash ); + shape = m_instancingConverter->convert( prims, m_motionStart, m_motionEnd, hash ); } else { @@ -813,14 +826,14 @@ void IECoreArnold::RendererImplementation::addPrimitive( const IECore::Primitive } else { - if( !m_motionTimes.empty() ) + if( m_motionBlockSize ) { vector prims; for( vector::const_iterator it = m_motionPrimitives.begin(), eIt = m_motionPrimitives.end(); it != eIt; ++it ) { prims.push_back( it->get() ); } - shape = NodeAlgo::convert( prims, m_motionTimes ); + shape = NodeAlgo::convert( prims, m_motionStart, m_motionEnd ); } else { @@ -881,23 +894,15 @@ void IECoreArnold::RendererImplementation::applyTransformToNode( AtNode *node ) } else { - AtArray *times = AiArrayAllocate( numSamples, 1, AI_TYPE_FLOAT ); AtArray *matrices = AiArrayAllocate( 1, numSamples, AI_TYPE_MATRIX ); for( size_t i = 0; i < numSamples; ++i ) { - AiArraySetFlt( times, i, m_transformStack.sampleTime( i ) ); M44f m = m_transformStack.sample( i ); AiArraySetMtx( matrices, i, *reinterpret_cast( (float(*)[4][4])&m.x ) ); } AiNodeSetArray( node, "matrix", matrices ); - if( AiNodeEntryLookUpParameter( AiNodeGetNodeEntry( node ), "transform_time_samples" ) ) - { - AiNodeSetArray( node, "transform_time_samples", times ); - } - else - { - AiNodeSetArray( node, "time_samples", times ); - } + AiNodeSetFlt( node, "motion_start", m_motionStart ); + AiNodeSetFlt( node, "motion_end", m_motionEnd ); } } diff --git a/contrib/IECoreArnold/src/IECoreArnold/SphereAlgo.cpp b/contrib/IECoreArnold/src/IECoreArnold/SphereAlgo.cpp index 72686c9d7a..78fdc5db72 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/SphereAlgo.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/SphereAlgo.cpp @@ -87,7 +87,7 @@ AtNode *SphereAlgo::convert( const IECore::SpherePrimitive *sphere ) return result; } -AtNode *SphereAlgo::convert( const std::vector &samples, const std::vector &sampleTimes ) +AtNode *SphereAlgo::convert( const std::vector &samples, float motionStart, float motionEnd ) { AtNode *result = AiNode( "sphere" ); ShapeAlgo::convertPrimitiveVariables( samples.front(), result ); @@ -102,7 +102,8 @@ AtNode *SphereAlgo::convert( const std::vector } AiNodeSetArray( result, "radius", radiusSamples ); - AiNodeSetArray( result, "deform_time_samples", AiArrayConvert( sampleTimes.size(), 1, AI_TYPE_FLOAT, &sampleTimes.front() ) ); + AiNodeSetFlt( result, "motion_start", motionStart ); + AiNodeSetFlt( result, "motion_end", motionEnd ); return result; } diff --git a/contrib/IECoreArnold/src/IECoreArnold/bindings/InstancingConverterBinding.cpp b/contrib/IECoreArnold/src/IECoreArnold/bindings/InstancingConverterBinding.cpp index deb38cb751..b83fed02e4 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/bindings/InstancingConverterBinding.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/bindings/InstancingConverterBinding.cpp @@ -55,32 +55,28 @@ using namespace IECoreArnoldBindings; namespace { -object convertWrapper1( InstancingConverter &c, object pythonSamples, object pythonSampleTimes ) +object convertWrapper1( InstancingConverter &c, object pythonSamples, float motionStart, float motionEnd ) { vector samples; - vector sampleTimes; boost::python::container_utils::extend_container( samples, pythonSamples ); - boost::python::container_utils::extend_container( sampleTimes, pythonSampleTimes ); AtNode *node; { IECorePython::ScopedGILRelease gilRelease; - node = c.convert( samples, sampleTimes ); + node = c.convert( samples, motionStart, motionEnd ); } return atNodeToPythonObject( node ); } -object convertWrapper2( InstancingConverter &c, object pythonSamples, object pythonSampleTimes, const IECore::MurmurHash &h ) +object convertWrapper2( InstancingConverter &c, object pythonSamples, float motionStart, float motionEnd, const IECore::MurmurHash &h ) { vector samples; - vector sampleTimes; boost::python::container_utils::extend_container( samples, pythonSamples ); - boost::python::container_utils::extend_container( sampleTimes, pythonSampleTimes ); AtNode *node; { IECorePython::ScopedGILRelease gilRelease; - node = c.convert( samples, sampleTimes, h ); + node = c.convert( samples, motionStart, motionEnd, h ); } return atNodeToPythonObject( node ); } diff --git a/contrib/IECoreArnold/src/IECoreArnold/bindings/NodeAlgoBinding.cpp b/contrib/IECoreArnold/src/IECoreArnold/bindings/NodeAlgoBinding.cpp index c9baed8060..ac4c9cc89c 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/bindings/NodeAlgoBinding.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/bindings/NodeAlgoBinding.cpp @@ -51,14 +51,12 @@ object convertWrapper( const IECore::Object *object ) return atNodeToPythonObject( NodeAlgo::convert( object ) ); } -object convertWrapper2( object pythonSamples, object pythonSampleTimes ) +object convertWrapper2( object pythonSamples, float motionStart, float motionEnd ) { std::vector samples; - std::vector sampleTimes; container_utils::extend_container( samples, pythonSamples ); - container_utils::extend_container( sampleTimes, pythonSampleTimes ); - return atNodeToPythonObject( NodeAlgo::convert( samples, sampleTimes ) ); + return atNodeToPythonObject( NodeAlgo::convert( samples, motionStart, motionEnd ) ); } } // namespace diff --git a/contrib/IECoreArnold/test/IECoreArnold/CurvesTest.py b/contrib/IECoreArnold/test/IECoreArnold/CurvesTest.py index 97f8afa914..565668b2be 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/CurvesTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/CurvesTest.py @@ -59,7 +59,7 @@ def testMotion( self ) : with IECoreArnold.UniverseBlock( writable = True ) : - n = IECoreArnold.NodeAlgo.convert( [ c1, c2 ], [ -0.25, 0.25 ] ) + n = IECoreArnold.NodeAlgo.convert( [ c1, c2 ], -0.25, 0.25 ) a = arnold.AiNodeGetArray( n, "points" ) self.assertEqual( arnold.AiArrayGetNumElements( a.contents ), 4 ) @@ -70,11 +70,8 @@ def testMotion( self ) : for i in range( 4, 8 ) : self.assertEqual( arnold.AiArrayGetVec( a, i ), arnold.AtVector( 2 ) ) - a = arnold.AiNodeGetArray( n, "deform_time_samples" ) - self.assertEqual( a.contents.nelements, 2 ) - self.assertEqual( a.contents.nkeys, 1 ) - self.assertEqual( arnold.AiArrayGetFlt( a, 0 ), -0.25 ) - self.assertEqual( arnold.AiArrayGetFlt( a, 1 ), 0.25 ) + self.assertEqual( arnold.AiNodeGetFlt( n, "motion_start" ), -0.25 ) + self.assertEqual( arnold.AiNodeGetFlt( n, "motion_end" ), 0.25 ) def testNPrimitiveVariable( self ) : @@ -115,7 +112,7 @@ def testNPrimitiveVariable( self ) : IECore.V3fVectorData( [ IECore.V3f( 0, math.sin( x + 0.2 ), math.cos( x + 0.2 ) ) for x in range( 0, 4 ) ] ) ) - n = IECoreArnold.NodeAlgo.convert( [ c, c2 ], [ 0.0, 1.0 ] ) + n = IECoreArnold.NodeAlgo.convert( [ c, c2 ], 0.0, 1.0 ) self.assertEqual( arnold.AiNodeGetStr( n, "mode" ), "oriented" ) orientations = arnold.AiNodeGetArray( n, "orientations" ) diff --git a/contrib/IECoreArnold/test/IECoreArnold/InstancingConverterTest.py b/contrib/IECoreArnold/test/IECoreArnold/InstancingConverterTest.py index 376987b93e..c4bac5d870 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/InstancingConverterTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/InstancingConverterTest.py @@ -152,19 +152,19 @@ def testMotion( self ) : m1 = IECore.MeshPrimitive.createPlane( IECore.Box2f( IECore.V2f( -1 ), IECore.V2f( 1 ) ) ) m2 = IECore.MeshPrimitive.createPlane( IECore.Box2f( IECore.V2f( -2 ), IECore.V2f( 2 ) ) ) - n1 = c.convert( [ m1, m2 ], [ -0.25, 0.25 ] ) + n1 = c.convert( [ m1, m2 ], -0.25, 0.25 ) self.assertEqual( arnold.AiNodeEntryGetName( arnold.AiNodeGetNodeEntry( n1 ) ), "polymesh" ) self.assertEqual( arnold.AiArrayGetNumKeys( arnold.AiNodeGetArray( n1, "vlist" ).contents ), 2 ) - n2 = c.convert( [ m1, m2 ], [ -0.25, 0.25 ] ) + n2 = c.convert( [ m1, m2 ], -0.25, 0.25 ) self.assertEqual( arnold.AiNodeEntryGetName( arnold.AiNodeGetNodeEntry( n2 ) ), "ginstance" ) self.assertEqual( arnold.AiNodeGetPtr( n2, "node" ), ctypes.addressof( n1.contents ) ) - n3 = c.convert( [ m2, m1 ], [ -0.25, 0.25 ] ) + n3 = c.convert( [ m2, m1 ], -0.25, 0.25 ) self.assertEqual( arnold.AiNodeEntryGetName( arnold.AiNodeGetNodeEntry( n1 ) ), "polymesh" ) self.assertEqual( arnold.AiArrayGetNumKeys( arnold.AiNodeGetArray( n1, "vlist" ).contents ), 2 ) - n4 = c.convert( [ m1, m2 ], [ -0.5, 0.5 ] ) + n4 = c.convert( [ m1, m2 ], -0.5, 0.5 ) self.assertEqual( arnold.AiNodeEntryGetName( arnold.AiNodeGetNodeEntry( n1 ) ), "polymesh" ) self.assertEqual( arnold.AiArrayGetNumKeys( arnold.AiNodeGetArray( n1, "vlist" ).contents ), 2 ) diff --git a/contrib/IECoreArnold/test/IECoreArnold/MeshTest.py b/contrib/IECoreArnold/test/IECoreArnold/MeshTest.py index 7322875365..836d024fc9 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/MeshTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/MeshTest.py @@ -175,7 +175,7 @@ def testMotion( self ) : with IECoreArnold.UniverseBlock( writable = True ) : - node = IECoreArnold.NodeAlgo.convert( [ m1, m2 ], [ -0.25, 0.25 ] ) + node = IECoreArnold.NodeAlgo.convert( [ m1, m2 ], -0.25, 0.25 ) vList = arnold.AiNodeGetArray( node, "vlist" ) self.assertEqual( arnold.AiArrayGetNumElements( vList.contents ), 4 ) @@ -197,11 +197,8 @@ def testMotion( self ) : n = arnold.AiArrayGetVec( nList, i ) self.assertEqual( IECore.V3f( n.x, n.y, n.z ), m2["N"].data[i-4] ) - a = arnold.AiNodeGetArray( node, "deform_time_samples" ) - self.assertEqual( a.contents.nelements, 2 ) - self.assertEqual( a.contents.nkeys, 1 ) - self.assertEqual( arnold.AiArrayGetFlt( a, 0 ), -0.25 ) - self.assertEqual( arnold.AiArrayGetFlt( a, 1 ), 0.25 ) + self.assertEqual( arnold.AiNodeGetFlt( node, "motion_start" ), -0.25 ) + self.assertEqual( arnold.AiNodeGetFlt( node, "motion_end" ), 0.25 ) def testClashingPrimitiveVariables( self ) : # make sure that names of arnold built-in's can't be used as names for primitive variables diff --git a/contrib/IECoreArnold/test/IECoreArnold/PointsTest.py b/contrib/IECoreArnold/test/IECoreArnold/PointsTest.py index 8dece2e88e..f25c8c3eb7 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/PointsTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/PointsTest.py @@ -190,7 +190,7 @@ def testMotion( self ) : with IECoreArnold.UniverseBlock( writable = True ) : - n = IECoreArnold.NodeAlgo.convert( [ p1, p2 ], [ -0.25, 0.25 ] ) + n = IECoreArnold.NodeAlgo.convert( [ p1, p2 ], -0.25, 0.25 ) a = arnold.AiNodeGetArray( n, "points" ) self.assertEqual( arnold.AiArrayGetNumElements( a.contents ), 10 ) @@ -207,11 +207,8 @@ def testMotion( self ) : self.assertEqual( arnold.AiArrayGetVec( a, i ), arnold.AtVector( 20 ) ) self.assertEqual( arnold.AiArrayGetFlt( r, i ), 1 ) - a = arnold.AiNodeGetArray( n, "deform_time_samples" ) - self.assertEqual( a.contents.nelements, 2 ) - self.assertEqual( a.contents.nkeys, 1 ) - self.assertEqual( arnold.AiArrayGetFlt( a, 0 ), -0.25 ) - self.assertEqual( arnold.AiArrayGetFlt( a, 1 ), 0.25 ) + self.assertEqual( arnold.AiNodeGetFlt( n, "motion_start" ), -0.25 ) + self.assertEqual( arnold.AiNodeGetFlt( n, "motion_end" ), 0.25 ) if __name__ == "__main__": unittest.main() diff --git a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py index 3ebfee1fb9..b5492648a1 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py @@ -837,6 +837,43 @@ def testTransformationMotionBlur( self ) : e.pointAtUV( IECore.V2f( 0.5 ), result ) self.assertAlmostEqual( result.floatPrimVar( e.A() ), 0.5, 2 ) + def testNonUniformMotionBlur( self ) : + + r = IECoreArnold.Renderer() + + r.display( "test", "ieDisplay", "rgba", { "driverType" : "ImageDisplayDriver", "handle" : "test" } ) + r.setOption( "ai:AA_samples", IECore.IntData( 20 ) ) + + r.camera( "main", { "resolution" : IECore.V2i( 128, 128 ), "shutter" : IECore.V2f( 0, 1 ) } ) + + with IECore.WorldBlock( r ) : + + r.concatTransform( IECore.M44f.createTranslated( IECore.V3f( 0, 0, -5 ) ) ) + + # A motion block that has slightly non-uniform sampling, but not enough to notice + # We should allow it, since the user won't notice that Arnold is ignoring the non-uniformity + with IECore.MotionBlock( r, [ 0, 0.3333, 0.6666, 1 ] ) : + r.concatTransform( IECore.M44f.createTranslated( IECore.V3f( -1, 0, 0 ) ) ) + r.concatTransform( IECore.M44f.createTranslated( IECore.V3f( -0.3333333333, 0, 0 ) ) ) + r.concatTransform( IECore.M44f.createTranslated( IECore.V3f( 0.33333333333, 0, 0 ) ) ) + r.concatTransform( IECore.M44f.createTranslated( IECore.V3f( 1, 0, 0 ) ) ) + + with self.assertRaises( RuntimeError ): + # This block is actually non-uniform, and won't render correctly, so we should fail + with IECore.MotionBlock( r, [ 0, 0.333, 0.666, 2 ] ): + pass + + mesh = IECore.MeshPrimitive.createPlane( IECore.Box2f( IECore.V2f( -0.5 ), IECore.V2f( 0.5 ) ) ) + mesh.render( r ) + + + image = IECore.ImageDisplayDriver.removeStoredImage( "test" ) + e = IECore.PrimitiveEvaluator.create( image ) + result = e.createResult() + + e.pointAtUV( IECore.V2f( 0.5 ), result ) + self.assertAlmostEqual( result.floatPrimVar( e.A() ), 0.5, 2 ) + def testProcedural( self ) : r = IECoreArnold.Renderer( "/tmp/test.ass" ) diff --git a/contrib/IECoreArnold/test/IECoreArnold/SphereAlgoTest.py b/contrib/IECoreArnold/test/IECoreArnold/SphereAlgoTest.py index 2c9bb6771f..34a890b726 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/SphereAlgoTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/SphereAlgoTest.py @@ -56,16 +56,15 @@ def testConvertWithMotion( self ) : with IECoreArnold.UniverseBlock( writable = True ) : - n = IECoreArnold.NodeAlgo.convert( s, [ 0, 1 ] ) + n = IECoreArnold.NodeAlgo.convert( s, 0, 1 ) self.assertEqual( arnold.AiNodeEntryGetName( arnold.AiNodeGetNodeEntry( n ) ), "sphere" ) a = arnold.AiNodeGetArray( n, "radius" ) self.assertEqual( arnold.AiArrayGetFlt( a, 0 ), 0.25 ) self.assertEqual( arnold.AiArrayGetFlt( a, 1 ), 0.5 ) - a = arnold.AiNodeGetArray( n, "deform_time_samples" ) - self.assertEqual( arnold.AiArrayGetFlt( a, 0 ), 0 ) - self.assertEqual( arnold.AiArrayGetFlt( a, 1 ), 1 ) + self.assertEqual( arnold.AiNodeGetFlt( n, "motion_start" ), 0 ) + self.assertEqual( arnold.AiNodeGetFlt( n, "motion_end" ), 1 ) def testPrimitiveVariables( self ) : From dde622388c1ecaa302c61df72733571876958dad Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Fri, 15 Sep 2017 13:42:22 -0700 Subject: [PATCH 20/26] IECoreArnold : Arnold 5 renamed aspect_ratio to pixel_aspect_ratio --- .../IECoreArnold/src/IECoreArnold/RendererImplementation.cpp | 2 +- .../IECoreArnold/src/IECoreArnold/outputDriver/OutputDriver.cpp | 2 +- contrib/IECoreArnold/test/IECoreArnold/RendererTest.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp index 786ba17636..f28a3d9bd4 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp @@ -238,7 +238,7 @@ void IECoreArnold::RendererImplementation::camera( const std::string &name, cons AiNodeSetInt( options, "yres", resolution->readable().y ); const FloatData *pixelAspectRatio = cortexCamera->parametersData()->member( "pixelAspectRatio" ); - AiNodeSetFlt( options, "aspect_ratio", 1.0f / pixelAspectRatio->readable() ); // arnold is y/x, we're x/y + AiNodeSetFlt( options, "pixel_aspect_ratio", 1.0f / pixelAspectRatio->readable() ); // arnold is y/x, we're x/y } void IECoreArnold::RendererImplementation::display( const std::string &name, const std::string &type, const std::string &data, const IECore::CompoundDataMap ¶meters ) diff --git a/contrib/IECoreArnold/src/IECoreArnold/outputDriver/OutputDriver.cpp b/contrib/IECoreArnold/src/IECoreArnold/outputDriver/OutputDriver.cpp index 3b212cc235..890c43f622 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/outputDriver/OutputDriver.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/outputDriver/OutputDriver.cpp @@ -193,7 +193,7 @@ void driverOpen( AtNode *node, struct AtOutputIterator *iterator, AtBBox2 displa // window. parameters->writable()["pixelAspect"] = new FloatData( // Arnold is y/x, we're x/y - 1.0f / AiNodeGetFlt( AiUniverseGetOptions(), "aspect_ratio" ) + 1.0f / AiNodeGetFlt( AiUniverseGetOptions(), "pixel_aspect_ratio" ) ); const std::string driverType = AiNodeGetStr( node, "driverType" ).c_str(); diff --git a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py index b5492648a1..7f2bd48013 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py @@ -762,7 +762,7 @@ def testPixelAspectRatio( self ) : pass ass = "".join( file( self.__assFileName ).readlines() ) - self.assertTrue( "aspect_ratio 0.5" in ass ) + self.assertTrue( "pixel_aspect_ratio 0.5" in ass ) def testLightPrefixes( self ) : From a78e3627f117970a3a118bb0de1b63cef62cf1de Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Mon, 18 Sep 2017 14:33:45 -0700 Subject: [PATCH 21/26] IECoreArnold::RenderImplemenation : Update for Arnold 5 procedural interface --- .../private/RendererImplementation.h | 8 ++-- .../IECoreArnold/RendererImplementation.cpp | 48 ++++++------------- .../test/IECoreArnold/RendererTest.py | 16 +++---- 3 files changed, 25 insertions(+), 47 deletions(-) diff --git a/contrib/IECoreArnold/include/IECoreArnold/private/RendererImplementation.h b/contrib/IECoreArnold/include/IECoreArnold/private/RendererImplementation.h index b8751b3739..2f45734d14 100644 --- a/contrib/IECoreArnold/include/IECoreArnold/private/RendererImplementation.h +++ b/contrib/IECoreArnold/include/IECoreArnold/private/RendererImplementation.h @@ -149,11 +149,11 @@ class RendererImplementation : public IECore::Renderer RendererPtr renderer; }; - static int procLoader( AtProcVtable *vTable ); + static int procFunc( AtProceduralNodeMethods *methods ); static int procInit( AtNode *node, void **userPtr ); - static int procCleanup( void *userPtr ); - static int procNumNodes( void *userPtr ); - static AtNode *procGetNode( void *userPtr, int i ); + static int procCleanup( const AtNode *node, void *userPtr ); + static int procNumNodes( const AtNode *node, void *userPtr ); + static AtNode *procGetNode( const AtNode *node, void *userPtr, int i ); boost::shared_ptr m_universe; InstancingConverterPtr m_instancingConverter; diff --git a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp index f28a3d9bd4..4af9ea9e44 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp @@ -645,14 +645,12 @@ void IECoreArnold::RendererImplementation::geometry( const std::string &type, co ///////////////////////////////////////////////////////////////////////////////////////// // procedurals ///////////////////////////////////////////////////////////////////////////////////////// - -int IECoreArnold::RendererImplementation::procLoader( AtProcVtable *vTable ) +int IECoreArnold::RendererImplementation::procFunc( AtProceduralNodeMethods *methods ) { - vTable->Init = procInit; - vTable->Cleanup = procCleanup; - vTable->NumNodes = procNumNodes; - vTable->GetNode = procGetNode; - strcpy( vTable->version, AI_VERSION ); + methods->Init = procInit; + methods->Cleanup = procCleanup; + methods->NumNodes = procNumNodes; + methods->GetNode = procGetNode; return 1; } @@ -665,20 +663,20 @@ int IECoreArnold::RendererImplementation::procInit( AtNode *node, void **userPtr return 1; } -int IECoreArnold::RendererImplementation::procCleanup( void *userPtr ) +int IECoreArnold::RendererImplementation::procCleanup( const AtNode *node, void *userPtr ) { ProceduralData *data = (ProceduralData *)( userPtr ); delete data; return 1; } -int IECoreArnold::RendererImplementation::procNumNodes( void *userPtr ) +int IECoreArnold::RendererImplementation::procNumNodes( const AtNode *node, void *userPtr ) { ProceduralData *data = (ProceduralData *)( userPtr ); return data->renderer->m_implementation->m_nodes.size(); } -AtNode* IECoreArnold::RendererImplementation::procGetNode( void *userPtr, int i ) +AtNode* IECoreArnold::RendererImplementation::procGetNode( const AtNode *node, void *userPtr, int i ) { ProceduralData *data = (ProceduralData *)( userPtr ); return data->renderer->m_implementation->m_nodes[i]; @@ -697,17 +695,12 @@ void IECoreArnold::RendererImplementation::procedural( IECore::Renderer::Procedu if( const ExternalProcedural *externalProc = dynamic_cast( proc.get() ) ) { - // Allow a parameter "ai:nodeType" == "volume" to create a volume shape rather - // than a procedural shape. Volume shapes provide "dso", "min" and "max" parameters - // just as procedural shapes do, so the mapping is a fairly natural one. - CompoundDataMap::const_iterator nodeTypeIt = externalProc->parameters().find( "ai:nodeType" ); - if( nodeTypeIt != externalProc->parameters().end() && nodeTypeIt->second->isInstanceOf( StringData::staticTypeId() ) ) - { - nodeType = static_cast( nodeTypeIt->second.get() )->readable(); - } - node = AiNode( nodeType.c_str() ); - - AiNodeSetStr( node, "dso", externalProc->fileName().c_str() ); + // \todo In Arnold, external procedurals register node types, and then we use the node types + // just like built in nodes - we don't reference the filename of the dso that defines the node type. + // So here we just interpret "filename" as the node type to create. + // Where should this be documented. Should we change the name of the parameter to ExternalProcedural + // to be something other than "filename" in Cortex 10? + node = AiNode( externalProc->fileName().c_str() ); ParameterAlgo::setParameters( node, externalProc->parameters() ); applyTransformToNode( node ); } @@ -728,7 +721,7 @@ void IECoreArnold::RendererImplementation::procedural( IECore::Renderer::Procedu bound = transformedBound; } - AiNodeSetPtr( node, "funcptr", (void *)procLoader ); + AiNodeSetPtr( node, "funcptr", (void *)procFunc ); ProceduralData *data = new ProceduralData; data->procedural = proc; @@ -737,17 +730,6 @@ void IECoreArnold::RendererImplementation::procedural( IECore::Renderer::Procedu AiNodeSetPtr( node, "userptr", data ); } - if( bound != Procedural::noBound ) - { - AiNodeSetVec( node, "min", bound.min.x, bound.min.y, bound.min.z ); - AiNodeSetVec( node, "max", bound.max.x, bound.max.y, bound.max.z ); - } - else - { - // No bound available - expand procedural immediately. - AiNodeSetBool( node, "load_at_init", true ); - } - if( nodeType == "procedural" ) { // We call addNode() rather than addShape() as we don't want to apply transforms and diff --git a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py index 7f2bd48013..c64e0861bd 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/RendererTest.py @@ -723,7 +723,7 @@ def testExternalProcedural( self ) : r.procedural( r.ExternalProcedural( - "test.so", + "volume", IECore.Box3f( IECore.V3f( 1, 2, 3 ), IECore.V3f( 4, 5, 6 ) @@ -739,10 +739,7 @@ def testExternalProcedural( self ) : ass = "".join( file( self.__assFileName ).readlines() ) - self.assertTrue( "procedural" in ass ) - self.assertTrue( "min 1 2 3" in ass ) - self.assertTrue( "max 4 5 6" in ass ) - self.assertTrue( "dso \"test.so\"" in ass ) + self.assertTrue( "volume" in ass ) self.assertTrue( "declare stringParm constant STRING" in ass ) self.assertTrue( "declare floatParm constant FLOAT" in ass ) self.assertTrue( "declare intParm constant INT" in ass ) @@ -880,12 +877,14 @@ def testProcedural( self ) : with IECore.WorldBlock( r ) : + # In Arnold 5, external procedurals register node types that look just like the built-in + # ones. So we need to be able to use ExternalProcedural to create an arbitrary node type, + # instead of passing in a filename. Test with a volume, because this node type exists by default. r.procedural( r.ExternalProcedural( - "someVolumeThing.so", + "volume", IECore.Box3f( IECore.V3f( -1, -2, -3 ), IECore.V3f( 4, 5, 6 ) ), { - "ai:nodeType" : "volume", "testFloat" : 0.5 } ) @@ -894,9 +893,6 @@ def testProcedural( self ) : volume = self.__allNodes( type = arnold.AI_NODE_SHAPE )[-1] self.assertEqual( arnold.AiNodeEntryGetName( arnold.AiNodeGetNodeEntry( volume ) ), "volume" ) - self.assertEqual( arnold.AiNodeGetVec( volume, "min" ), arnold.AtVector( -1, -2, -3 ) ) - self.assertEqual( arnold.AiNodeGetVec( volume, "max" ), arnold.AtVector( 4, 5, 6 ) ) - self.assertEqual( arnold.AiNodeGetStr( volume, "dso" ), "someVolumeThing.so" ) self.assertEqual( arnold.AiNodeGetFlt( volume, "testFloat" ), 0.5 ) def tearDown( self ) : From c3771b69b5460ed002dd9faee0311e615dac10f9 Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Mon, 18 Sep 2017 14:34:19 -0700 Subject: [PATCH 22/26] IECoreArnold::Procedural : Update for Arnold 5, remove obselete test --- .../ProceduralHolderTranslator.cpp | 12 +---- .../IECoreArnold/procedural/Procedural.cpp | 45 ++++++++++++------- .../test/IECoreArnold/ProceduralTest.py | 22 --------- .../data/assFiles/proceduralDSO.ass | 11 +---- 4 files changed, 30 insertions(+), 60 deletions(-) diff --git a/contrib/IECoreArnold/src/IECoreArnold/mtoaExtension/ProceduralHolderTranslator.cpp b/contrib/IECoreArnold/src/IECoreArnold/mtoaExtension/ProceduralHolderTranslator.cpp index fa272b6e5f..79251e6b96 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/mtoaExtension/ProceduralHolderTranslator.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/mtoaExtension/ProceduralHolderTranslator.cpp @@ -62,7 +62,7 @@ class ProceduralHolderTranslator : public CShapeTranslator if (m_isMasterDag) { - return AddArnoldNode( "procedural" ); + return AddArnoldNode( "ieProcedural" ); } else { @@ -158,16 +158,6 @@ class ProceduralHolderTranslator : public CShapeTranslator MFnDagNode fnDagNode( m_dagPath ); MBoundingBox bound = fnDagNode.boundingBox(); - AiNodeSetVec( node, "min", bound.min().x, bound.min().y, bound.min().z ); - AiNodeSetVec( node, "max", bound.max().x, bound.max().y, bound.max().z ); - - const char *dsoPath = getenv( "IECOREARNOLD_PROCEDURAL_PATH" ); - AiNodeSetStr( node, "dso", dsoPath ? dsoPath : "ieProcedural.so" ); - - AiNodeDeclare( node, "className", "constant STRING" ); - AiNodeDeclare( node, "classVersion", "constant INT" ); - AiNodeDeclare( node, "parameterValues", "constant ARRAY STRING" ); - // cast should be ok as we're registered to only work on procedural holders IECoreMaya::ProceduralHolder *pHolder = static_cast( fnDagNode.userNode() ); diff --git a/contrib/IECoreArnold/src/IECoreArnold/procedural/Procedural.cpp b/contrib/IECoreArnold/src/IECoreArnold/procedural/Procedural.cpp index 9af662ce3a..0041cad73b 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/procedural/Procedural.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/procedural/Procedural.cpp @@ -99,7 +99,16 @@ static void initialisePython() PyEval_ReleaseThread( PyThreadState_Get() ); } -static int procInit( AtNode *node, void **userPtr ) +AI_PROCEDURAL_NODE_EXPORT_METHODS(IECoreProcedural); + +node_parameters +{ + AiParameterStr( "className", "" ); + AiParameterInt( "classVersion", 1 ); + AiParameterArray( "parameterValues", AiArray(0, 1, AI_TYPE_STRING) ); +} + +procedural_init { // load the class @@ -124,7 +133,7 @@ static int procInit( AtNode *node, void **userPtr ) { // hack to workaround ass parsing errors /// \todo Remove when we get the Arnold version that fixes this - std::string s = AiArrayGetStr( parameterValues, i ); + std::string s = AiArrayGetStr( parameterValues, i ).c_str(); for( size_t c = 0; crender( renderer.get() ); renderer->addRef(); - *userPtr = renderer.get(); + *user_ptr = renderer.get(); } else { - *userPtr = 0; + *user_ptr = 0; } return 1; } -static int procCleanup( void *userPtr ) +procedural_cleanup { - IECoreArnold::Renderer *renderer = (IECoreArnold::Renderer *)( userPtr ); + IECoreArnold::Renderer *renderer = (IECoreArnold::Renderer *)( user_ptr ); if( renderer ) { renderer->removeRef(); @@ -183,27 +192,29 @@ static int procCleanup( void *userPtr ) return 1; } -static int procNumNodes( void *userPtr ) +procedural_num_nodes { - IECoreArnold::Renderer *renderer = (IECoreArnold::Renderer *)( userPtr ); + IECoreArnold::Renderer *renderer = (IECoreArnold::Renderer *)( user_ptr ); return renderer ? renderer->numProceduralNodes() : 0; } -static AtNode* procGetNode( void *userPtr, int i ) +procedural_get_node { - IECoreArnold::Renderer *renderer = (IECoreArnold::Renderer *)( userPtr ); + IECoreArnold::Renderer *renderer = (IECoreArnold::Renderer *)( user_ptr ); return renderer ? (AtNode *)renderer->proceduralNode( i ) : 0; } -AI_EXPORT_LIB int ProcLoader( AtProcVtable *vTable ) +node_loader { + if (i>0) + return false; - vTable->Init = procInit; - vTable->Cleanup = procCleanup; - vTable->NumNodes = procNumNodes; - vTable->GetNode = procGetNode; - strcpy( vTable->version, AI_VERSION ); + node->methods = IECoreProcedural; + node->output_type = AI_TYPE_NONE; + node->name = "ieProcedural"; + node->node_type = AI_NODE_SHAPE_PROCEDURAL; + strcpy(node->version, AI_VERSION); - return 1; + return true; } diff --git a/contrib/IECoreArnold/test/IECoreArnold/ProceduralTest.py b/contrib/IECoreArnold/test/IECoreArnold/ProceduralTest.py index 0be1956b9e..25c72ffdd0 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/ProceduralTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/ProceduralTest.py @@ -224,27 +224,5 @@ def hash( self ): self.failIf( "ignoring parameter max" in self.__arnoldMessages ) - def testNoBound( self ) : - - r = IECoreArnold.Renderer( "/tmp/test.ass" ) - - with IECore.WorldBlock( r ) : - - r.procedural( - r.ExternalProcedural( - "test.so", - r.Procedural.noBound, - {} - ) - ) - - l = "".join( open( "/tmp/test.ass" ).readlines() ) - - self.assertTrue( "procedural" in l ) - self.assertFalse( "inf" in l ) - self.assertFalse( re.search( r"\bmin\b", l ) ) - self.assertFalse( re.search( r"\bmax\b", l ) ) - self.assertTrue( "load_at_init" in l ) - if __name__ == "__main__": unittest.main() diff --git a/contrib/IECoreArnold/test/IECoreArnold/data/assFiles/proceduralDSO.ass b/contrib/IECoreArnold/test/IECoreArnold/data/assFiles/proceduralDSO.ass index a92b9a5665..397e905eef 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/data/assFiles/proceduralDSO.ass +++ b/contrib/IECoreArnold/test/IECoreArnold/data/assFiles/proceduralDSO.ass @@ -24,17 +24,8 @@ persp_camera name camera } -procedural +ieProcedural { - - dso contrib/IECoreArnold/test/IECoreArnold/plugins/ieProcedural.so - min -100 -100 -100 - max 100 100 100 - - declare className constant STRING - declare classVersion constant INT - declare parameterValues constant ARRAY STRING - matrix 1 0 0 0 0 1 0 0 0 0 1 0 0 0 -20 1 className "read" From 7ad65959160f73e84f0a04b9547fad26fb5255d5 Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Mon, 18 Sep 2017 18:20:10 -0700 Subject: [PATCH 23/26] IECoreArnold::RendererImplemention : Remove duplicate AiEnd - the destructor for the writable UniverseBlock will always call it anyway --- .../IECoreArnold/src/IECoreArnold/RendererImplementation.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp index 4af9ea9e44..a8ef8b9082 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp @@ -158,10 +158,6 @@ void IECoreArnold::RendererImplementation::constructCommon( Mode mode ) IECoreArnold::RendererImplementation::~RendererImplementation() { - if( m_mode != Procedural ) - { - AiEnd(); - } } //////////////////////////////////////////////////////////////////////// From eeb3f92b63f29d4c67f52a0456598413b94a032e Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Wed, 20 Sep 2017 15:34:28 -0700 Subject: [PATCH 24/26] IECoreArnold::ParameterAlgo : Support V3i and V2i parameters by casting to float --- .../src/IECoreArnold/ParameterAlgo.cpp | 28 +++++++++++++++++-- .../test/IECoreArnold/ParameterAlgoTest.py | 13 +++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp b/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp index 73a7e4c30b..f1a90e0f61 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/ParameterAlgo.cpp @@ -149,14 +149,28 @@ void setParameterInternal( AtNode *node, AtString name, int parameterType, bool } break; case AI_TYPE_VECTOR2 : - if( const V2fData *data = dataCast( name, value ) ) + if( const V2iData *data = runTimeCast( value ) ) + { + // Accept a V2i as an alternate since Arnold has + // no integer vector type to store these in. + const Imath::V2i &v = data->readable(); + AiNodeSetVec2( node, name, v.x, v.y ); + } + else if( const V2fData *data = dataCast( name, value ) ) { const Imath::V2f &v = data->readable(); AiNodeSetVec2( node, name, v.x, v.y ); } break; case AI_TYPE_VECTOR : - if( const V3fData *data = dataCast( name, value ) ) + if( const V3iData *data = runTimeCast( value ) ) + { + // Accept a V3i as an alternate since Arnold has + // no integer vector type to store these in. + const Imath::V3i &v = data->readable(); + AiNodeSetVec( node, name, v.x, v.y, v.z ); + } + else if( const V3fData *data = dataCast( name, value ) ) { const Imath::V3f &v = data->readable(); AiNodeSetVec( node, name, v.x, v.y, v.z ); @@ -431,7 +445,12 @@ int parameterType( IECore::TypeId dataType, bool &array ) case BoolDataTypeId : array = false; return AI_TYPE_BOOLEAN; + case V2fDataTypeId : + case V2iDataTypeId : + array = false; + return AI_TYPE_VECTOR2; case V3fDataTypeId : + case V3iDataTypeId : array = false; return AI_TYPE_VECTOR; case M44fDataTypeId : @@ -456,7 +475,12 @@ int parameterType( IECore::TypeId dataType, bool &array ) case BoolVectorDataTypeId : array = true; return AI_TYPE_BOOLEAN; + case V2fVectorDataTypeId : + case V2iVectorDataTypeId : + array = true; + return AI_TYPE_VECTOR2; case V3fVectorDataTypeId : + case V3iVectorDataTypeId : array = true; return AI_TYPE_VECTOR; case M44fVectorDataTypeId : diff --git a/contrib/IECoreArnold/test/IECoreArnold/ParameterAlgoTest.py b/contrib/IECoreArnold/test/IECoreArnold/ParameterAlgoTest.py index ba4b570128..9f9be1b5a3 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/ParameterAlgoTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/ParameterAlgoTest.py @@ -109,5 +109,18 @@ def testStringArray( self ) : self.assertEqual( arnold.AiArrayGetStr( a, 0 ), "a" ) self.assertEqual( arnold.AiArrayGetStr( a, 1 ), "b" ) + def testVectorIntData( self ) : + + with IECoreArnold.UniverseBlock( writable = True ) : + + n = arnold.AiNode( "standard_surface" ) + + IECoreArnold.ParameterAlgo.setParameter( n, "customV2i", IECore.V2iData( IECore.V2i( 3, 4 ) ) ) + self.assertEqual( arnold.AiNodeGetVec2( n, "customV2i" ), arnold.AtVector2( 3, 4 ) ) + + IECoreArnold.ParameterAlgo.setParameter( n, "customV3i", IECore.V3iData( IECore.V3i( 3, 4, 5 ) ) ) + self.assertEqual( arnold.AiNodeGetVec( n, "customV3i" ), arnold.AtVector( 3, 4, 5 ) ) + + if __name__ == "__main__": unittest.main() From 5ad3e9e49a06e826d412e0fced37fd5ab0d0e24b Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Thu, 21 Sep 2017 18:52:29 -0700 Subject: [PATCH 25/26] ICoreArnold : Without any options for setting log verbosity, default to no output In Gaffer::IECoreArnoldPreview or Cortex10, there are options to set verbosity Currently, without this, we would dump a huge pile of output every time you made a UniverseBlock just to query metadata or something. --- .../IECoreArnold/src/IECoreArnold/RendererImplementation.cpp | 3 --- contrib/IECoreArnold/src/IECoreArnold/UniverseBlock.cpp | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp index a8ef8b9082..69bfeb9b16 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/RendererImplementation.cpp @@ -145,9 +145,6 @@ void IECoreArnold::RendererImplementation::constructCommon( Mode mode ) m_universe = boost::shared_ptr( new UniverseBlock( /* writable = */ true ) ); m_instancingConverter = new InstancingConverter; - /// \todo Control with an option - AiMsgSetConsoleFlags( AI_LOG_ALL ); - // create a generic filter we can use for all displays m_defaultFilter = AiNode( "gaussian_filter" ); AiNodeSetStr( m_defaultFilter, "name", "ieCoreArnold:defaultFilter" ); diff --git a/contrib/IECoreArnold/src/IECoreArnold/UniverseBlock.cpp b/contrib/IECoreArnold/src/IECoreArnold/UniverseBlock.cpp index 8a7abf59f6..e5c404887c 100644 --- a/contrib/IECoreArnold/src/IECoreArnold/UniverseBlock.cpp +++ b/contrib/IECoreArnold/src/IECoreArnold/UniverseBlock.cpp @@ -80,6 +80,10 @@ void loadMetadata( const std::string &pluginPaths ) void begin() { AiBegin(); + // Default to logging errors / warnings only - we may not even be using this universe block to perform a render, + // we might just be loading some shader metadata or something, so we don't want to be dumping lots of + // unnecessary output + AiMsgSetConsoleFlags( AI_LOG_ERRORS | AI_LOG_WARNINGS ); const char *pluginPaths = getenv( "ARNOLD_PLUGIN_PATH" ); if( pluginPaths ) From 96b708e5094b502a901d07a70ed2a30746d8ec91 Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Thu, 21 Sep 2017 18:54:03 -0700 Subject: [PATCH 26/26] IECoreArnold::AutomaticInstancingTest : Force on console output so we can check it ( There is no way to do this through Cortex 9 at the moment, so we use Arnold's python API ) --- .../IECoreArnold/test/IECoreArnold/AutomaticInstancingTest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/IECoreArnold/test/IECoreArnold/AutomaticInstancingTest.py b/contrib/IECoreArnold/test/IECoreArnold/AutomaticInstancingTest.py index c68d92908d..24d419550b 100644 --- a/contrib/IECoreArnold/test/IECoreArnold/AutomaticInstancingTest.py +++ b/contrib/IECoreArnold/test/IECoreArnold/AutomaticInstancingTest.py @@ -143,11 +143,11 @@ def arnoldMessageCallback( logMask, severity, msg, tabs ) : self.__arnoldMessages.append( msg ) r = IECoreArnold.Renderer() - r.display( "test", "driver_null", "rgba", {} ) messageCallback = arnold.AtMsgCallBack( arnoldMessageCallback ) arnold.AiMsgSetCallback( messageCallback ) self.__arnoldMessages = [] + arnold.AiMsgSetConsoleFlags( arnold.AI_LOG_ALL ) with IECore.WorldBlock( r ) :