From b89c4af33adee85b1aa5bdfe438c04644e0f169e Mon Sep 17 00:00:00 2001 From: Alex Fuller Date: Mon, 20 Jan 2025 17:09:00 +1100 Subject: [PATCH] IECoreScene : Spline ramp conversions for Arnold and Renderman native shaders. --- include/IECoreScene/ShaderNetworkAlgo.h | 4 +- src/IECoreScene/ShaderNetworkAlgo.cpp | 130 +++++++++++++++--- .../bindings/ShaderNetworkAlgoBinding.cpp | 8 +- test/IECoreScene/ShaderNetworkAlgoTest.py | 67 +++++++++ 4 files changed, 184 insertions(+), 25 deletions(-) diff --git a/include/IECoreScene/ShaderNetworkAlgo.h b/include/IECoreScene/ShaderNetworkAlgo.h index e7e7ef445c..3fc2f9a030 100644 --- a/include/IECoreScene/ShaderNetworkAlgo.h +++ b/include/IECoreScene/ShaderNetworkAlgo.h @@ -149,10 +149,10 @@ IECORESCENE_API void expandSplines( ShaderNetwork *network, std::string targetPr /// \deprecated: Use collapseSplines on the whole network, which can handle input connections -IECORESCENE_API IECore::ConstCompoundDataPtr collapseSplineParameters( const IECore::ConstCompoundDataPtr& parametersData ); +IECORESCENE_API IECore::ConstCompoundDataPtr collapseSplineParameters( const IECore::ConstCompoundDataPtr& parametersData, const std::string shaderType = "", const std::string shaderName = "" ); /// \deprecated: Use expandSplines on the whole network, which can handle input connections -IECORESCENE_API IECore::ConstCompoundDataPtr expandSplineParameters( const IECore::ConstCompoundDataPtr& parametersData ); +IECORESCENE_API IECore::ConstCompoundDataPtr expandSplineParameters( const IECore::ConstCompoundDataPtr& parametersData, const std::string shaderType = "", const std::string shaderName = "" ); } // namespace ShaderNetworkAlgo diff --git a/src/IECoreScene/ShaderNetworkAlgo.cpp b/src/IECoreScene/ShaderNetworkAlgo.cpp index 51230dc994..2dffd0fd79 100644 --- a/src/IECoreScene/ShaderNetworkAlgo.cpp +++ b/src/IECoreScene/ShaderNetworkAlgo.cpp @@ -811,9 +811,13 @@ std::pair< size_t, size_t > getEndPointDuplication( const T &basis ) } template -void expandSpline( const InternedString &name, const Spline &spline, CompoundDataMap &newParameters ) +void expandSpline( const InternedString &name, const Spline &spline, CompoundDataMap &newParameters, const std::string shaderType = "", const std::string shaderName = "" ) { const char *basis = "catmull-rom"; + // For Renderman see https://rmanwiki-26.pixar.com/space/REN26/19661691/PxrRamp + const char *riBasis = "catmull-rom"; + // For Arnold see https://help.autodesk.com/view/ARNOL/ENU/?guid=arnold_user_guide_ac_texture_shaders_ac_texture_ramp_html + int aiBasisIdx = 2; if( spline.basis == Spline::Basis::bezier() ) { basis = "bezier"; @@ -821,16 +825,21 @@ void expandSpline( const InternedString &name, const Spline &spline, CompoundDat else if( spline.basis == Spline::Basis::bSpline() ) { basis = "bspline"; + riBasis = "bspline"; } else if( spline.basis == Spline::Basis::linear() ) { basis = "linear"; + riBasis = "linear"; + aiBasisIdx = 1; } else if( spline.basis == Spline::Basis::constant() ) { // Also, "To maintain consistency", "constant splines ignore the first and the two last // data values." basis = "constant"; + riBasis = "constant"; + aiBasisIdx = 0; } auto [ duplicateStartPoints, duplicateEndPoints ] = getEndPointDuplication( spline.basis ); @@ -865,9 +874,42 @@ void expandSpline( const InternedString &name, const Spline &spline, CompoundDat } } - newParameters[ name.string() + "Positions" ] = positionsData; - newParameters[ name.string() + "Values" ] = valuesData; - newParameters[ name.string() + "Basis" ] = new StringData( basis ); + if( boost::starts_with( shaderType, "ai:" ) && ( shaderName == "ramp_float" || shaderName == "ramp_rgb" ) ) + { + newParameters[ "position" ] = positionsData; + if constexpr ( std::is_same_v ) + { + newParameters[ "color" ] = valuesData; + } + else + { + newParameters[ "value" ] = valuesData; + } + std::vector interp; + interp.resize( spline.points.size() ); + std::fill( interp.begin(), interp.end(), aiBasisIdx ); + newParameters[ "interpolation" ] = new IntVectorData( interp ); + } + // Intentionally OR'd here as many Renderman shaders are OSL so search for the 'Pxr' prefix. + else if( boost::starts_with( shaderType, "ri:" ) || ( boost::starts_with( shaderName, "Pxr" ) ) ) + { + newParameters[ name.string() + "_Knots" ] = positionsData; + if constexpr ( std::is_same_v ) + { + newParameters[ name.string() + "_Colors" ] = valuesData; + } + else + { + newParameters[ name.string() + "_Floats" ] = valuesData; + } + newParameters[ name.string() + "_Interpolation" ] = new StringData( riBasis ); + } + else + { + newParameters[ name.string() + "Positions" ] = positionsData; + newParameters[ name.string() + "Values" ] = valuesData; + newParameters[ name.string() + "Basis" ] = new StringData( basis ); + } } template @@ -1039,7 +1081,7 @@ void ShaderNetworkAlgo::collapseSplines( ShaderNetwork *network, std::string tar } // For nodes which aren't spline adapters, we just need to deal with any parameters that are splines - ConstCompoundDataPtr collapsed = collapseSplineParameters( shader->parametersData() ); + ConstCompoundDataPtr collapsed = collapseSplineParameters( shader->parametersData(), shader->getType(), shader->getName()); if( collapsed != shader->parametersData() ) { // \todo - this const_cast is ugly, although safe because if the return from collapseSplineParameters @@ -1166,13 +1208,13 @@ void ShaderNetworkAlgo::expandSplines( ShaderNetwork *network, std::string targe { ensureParametersCopy( origParameters, newParametersData, newParameters ); newParameters->erase( name ); - expandSpline( name, colorSpline->readable(), *newParameters ); + expandSpline( name, colorSpline->readable(), *newParameters, s.second->getType(), s.second->getName() ); } else if( const SplineffData *floatSpline = runTimeCast( value.get() ) ) { ensureParametersCopy( origParameters, newParametersData, newParameters ); newParameters->erase( name ); - expandSpline( name, floatSpline->readable(), *newParameters ); + expandSpline( name, floatSpline->readable(), *newParameters, s.second->getType(), s.second->getName() ); } } @@ -1288,27 +1330,64 @@ void ShaderNetworkAlgo::expandSplines( ShaderNetwork *network, std::string targe } } -IECore::ConstCompoundDataPtr ShaderNetworkAlgo::collapseSplineParameters( const IECore::ConstCompoundDataPtr ¶metersData ) +IECore::ConstCompoundDataPtr ShaderNetworkAlgo::collapseSplineParameters( const IECore::ConstCompoundDataPtr ¶metersData, const std::string shaderType, const std::string shaderName ) { const CompoundDataMap ¶meters( parametersData->readable() ); CompoundDataPtr newParametersData; CompoundDataMap *newParameters = nullptr; + std::string basisStr = "Basis"; + std::string positionsStr = "Positions"; + std::string valuesStr = "Values"; + + if( boost::starts_with( shaderType, "ai:" ) && ( shaderName == "ramp_float" || shaderName == "ramp_rgb" ) ) + { + basisStr = "interpolation"; + positionsStr = "position"; + valuesStr = "value"; + } + else if( boost::starts_with( shaderType, "ri:" ) || boost::starts_with( shaderName, "Pxr" ) ) + { + basisStr = "_Interpolation"; + positionsStr = "_Knots"; + valuesStr = "_Floats"; + } + for( const auto &maybeBasis : parameters ) { - if( !boost::ends_with( maybeBasis.first.string(), "Basis" ) ) + if( !boost::ends_with( maybeBasis.first.string(), basisStr ) ) { continue; } - const StringData *basis = runTimeCast( maybeBasis.second.get() ); + StringData *basis = runTimeCast( maybeBasis.second.get() ); if( !basis ) { - continue; + const IntVectorData *intBasis = runTimeCast( maybeBasis.second.get() ); + if( !intBasis ) + { + continue; + } + // Do int to string conversion here, using the first value of the interpolation array + if( intBasis->readable().front() == 0 ) + { + basis = new StringData( "constant" ); + } + else if( intBasis->readable().front() == 1 ) + { + basis = new StringData( "linear" ); + } + else if( intBasis->readable().front() == 3 ) + { + basis = new StringData( "monotonecubic" ); + } + else + { + basis = new StringData( "catmull-rom" ); + } } - - std::string prefix = maybeBasis.first.string().substr( 0, maybeBasis.first.string().size() - 5 ); - IECore::InternedString positionsName = prefix + "Positions"; + std::string prefix = maybeBasis.first.string().substr( 0, maybeBasis.first.string().size() - basisStr.size() ); + IECore::InternedString positionsName = prefix + positionsStr; const auto positionsIter = parameters.find( positionsName ); const FloatVectorData *floatPositions = nullptr; @@ -1322,8 +1401,21 @@ IECore::ConstCompoundDataPtr ShaderNetworkAlgo::collapseSplineParameters( const continue; } - IECore::InternedString valuesName = prefix + "Values"; - const auto valuesIter = parameters.find( valuesName ); + IECore::InternedString valuesName = prefix + valuesStr; + auto valuesIter = parameters.find( valuesName ); + if( valuesIter == parameters.end() ) + { + if( boost::starts_with( shaderType, "ai:" ) ) + { + valuesName = prefix + "color"; + valuesIter = parameters.find( valuesName ); + } + else if( boost::starts_with( shaderType, "ri:" ) ) + { + valuesName = prefix + "_Colors"; + valuesIter = parameters.find( valuesName ); + } + } IECore::DataPtr foundSpline; if( valuesIter != parameters.end() ) @@ -1362,7 +1454,7 @@ IECore::ConstCompoundDataPtr ShaderNetworkAlgo::collapseSplineParameters( const } } -IECore::ConstCompoundDataPtr ShaderNetworkAlgo::expandSplineParameters( const IECore::ConstCompoundDataPtr ¶metersData ) +IECore::ConstCompoundDataPtr ShaderNetworkAlgo::expandSplineParameters( const IECore::ConstCompoundDataPtr ¶metersData, const std::string shaderType, const std::string shaderName ) { const CompoundDataMap ¶meters( parametersData->readable() ); @@ -1375,13 +1467,13 @@ IECore::ConstCompoundDataPtr ShaderNetworkAlgo::expandSplineParameters( const IE { ensureParametersCopy( parameters, newParametersData, newParameters ); newParameters->erase( i.first ); - expandSpline( i.first, colorSpline->readable(), *newParameters ); + expandSpline( i.first, colorSpline->readable(), *newParameters, shaderType, shaderName ); } else if( const SplineffData *floatSpline = runTimeCast( i.second.get() ) ) { ensureParametersCopy( parameters, newParametersData, newParameters ); newParameters->erase( i.first ); - expandSpline( i.first, floatSpline->readable(), *newParameters ); + expandSpline( i.first, floatSpline->readable(), *newParameters, shaderType, shaderName ); } } diff --git a/src/IECoreScene/bindings/ShaderNetworkAlgoBinding.cpp b/src/IECoreScene/bindings/ShaderNetworkAlgoBinding.cpp index c886c98a7d..6517679705 100644 --- a/src/IECoreScene/bindings/ShaderNetworkAlgoBinding.cpp +++ b/src/IECoreScene/bindings/ShaderNetworkAlgoBinding.cpp @@ -70,14 +70,14 @@ void convertOSLComponentConnectionsWrapper( ShaderNetwork *network, int oslVersi ShaderNetworkAlgo::convertOSLComponentConnections( network, oslVersion ); } -CompoundDataPtr collapseSplineParametersWrapper( CompoundDataPtr parameters ) +CompoundDataPtr collapseSplineParametersWrapper( CompoundDataPtr parameters, const std::string shaderType, const std::string shaderName ) { - return boost::const_pointer_cast< CompoundData >( ShaderNetworkAlgo::collapseSplineParameters( parameters ) ); + return boost::const_pointer_cast< CompoundData >( ShaderNetworkAlgo::collapseSplineParameters( parameters, shaderType, shaderName ) ); } -CompoundDataPtr expandSplineParametersWrapper( CompoundDataPtr parameters ) +CompoundDataPtr expandSplineParametersWrapper( CompoundDataPtr parameters, const std::string shaderType, const std::string shaderName ) { - return boost::const_pointer_cast< CompoundData >( ShaderNetworkAlgo::expandSplineParameters( parameters ) ); + return boost::const_pointer_cast< CompoundData >( ShaderNetworkAlgo::expandSplineParameters( parameters, shaderType, shaderName ) ); } std::string componentConnectionAdapterLabelWrapper() diff --git a/test/IECoreScene/ShaderNetworkAlgoTest.py b/test/IECoreScene/ShaderNetworkAlgoTest.py index 0ae5268a0f..c4cdc46d66 100644 --- a/test/IECoreScene/ShaderNetworkAlgoTest.py +++ b/test/IECoreScene/ShaderNetworkAlgoTest.py @@ -490,6 +490,73 @@ def testSplineConversion( self ): IECoreScene.ShaderNetworkAlgo.collapseSplines( shaderNetworkInvalid ) self.assertEqual( shaderNetworkInvalid, shaderNetworkInvalidOrig ) + def __splineConversionArnold( self, shaderName, valueName, valueType, parms ): + + shaderNetworkOrig = IECoreScene.ShaderNetwork( + shaders = { "test" : IECoreScene.Shader( shaderName, "ai:surface", parms ) }, + output = "test" + ) + shaderNetwork = shaderNetworkOrig.copy() + IECoreScene.ShaderNetworkAlgo.expandSplines( shaderNetwork ) + + parmsExpanded = shaderNetwork.outputShader().parameters + + self.assertEqual( type( parmsExpanded["interpolation"] ), IECore.IntVectorData ) + self.assertEqual( type( parmsExpanded["position"] ), IECore.FloatVectorData ) + self.assertEqual( type( parmsExpanded[valueName] ), valueType ) + + IECoreScene.ShaderNetworkAlgo.collapseSplines( shaderNetwork ) + + self.assertEqual( shaderNetwork, shaderNetworkOrig ) + + def testSplineConversionArnold( self ): + + parmsRgb = IECore.CompoundData() + parmsRgb["ramp"] = IECore.SplinefColor3fData( IECore.SplinefColor3f( IECore.CubicBasisf.catmullRom(), +( ( 0, imath.Color3f(1) ), ( 10, imath.Color3f(2) ), ( 20, imath.Color3f(0) ), ( 30, imath.Color3f(5) ), ( 40, imath.Color3f(2) ), ( 50, imath.Color3f(6) ) ) ) ) + + self.__splineConversionArnold( "ramp_rgb", "color", IECore.Color3fVectorData, parmsRgb ) + + parmsFloat = IECore.CompoundData() + parmsFloat["ramp"] = IECore.SplineffData( IECore.Splineff( IECore.CubicBasisf.constant(), + ( ( 0, 1 ), ( 0.2, 6 ), ( 0.3, 7 ) ) ) ) + + self.__splineConversionArnold( "ramp_float", "value", IECore.FloatVectorData, parmsFloat ) + + def __splineConversionRenderman( self, shaderType ): + + parms = IECore.CompoundData() + parms["colorRamp"] = IECore.SplinefColor3fData( IECore.SplinefColor3f( IECore.CubicBasisf.catmullRom(), +( ( 0, imath.Color3f(1) ), ( 10, imath.Color3f(2) ), ( 20, imath.Color3f(0) ), ( 30, imath.Color3f(5) ), ( 40, imath.Color3f(2) ), ( 50, imath.Color3f(6) ) ) ) ) + parms["floatRamp"] = IECore.SplineffData( IECore.Splineff( IECore.CubicBasisf.constant(), + ( ( 0, 1 ), ( 0.2, 6 ), ( 0.3, 7 ) ) ) ) + + shaderNetworkOrig = IECoreScene.ShaderNetwork( + shaders = { "test" : IECoreScene.Shader( "PxrSplineMap", shaderType, parms ) }, + output = "test" + ) + shaderNetwork = shaderNetworkOrig.copy() + IECoreScene.ShaderNetworkAlgo.expandSplines( shaderNetwork ) + + parmsExpanded = shaderNetwork.outputShader().parameters + + self.assertEqual( type( parmsExpanded["colorRamp_Interpolation"] ), IECore.StringData ) + self.assertEqual( type( parmsExpanded["colorRamp_Knots"] ), IECore.FloatVectorData ) + self.assertEqual( type( parmsExpanded["colorRamp_Colors"] ), IECore.Color3fVectorData ) + + self.assertEqual( type( parmsExpanded["floatRamp_Interpolation"] ), IECore.StringData ) + self.assertEqual( type( parmsExpanded["floatRamp_Knots"] ), IECore.FloatVectorData ) + self.assertEqual( type( parmsExpanded["floatRamp_Floats"] ), IECore.FloatVectorData ) + + IECoreScene.ShaderNetworkAlgo.collapseSplines( shaderNetwork ) + + self.assertEqual( shaderNetwork, shaderNetworkOrig ) + + def testSplineConversionRenderman( self ): + + self.__splineConversionRenderman( "osl:shader" ) + self.__splineConversionRenderman( "ri:surface" ) + def testSplineInputs( self ): fC3fcatmullRom = IECore.SplinefColor3fData( IECore.SplinefColor3f(