From 0ff346b8aadb2b83e7b69418fed458ab864c373d 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 | 175 +++++++++++++++--- .../bindings/ShaderNetworkAlgoBinding.cpp | 8 +- test/IECoreScene/ShaderNetworkAlgoTest.py | 67 +++++++ 4 files changed, 218 insertions(+), 36 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..5a89e78f51 100644 --- a/src/IECoreScene/ShaderNetworkAlgo.cpp +++ b/src/IECoreScene/ShaderNetworkAlgo.cpp @@ -811,9 +811,15 @@ 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; + const bool isArnold = boost::starts_with( shaderType, "ai:" ); + if( spline.basis == Spline::Basis::bezier() ) { basis = "bezier"; @@ -821,16 +827,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 ); @@ -843,7 +854,7 @@ void expandSpline( const InternedString &name, const Spline &spline, CompoundDat auto &values = valuesData->writable(); values.reserve( spline.points.size() + duplicateStartPoints + duplicateEndPoints ); - if( spline.points.size() ) + if( spline.points.size() && !isArnold ) { for( size_t i = 0; i < duplicateStartPoints; i++ ) { @@ -856,7 +867,7 @@ void expandSpline( const InternedString &name, const Spline &spline, CompoundDat positions.push_back( it->first ); values.push_back( it->second ); } - if( spline.points.size() ) + if( spline.points.size() && !isArnold ) { for( size_t i = 0; i < duplicateEndPoints; i++ ) { @@ -865,16 +876,50 @@ 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( isArnold && ( 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 IECore::DataPtr loadSpline( const StringData *basisData, const IECore::TypedData< std::vector< typename SplineData::ValueType::XType > > *positionsData, - const IECore::TypedData< std::vector< typename SplineData::ValueType::YType > > *valuesData + const IECore::TypedData< std::vector< typename SplineData::ValueType::YType > > *valuesData, + const bool unduplicatePoints = true ) { typename SplineData::Ptr resultData = new SplineData(); @@ -895,15 +940,21 @@ IECore::DataPtr loadSpline( else if( basis == "linear" ) { // Reverse the duplication we do when expanding splines - unduplicateStartPoints = 1; - unduplicateEndPoints = 1; + if( unduplicatePoints ) + { + unduplicateStartPoints = 1; + unduplicateEndPoints = 1; + } result.basis = SplineData::ValueType::Basis::linear(); } else if( basis == "constant" ) { // Reverse the duplication we do when expanding splines - unduplicateStartPoints = 1; - unduplicateEndPoints = 2; + if( unduplicatePoints ) + { + unduplicateStartPoints = 1; + unduplicateEndPoints = 2; + } result.basis = SplineData::ValueType::Basis::constant(); } else @@ -1039,7 +1090,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 +1217,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 +1339,80 @@ 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"; + + const bool isArnold = boost::starts_with( shaderType, "ai:" ); + const bool isRenderman = boost::starts_with( shaderType, "ri:" ) || boost::starts_with( shaderName, "Pxr" ); + const bool unduplicatePoints = !isArnold; + + if( isArnold && ( shaderName == "ramp_float" || shaderName == "ramp_rgb" ) ) + { + basisStr = "interpolation"; + positionsStr = "position"; + if( shaderName == "ramp_rgb" ) + { + valuesStr = "color"; + } + else + { + valuesStr = "value"; + } + } + else if( isRenderman ) + { + 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() ); + StringDataPtr basisPtr; + const 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 ) + { + basisPtr = new StringData( "constant" ); + } + else if( intBasis->readable().front() == 1 ) + { + basisPtr = new StringData( "linear" ); + } + else if( intBasis->readable().front() == 3 ) + { + basisPtr = new StringData( "monotonecubic" ); + } + else + { + basisPtr = new StringData( "catmull-rom" ); + } + } + else + { + basisPtr = basis->copy(); } - - 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,30 +1426,41 @@ 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() && isRenderman ) + { + valuesName = prefix + "_Colors"; + valuesIter = parameters.find( valuesName ); + } IECore::DataPtr foundSpline; if( valuesIter != parameters.end() ) { if( const FloatVectorData *floatValues = runTimeCast( valuesIter->second.get() ) ) { - foundSpline = loadSpline( basis, floatPositions, floatValues ); + foundSpline = loadSpline( basisPtr.get(), floatPositions, floatValues, unduplicatePoints ); } else if( const Color3fVectorData *color3Values = runTimeCast( valuesIter->second.get() ) ) { - foundSpline = loadSpline( basis, floatPositions, color3Values ); + foundSpline = loadSpline( basisPtr.get(), floatPositions, color3Values, unduplicatePoints ); } else if( const Color4fVectorData *color4Values = runTimeCast( valuesIter->second.get() ) ) { - foundSpline = loadSpline( basis, floatPositions, color4Values ); + foundSpline = loadSpline( basisPtr.get(), floatPositions, color4Values, unduplicatePoints ); } } if( foundSpline ) { ensureParametersCopy( parameters, newParametersData, newParameters ); - (*newParameters)[prefix] = foundSpline; + // Arnold ramp_rgb/ramp_float has no prefix so ensure we have a parameter name to set + std::string newParamName( "ramp" ); + if( !prefix.empty() ) + { + newParamName = prefix; + } + (*newParameters)[newParamName] = foundSpline; newParameters->erase( maybeBasis.first ); newParameters->erase( positionsName ); newParameters->erase( valuesName ); @@ -1362,7 +1477,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 +1490,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(