diff --git a/Changes b/Changes index 1afa9a3aac..a58b74bce7 100644 --- a/Changes +++ b/Changes @@ -1,7 +1,10 @@ 10.5.x.x (relative to 10.5.13.0) ======== +Features +-------- +- IECoreUSD::ShaderAlgo : Round-trip ColorSpace data that is stored on USD Attributes by storing an additional parameter with the `_colorspace` suffix. 10.5.13.0 (relative to 10.5.12.0) ========= diff --git a/contrib/IECoreUSD/src/IECoreUSD/ShaderAlgo.cpp b/contrib/IECoreUSD/src/IECoreUSD/ShaderAlgo.cpp index 9f2891ead9..622f54b311 100644 --- a/contrib/IECoreUSD/src/IECoreUSD/ShaderAlgo.cpp +++ b/contrib/IECoreUSD/src/IECoreUSD/ShaderAlgo.cpp @@ -244,6 +244,16 @@ IECore::InternedString readShaderNetworkWalk( const pxr::SdfPath &anchorPath, co if( IECore::DataPtr d = IECoreUSD::DataAlgo::fromUSD( pxr::UsdAttribute( valueAttribute ) ) ) { parameters[fromUSDParameterName( i.GetBaseName() )] = d; + // If there's colorspace data on the parameter, we can store this as + // `_colorspace`, this is how MaterialX stores colorspace on + // generated OSL nodes so we match here the same behaviour. + if( pxr::UsdAttribute( valueAttribute ).HasColorSpace() ) + { + const IECore::InternedString colorSpace = pxr::UsdAttribute( valueAttribute ).GetColorSpace().GetString(); + const IECore::InternedString paramName = + ( boost::format( "%s_colorspace" ) % fromUSDParameterName( i.GetBaseName() ).string() ).str(); + parameters[paramName] = colorSpace; + } } } @@ -334,6 +344,13 @@ void writeShaderParameterValues( const IECoreScene::Shader *shader, pxr::UsdShad continue; } + // Skip colorspace parameters, these will be set using SetColorSpace() on the + // USD attribue that it corresponds to. + if( boost::ends_with( p.first.string(), "_colorspace" ) ) + { + continue; + } + const pxr::TfToken usdParameterName = toUSDParameterName( p.first ); pxr::UsdShadeInput input = usdShader.GetInput( usdParameterName ); if( !input ) @@ -364,6 +381,17 @@ void writeShaderParameterValues( const IECoreScene::Shader *shader, pxr::UsdShad } } input.Set( IECoreUSD::DataAlgo::toUSD( p.second.get() ) ); + + // Make sure to set any colorspace parameters onto the attribute + // if any exist. + auto it = shader->parameters().find( ( boost::format( "%s_colorspace" ) % p.first.string() ).str() ); + if( it != shader->parameters().end() ) + { + if( auto *s = IECore::runTimeCast( it->second.get() ) ) + { + pxr::UsdAttribute( input ).SetColorSpace( pxr::TfToken( s->readable().string() ) ); + } + } } if( shader->blindData()->readable().size() ) diff --git a/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py b/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py index c9a52115cf..fc5c338897 100644 --- a/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py +++ b/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py @@ -2877,6 +2877,7 @@ def testShaders( self ) : texture = IECoreScene.Shader( "texture", "ai:shader" ) texture.parameters["filename"] = IECore.StringData( "sometexture.tx" ) + texture.parameters["filename_colorspace"] = IECore.InternedStringData( "ACEScg" ) oneShaderNetwork = IECoreScene.ShaderNetwork() oneShaderNetwork.addShader( "foo", surface ) @@ -3064,6 +3065,8 @@ def testShaders( self ) : textureUsd = pxr.UsdShade.Shader( add1Source[0].GetPrim() ) self.assertEqual( textureUsd.GetShaderId(), "arnold:texture" ) self.assertEqual( textureUsd.GetInput( "filename" ).Get(), "sometexture.tx" ) + self.assertEqual( add1Source[0].GetPrim().GetAttribute( "inputs:filename" ).GetColorSpace(), "ACEScg" ) + self.assertIsNone( textureUsd.GetInput( "filename_colorspace" ) ) # Read via SceneInterface, and check that we've round-tripped successfully. @@ -3340,6 +3343,15 @@ def testTextureParameters( self ) : os.path.normcase( os.path.normpath( network.getShader( "udimTexture" ).parameters["file"].value ) ), os.path.normcase( os.path.normpath( "/full/path/to/myTexture..tx" ) ) ) + self.assertEqual( + network.getShader( "relativeTexture" ).parameters["file_colorspace"].value, "ACEScg" + ) + self.assertEqual( + network.getShader( "relativeUDIMTexture" ).parameters["file_colorspace"].value, "lin_rec709_scene" + ) + self.assertEqual( + network.getShader( "udimTexture" ).parameters["file_colorspace"].value, "srgb_rec709_scene" + ) def testExposedShaderInput( self ) : diff --git a/contrib/IECoreUSD/test/IECoreUSD/data/textureParameters.usda b/contrib/IECoreUSD/test/IECoreUSD/data/textureParameters.usda index 6ddcb1d561..9f5e35a4a4 100644 --- a/contrib/IECoreUSD/test/IECoreUSD/data/textureParameters.usda +++ b/contrib/IECoreUSD/test/IECoreUSD/data/textureParameters.usda @@ -29,14 +29,18 @@ def "model" def Shader "relativeTexture" { uniform token info:id = "UsdUVTexture" - asset inputs:file = @../myTexture.tx@ + asset inputs:file = @../myTexture.tx@ ( + colorSpace = "ACEScg" + ) vector3f outputs:rgb } def Shader "relativeUDIMTexture" { uniform token info:id = "UsdUVTexture" - asset inputs:file = @../myTexture..tx@ + asset inputs:file = @../myTexture..tx@ ( + colorSpace = "lin_rec709_scene" + ) vector3f outputs:rgb } @@ -44,7 +48,9 @@ def "model" def Shader "udimTexture" { uniform token info:id = "UsdUVTexture" - asset inputs:file = @/full/path/to/myTexture..tx@ + asset inputs:file = @/full/path/to/myTexture..tx@ ( + colorSpace = "srgb_rec709_scene" + ) float outputs:r } }