From 4df9a975902e07e8e2d334df1e7a976c7ba78ac7 Mon Sep 17 00:00:00 2001 From: John Haddon Date: Wed, 2 Dec 2020 14:06:42 +0000 Subject: [PATCH] ValuePlugSerialiser : Fix serialisation of complex CompoundObjects --- .../GafferSceneTest/CustomAttributesTest.py | 22 +++++++++++++ python/GafferTest/TypedObjectPlugTest.py | 17 ++++++++-- src/GafferBindings/ValuePlugBinding.cpp | 33 +++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/python/GafferSceneTest/CustomAttributesTest.py b/python/GafferSceneTest/CustomAttributesTest.py index c870792d9b0..2f05f6d7f7f 100644 --- a/python/GafferSceneTest/CustomAttributesTest.py +++ b/python/GafferSceneTest/CustomAttributesTest.py @@ -370,6 +370,28 @@ def testLoadExtraAttributesFrom0_58( self ) : } ) ) + def testAssignShader( self ) : + + script = Gaffer.ScriptNode() + + script["sphere"] = GafferScene.Sphere() + script["sphereFilter"] = GafferScene.PathFilter() + script["sphereFilter"]["paths"].setValue( IECore.StringVectorData( [ "/sphere" ] ) ) + + attributes = IECore.CompoundObject( { + "ai:surface" : IECoreScene.ShaderNetwork( { "output" : IECoreScene.Shader( "flat" ) }, output = "output" ) + } ) + + script["attributes"] = GafferScene.CustomAttributes() + script["attributes"]["in"].setInput( script["sphere"]["out"] ) + script["attributes"]["filter"].setInput( script["sphereFilter"]["out"] ) + script["attributes"]["extraAttributes"].setValue( attributes ) + self.assertEqual( script["attributes"]["out"].attributes( "/sphere" ), attributes ) + + script2 = Gaffer.ScriptNode() + script2.execute( script.serialise() ) + self.assertEqual( script2["attributes"]["out"].attributes( "/sphere" ), attributes ) + def testDirtyPropagation( self ) : attributes = GafferScene.CustomAttributes() diff --git a/python/GafferTest/TypedObjectPlugTest.py b/python/GafferTest/TypedObjectPlugTest.py index 85e9b1ab6b2..9f19d6c61a8 100644 --- a/python/GafferTest/TypedObjectPlugTest.py +++ b/python/GafferTest/TypedObjectPlugTest.py @@ -239,6 +239,13 @@ def testSerialisationWithoutRepr( self ) : ) ) + v3 = IECore.CompoundObject( { + "a" : IECore.IntData( 10 ), + "b" : v1, + "c" : v2, + "d" : IECore.StringData( "test" ), + } ) + with self.assertRaises( Exception ) : eval( repr( v1 ) ) @@ -246,23 +253,29 @@ def testSerialisationWithoutRepr( self ) : s["n"] = Gaffer.Node() s["n"]["user"]["p1"] = Gaffer.ObjectPlug( defaultValue = v1, flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) s["n"]["user"]["p2"] = Gaffer.ObjectPlug( defaultValue = v2, flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) + s["n"]["user"]["p3"] = Gaffer.ObjectPlug( defaultValue = v3, flags = Gaffer.Plug.Flags.Default | Gaffer.Plug.Flags.Dynamic ) s2 = Gaffer.ScriptNode() s2.execute( s.serialise() ) self.assertEqual( s2["n"]["user"]["p1"].defaultValue(), v1 ) self.assertEqual( s2["n"]["user"]["p2"].defaultValue(), v2 ) + self.assertEqual( s2["n"]["user"]["p3"].defaultValue(), v3 ) self.assertEqual( s2["n"]["user"]["p1"].getValue(), v1 ) self.assertEqual( s2["n"]["user"]["p2"].getValue(), v2 ) + self.assertEqual( s2["n"]["user"]["p3"].getValue(), v3 ) s["n"]["user"]["p1"].setValue( v2 ) - s["n"]["user"]["p2"].setValue( v1 ) + s["n"]["user"]["p2"].setValue( v3 ) + s["n"]["user"]["p3"].setValue( v1 ) s2 = Gaffer.ScriptNode() s2.execute( s.serialise() ) self.assertEqual( s2["n"]["user"]["p1"].defaultValue(), v1 ) self.assertEqual( s2["n"]["user"]["p2"].defaultValue(), v2 ) + self.assertEqual( s2["n"]["user"]["p3"].defaultValue(), v3 ) self.assertEqual( s2["n"]["user"]["p1"].getValue(), v2 ) - self.assertEqual( s2["n"]["user"]["p2"].getValue(), v1 ) + self.assertEqual( s2["n"]["user"]["p2"].getValue(), v3 ) + self.assertEqual( s2["n"]["user"]["p3"].getValue(), v1 ) def testConnectCompoundDataToCompoundObject( self ) : diff --git a/src/GafferBindings/ValuePlugBinding.cpp b/src/GafferBindings/ValuePlugBinding.cpp index cf8c842dc22..8f7e3463751 100644 --- a/src/GafferBindings/ValuePlugBinding.cpp +++ b/src/GafferBindings/ValuePlugBinding.cpp @@ -132,6 +132,28 @@ std::string valueSerialisationWalk( const Gaffer::ValuePlug *plug, const std::st return identifier + ".setValue( " + ValuePlugSerialiser::valueRepr( pythonValue ) + " )\n"; } +std::string compoundObjectRepr( const IECore::CompoundObject *o ) +{ + std::string items; + for( const auto &e : o->members() ) + { + if( items.size() ) + { + items += ", "; + } + items += "'" + e.first.string() + "' : " + ValuePlugSerialiser::valueRepr( object( e.second ) ); + } + + if( items.empty() ) + { + return "IECore.CompoundObject()"; + } + else + { + return "IECore.CompoundObject( { " + items + "} )"; + } +} + } // namespace std::string ValuePlugSerialiser::repr( const Gaffer::ValuePlug *plug, const std::string &extraArguments, const Serialisation *serialisation ) @@ -242,6 +264,17 @@ std::string ValuePlugSerialiser::postHierarchy( const Gaffer::GraphComponent *gr std::string ValuePlugSerialiser::valueRepr( const boost::python::object &value ) { + // CompoundObject may contain objects which can only be serialised + // via `objectToBase64()`, so we need to override the standard Cortex + // serialiser. + + boost::python::extract compoundObjectExtractor( value ); + if( compoundObjectExtractor.check() ) + { + auto co = compoundObjectExtractor(); + return compoundObjectRepr( co.get() ); + } + // We use IECore.repr() because it correctly prefixes the imath // types with the module name, and also works around problems // when round-tripping empty Box2fs.