diff --git a/Changes b/Changes index 0da2a88fe5..2007db8c94 100644 --- a/Changes +++ b/Changes @@ -1,8 +1,18 @@ -10.5.x.x (relative to 10.5.3.0) +10.5.x.x (relative to 10.5.4.0) ======== +10.5.4.0 (relative to 10.5.3.0) +======== + +Improvements +------------ + +- ShaderNetwork : Added support for InternedStringData attributes in string parameter substitutions. +- MurmurHash : Added specialisation for `std::hash`, among other things allowing the use of MurmurHash as a key in `unordered_map`. +- CompoundObject : Defaulted template argument to `Object` in `member()` methods. + 10.5.3.0 (relative to 10.5.2.1) ======== diff --git a/SConstruct b/SConstruct index d60055020c..e71936c911 100644 --- a/SConstruct +++ b/SConstruct @@ -56,7 +56,7 @@ SConsignFile() ieCoreMilestoneVersion = 10 # for announcing major milestones - may contain all of the below ieCoreMajorVersion = 5 # backwards-incompatible changes -ieCoreMinorVersion = 3 # new backwards-compatible features +ieCoreMinorVersion = 4 # new backwards-compatible features ieCorePatchVersion = 0 # bug fixes ieCoreVersionSuffix = "" # used for alpha/beta releases. Example: "a1", "b2", etc. @@ -1226,7 +1226,7 @@ else: "/Fd${TARGET}.pdb", ], ) - + # Reorder build commands so that `/external:I` includes come after `/I` includes. # Otherwise we'll pick up the Gaffer includes from the build directory, and not # the ones in the source tree. diff --git a/include/IECore/CompoundObject.h b/include/IECore/CompoundObject.h index ce92c68246..2f5ef137ed 100644 --- a/include/IECore/CompoundObject.h +++ b/include/IECore/CompoundObject.h @@ -64,9 +64,9 @@ class IECORE_API CompoundObject : public Object /// or doesn't match the type specified as the template argument, behavior /// is defined by the throwExceptions parameter. When this parameter is true a descriptive /// Exception is thrown, and when false 0 is returned. - template + template T *member( const InternedString &name, bool throwExceptions = false ); - template + template const T *member( const InternedString &name, bool throwExceptions = false ) const; /// A Convenience function to find an object in members(). @@ -74,7 +74,7 @@ class IECORE_API CompoundObject : public Object /// with the type's object factory create method. If false, or the named entry does not match the /// type specified as the template argument, behavior is defined by the throwExceptions parameter. /// When this parameter is true a descriptive Exception is thrown, and when false 0 is returned. - template + template T *member( const InternedString &name, bool throwExceptions, bool createIfMissing ); /// Returns an instance of CompoundObject which can be shared by everyone - for instance a procedural diff --git a/src/IECoreMaya/ParameterisedHolderModificationCmd.cpp b/src/IECoreMaya/ParameterisedHolderModificationCmd.cpp index ee834fb079..df839e1cdb 100644 --- a/src/IECoreMaya/ParameterisedHolderModificationCmd.cpp +++ b/src/IECoreMaya/ParameterisedHolderModificationCmd.cpp @@ -323,7 +323,7 @@ void ParameterisedHolderModificationCmd::storeParametersWithNewValues( const IEC { childParameterPath = it->first; } - storeParametersWithNewValues( it->second.get(), newCompound->member( it->first ), childParameterPath ); + storeParametersWithNewValues( it->second.get(), newCompound->member( it->first ), childParameterPath ); } const CompoundObject::ObjectMap &newChildren = static_cast( newValue )->members(); diff --git a/src/IECoreNuke/ParameterisedHolder.cpp b/src/IECoreNuke/ParameterisedHolder.cpp index ce4373fa7f..1321b9798d 100644 --- a/src/IECoreNuke/ParameterisedHolder.cpp +++ b/src/IECoreNuke/ParameterisedHolder.cpp @@ -188,7 +188,7 @@ int ParameterisedHolder::knob_changed( DD::Image::Knob *knob ) ParameterisedInterface *parameterisedInterface = dynamic_cast( g_getParameterisedResult.get() ); // apply current state ConstCompoundObjectPtr classSpecifier = runTimeCast( m_classSpecifierKnob->getValue() ); - ConstObjectPtr handlerState = classSpecifier->member( "handlerState" ); + ConstObjectPtr handlerState = classSpecifier->member( "handlerState" ); if( handlerState ) { m_parameterHandler->setState( parameterisedInterface->parameters(), handlerState.get() ); @@ -525,7 +525,7 @@ void ParameterisedHolder::updateParameterised( bool reload ) // apply the previously stored handler state ConstCompoundObjectPtr classSpecifier = runTimeCast( m_classSpecifierKnob->getValue() ); - ConstObjectPtr handlerState = classSpecifier->member( "handlerState" ); + ConstObjectPtr handlerState = classSpecifier->member( "handlerState" ); if( handlerState ) { m_parameterHandler->setState( parameterisedInterface->parameters(), handlerState.get() ); diff --git a/src/IECoreScene/ShaderNetwork.cpp b/src/IECoreScene/ShaderNetwork.cpp index 0cd18d898a..eb98abc39f 100644 --- a/src/IECoreScene/ShaderNetwork.cpp +++ b/src/IECoreScene/ShaderNetwork.cpp @@ -66,10 +66,14 @@ struct ReplaceFunctor std::string operator()( const boost::smatch & match ) { // Search for attribute matching token - const StringData *sourceAttribute = m_attributes->member( match[1].str() ); - if( sourceAttribute ) + const Object *sourceAttribute = m_attributes->member( match[1].str() ); + if( auto stringData = runTimeCast( sourceAttribute ) ) { - return sourceAttribute->readable(); + return stringData->readable(); + } + else if( auto internedStringData = runTimeCast( sourceAttribute ) ) + { + return internedStringData->readable().string(); } else { @@ -86,7 +90,7 @@ boost::regex attributeRegex() // Extract ATTR_NAME from the pattern // Only match if the angle brackets haven't been escaped with a backslash static boost::regex r( "(?]*[^\\\\>])>" ); - return r; + return r; } bool stringFindSubstitutions( const std::string &target, std::unordered_set< InternedString > &requestedAttributes ) @@ -428,10 +432,14 @@ class ShaderNetwork::Implementation update(); for( const auto &a : m_neededSubstitutions ) { - const StringData *sourceAttribute = attributes->member( a ); - if( sourceAttribute ) + const Object *sourceAttribute = attributes->member( a ); + if( auto stringData = runTimeCast( sourceAttribute ) ) + { + h.append( stringData->readable() ); + } + else if( auto internedStringData = runTimeCast( sourceAttribute ) ) { - h.append( sourceAttribute->readable() ); + h.append( internedStringData->readable().string() ); } else { @@ -737,7 +745,7 @@ class ShaderNetwork::Implementation } } - m_parmsNeedingSubstitution[ node.handle ] = parmsNeedingSub; + m_parmsNeedingSubstitution[ node.handle ] = parmsNeedingSub; } m_hash.append( m_output.shader ); diff --git a/test/IECoreScene/ShaderNetworkTest.py b/test/IECoreScene/ShaderNetworkTest.py index c255bd1403..1f7f5373e6 100644 --- a/test/IECoreScene/ShaderNetworkTest.py +++ b/test/IECoreScene/ShaderNetworkTest.py @@ -513,15 +513,17 @@ def testUniqueHandles( self ) : { "test" } | { "test{0}".format( x ) for x in range( 1, 20 ) } ) - def testSubstitutions( self ): - def runSubstitutionTest( shader, attributes ): - n = IECoreScene.ShaderNetwork( shaders = { "s" : s } ) - a = IECore.CompoundObject( attributes ) - h = IECore.MurmurHash() - n.hashSubstitutions( a, h ) - nSubst = n.copy() - nSubst.applySubstitutions( a ) - return ( h, nSubst.getShader("s") ) + def __hashAndSubstitution( self, shader, attributes ) : + + n = IECoreScene.ShaderNetwork( shaders = { "s" : shader } ) + a = IECore.CompoundObject( attributes ) + h = IECore.MurmurHash() + n.hashSubstitutions( a, h ) + nSubst = n.copy() + nSubst.applySubstitutions( a ) + return ( h, nSubst.getShader( "s" ) ) + + def testSubstitutions( self ) : s = IECoreScene.Shader( "test", "surface",IECore.CompoundData( { "a" : IECore.StringData( "foo" ), @@ -529,7 +531,7 @@ def runSubstitutionTest( shader, attributes ): "c" : IECore.StringVectorData( [ "foo", "bar" ] ), } ) ) - ( h, sSubst ) = runSubstitutionTest( s, { "unused" : IECore.StringData( "blah" ) } ) + ( h, sSubst ) = self.__hashAndSubstitution( s, { "unused" : IECore.StringData( "blah" ) } ) self.assertEqual( h, IECore.MurmurHash() ) self.assertEqual( s, sSubst ) @@ -538,7 +540,7 @@ def runSubstitutionTest( shader, attributes ): "b" : IECore.FloatData( 42.42 ), "c" : IECore.StringVectorData( [ "", "pre", "post", " " ] ), } ) ) - ( h, sSubst ) = runSubstitutionTest( s, { "unused" : IECore.StringData( "blah" ) } ) + ( h, sSubst ) = self.__hashAndSubstitution( s, { "unused" : IECore.StringData( "blah" ) } ) # Now that we've got substitutions, the hash should be non-default self.assertNotEqual( h, IECore.MurmurHash() ) @@ -550,12 +552,12 @@ def runSubstitutionTest( shader, attributes ): self.assertEqual( sSubst.parameters["c"][2], "post" ) self.assertEqual( sSubst.parameters["c"][3], " " ) - ( h2, sSubst2 ) = runSubstitutionTest( s, { "unused" : IECore.StringData( "blah2" ) } ) + ( h2, sSubst2 ) = self.__hashAndSubstitution( s, { "unused" : IECore.StringData( "blah2" ) } ) # The attribute being changed has no impact self.assertEqual( h, h2 ) self.assertEqual( sSubst, sSubst2 ) - ( h3, sSubst3 ) = runSubstitutionTest( s, { "fred" : IECore.StringData( "CAT" ) } ) + ( h3, sSubst3 ) = self.__hashAndSubstitution( s, { "fred" : IECore.StringData( "CAT" ) } ) self.assertNotEqual( h, h3 ) self.assertNotEqual( s, sSubst3 ) self.assertEqual( sSubst3.parameters["a"].value, "preCATpost" ) @@ -564,7 +566,7 @@ def runSubstitutionTest( shader, attributes ): self.assertEqual( sSubst3.parameters["c"][2], "CATpost" ) self.assertEqual( sSubst3.parameters["c"][3], " CAT" ) - ( h4, sSubst4 ) = runSubstitutionTest( s, { "fred" : IECore.StringData( "FISH" ) } ) + ( h4, sSubst4 ) = self.__hashAndSubstitution( s, { "fred" : IECore.StringData( "FISH" ) } ) self.assertNotEqual( h3, h4 ) self.assertEqual( sSubst4.parameters["c"][2], "FISHpost" ) @@ -573,7 +575,7 @@ def runSubstitutionTest( shader, attributes ): "bob" : IECore.StringData( "CAT" ), "carol" : IECore.StringData( "BIRD" ) } - ( h5, sSubst5 ) = runSubstitutionTest( s, allAttributes ) + ( h5, sSubst5 ) = self.__hashAndSubstitution( s, allAttributes ) self.assertNotEqual( h4, h5 ) self.assertEqual( sSubst5.parameters["a"].value, "preFISHpost" ) self.assertEqual( sSubst5.parameters["c"][0], "CAT" ) @@ -587,8 +589,8 @@ def runSubstitutionTest( shader, attributes ): "b" : IECore.FloatData( 42.42 ), "c" : IECore.StringVectorData( [ r"\", r"\", r"" ] ), } ) ) - ( h6, sSubst6 ) = runSubstitutionTest( s, {} ) - ( h7, sSubst7 ) = runSubstitutionTest( s, allAttributes ) + ( h6, sSubst6 ) = self.__hashAndSubstitution( s, {} ) + ( h7, sSubst7 ) = self.__hashAndSubstitution( s, allAttributes ) self.assertEqual( h6, h7 ) self.assertEqual( sSubst6, sSubst7 ) self.assertEqual( sSubst6.parameters["a"].value, "prepost" ) @@ -596,5 +598,23 @@ def runSubstitutionTest( shader, attributes ): self.assertEqual( sSubst6.parameters["c"][1], "" ) self.assertEqual( sSubst6.parameters["c"][2], "" ) + def testInternedStringSubstitutions( self ) : + + shader = IECoreScene.Shader( + "test", "surface", + IECore.CompoundData( { + "a" : IECore.StringData( "" ), + } ) + ) + + hash1, substitutedShader = self.__hashAndSubstitution( shader, { "internedString" : IECore.InternedStringData( "a" ) } ) + self.assertNotEqual( hash1, IECore.MurmurHash() ) + self.assertEqual( substitutedShader.parameters["a"], IECore.StringData( "a" ) ) + + hash2, substitutedShader = self.__hashAndSubstitution( shader, { "internedString" : IECore.InternedStringData( "b" ) } ) + self.assertNotEqual( hash2, IECore.MurmurHash() ) + self.assertNotEqual( hash2, hash1 ) + self.assertEqual( substitutedShader.parameters["a"], IECore.StringData( "b" ) ) + if __name__ == "__main__": unittest.main()