Skip to content

Commit 901165c

Browse files
authored
Merge pull request #1416 from johnhaddon/arnoldLightPrimvars
USD ShaderAlgo : Fix `treatAs*` and `arnold:*` light parameters
2 parents f8d3b66 + 0590bbf commit 901165c

File tree

3 files changed

+156
-4
lines changed

3 files changed

+156
-4
lines changed

Changes

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
10.5.x.x (relative to 10.5.7.0)
22
========
33

4+
Fixes
5+
-----
6+
7+
- USDScene :
8+
- Adding mapping of `arnold:*` light parameters to and from the non-standard `primvars:arnold:*` attributes preferred by the `arnold-usd` project.
9+
- Fixed writing of `treatAsPoint` and `treatAsLine` light parameters, which were being written as generic `inputs:*` attributes and not the specific
10+
attributes defined by the SphereLight and CylinderLight schemas.
411

512
10.5.7.0 (relative to 10.5.6.2)
613
========

contrib/IECoreUSD/src/IECoreUSD/ShaderAlgo.cpp

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
#include "pxr/usd/usd/schemaRegistry.h"
5050
#endif
5151

52+
#include "pxr/usd/usdGeom/primvarsAPI.h"
53+
5254
#include "boost/algorithm/string/predicate.hpp"
5355
#include "boost/algorithm/string/replace.hpp"
5456
#include "boost/pointer_cast.hpp"
@@ -84,7 +86,43 @@ std::pair<pxr::TfToken, std::string> shaderIdAndType( const pxr::UsdShadeConnect
8486
return std::make_pair( id, type );
8587
}
8688

87-
void readAdditionalLightParameters( const pxr::UsdPrim &prim, IECore::CompoundDataMap &parameters )
89+
bool writeNonStandardLightParameter( const std::string &name, const IECore::Data *value, pxr::UsdShadeConnectableAPI usdShader )
90+
{
91+
#if PXR_VERSION >= 2111
92+
93+
if( auto sphereLight = pxr::UsdLuxSphereLight( usdShader.GetPrim() ) )
94+
{
95+
if( name == "treatAsPoint" )
96+
{
97+
sphereLight.GetTreatAsPointAttr().Set( IECoreUSD::DataAlgo::toUSD( value ) );
98+
return true;
99+
}
100+
}
101+
else if( auto cylinderLight = pxr::UsdLuxCylinderLight( usdShader.GetPrim() ) )
102+
{
103+
if( name == "treatAsLine" )
104+
{
105+
cylinderLight.GetTreatAsLineAttr().Set( IECoreUSD::DataAlgo::toUSD( value ) );
106+
return true;
107+
}
108+
}
109+
110+
if( pxr::UsdLuxLightAPI( usdShader.GetPrim() ) )
111+
{
112+
if( boost::starts_with( name, "arnold:" ) )
113+
{
114+
const pxr::SdfValueTypeName valueTypeName = IECoreUSD::DataAlgo::valueTypeName( value );
115+
pxr::UsdGeomPrimvar primVar = pxr::UsdGeomPrimvarsAPI( usdShader.GetPrim() ).CreatePrimvar( pxr::TfToken( name ), valueTypeName );
116+
primVar.Set( IECoreUSD::DataAlgo::toUSD( value ) );
117+
return true;
118+
}
119+
}
120+
121+
#endif
122+
return false;
123+
}
124+
125+
void readNonStandardLightParameters( const pxr::UsdPrim &prim, IECore::CompoundDataMap &parameters )
88126
{
89127
// Just to keep us on our toes, not all light parameters are stored as UsdShade inputs,
90128
// so we have special-case code for loading those here.
@@ -101,6 +139,25 @@ void readAdditionalLightParameters( const pxr::UsdPrim &prim, IECore::CompoundDa
101139
cylinderLight.GetTreatAsLineAttr().Get( &treatAsLine );
102140
parameters["treatAsLine"] = new IECore::BoolData( treatAsLine );
103141
}
142+
143+
if( auto light = pxr::UsdLuxLightAPI( prim ) )
144+
{
145+
pxr::UsdGeomPrimvarsAPI primVarsAPI( prim );
146+
for( const auto &primVar : primVarsAPI.GetPrimvarsWithAuthoredValues() )
147+
{
148+
pxr::TfToken name = primVar.GetPrimvarName();
149+
if( !boost::starts_with( name.GetString(), "arnold:" ) )
150+
{
151+
continue;
152+
}
153+
154+
pxr::VtValue value;
155+
if( primVar.Get( &value ) )
156+
{
157+
parameters[name.GetString()] = IECoreUSD::DataAlgo::fromUSD( value, primVar.GetTypeName() );
158+
}
159+
}
160+
}
104161
#endif
105162
}
106163

@@ -189,7 +246,7 @@ IECore::InternedString readShaderNetworkWalk( const pxr::SdfPath &anchorPath, co
189246
}
190247
}
191248

192-
readAdditionalLightParameters( usdShader.GetPrim(), parameters );
249+
readNonStandardLightParameters( usdShader.GetPrim(), parameters );
193250

194251
IECoreScene::ShaderPtr newShader = new IECoreScene::Shader( shaderName, shaderType, parametersData );
195252
pxr::VtValue metadataValue;
@@ -256,6 +313,11 @@ void writeShaderParameterValues( const IECoreScene::Shader *shader, pxr::UsdShad
256313
{
257314
for( const auto &p : shader->parametersData()->readable() )
258315
{
316+
if( writeNonStandardLightParameter( p.first.string(), p.second.get(), usdShader ) )
317+
{
318+
continue;
319+
}
320+
259321
const pxr::TfToken usdParameterName = toUSDParameterName( p.first );
260322
pxr::UsdShadeInput input = usdShader.GetInput( usdParameterName );
261323
if( !input )

contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -866,7 +866,7 @@ def testSubdOptions( self ) :
866866
( "interpolateBoundary", allowedIB ),
867867
( "faceVaryingLinearInterpolation", allowedFVLI ),
868868
( "triangleSubdivisionRule", allowedTS ),
869-
869+
870870
]:
871871
for value in allowed:
872872

@@ -889,7 +889,7 @@ def testSubdOptions( self ) :
889889

890890
if property == "triangleSubdivisionRule":
891891
mesh.CreateTriangleSubdivisionRuleAttr().Set( value, 0.0 )
892-
892+
893893
stage.GetRootLayer().Save()
894894
del stage
895895

@@ -4091,5 +4091,88 @@ def testRoundTripArnoldLight( self ) :
40914091
self.assertIn( "__lights", root.setNames() )
40924092
self.assertEqual( root.readSet( "__lights" ), IECore.PathMatcher( [ "/light" ] ) )
40934093

4094+
def testArnoldSpecificLightInputs( self ) :
4095+
4096+
# The `arnold-usd` project doesn't represent Arnold-specific UsdLux
4097+
# extensions as `inputs:arnold:*` attributes as it logically should :
4098+
# instead it uses `primvars:arnold:*` attributes. In Cortex/Gaffer we
4099+
# wish to use regular `arnold:*` shader parameters rather than primvars,
4100+
# so must convert to and from the less logical form in USDScene.
4101+
4102+
lightShader = IECoreScene.ShaderNetwork(
4103+
shaders = {
4104+
"light" : IECoreScene.Shader(
4105+
"RectLight", "light",
4106+
parameters = {
4107+
"exposure" : 1.0,
4108+
"arnold:roundness" : 2.0,
4109+
}
4110+
)
4111+
},
4112+
output = "light",
4113+
)
4114+
4115+
fileName = os.path.join( self.temporaryDirectory(), "test.usda" )
4116+
root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Write )
4117+
light = root.createChild( "light" )
4118+
light.writeAttribute( "light", lightShader, 0 )
4119+
del root, light
4120+
4121+
stage = pxr.Usd.Stage.Open( fileName )
4122+
shadeAPI = pxr.UsdShade.ConnectableAPI( stage.GetPrimAtPath( "/light" ) )
4123+
self.assertTrue( shadeAPI.GetInput( "exposure" ) )
4124+
self.assertFalse( shadeAPI.GetInput( "arnold:roundness" ) )
4125+
primvarsAPI = pxr.UsdGeom.PrimvarsAPI( stage.GetPrimAtPath( "/light" ) )
4126+
self.assertTrue( primvarsAPI.HasPrimvar( "arnold:roundness" ) )
4127+
self.assertEqual( primvarsAPI.GetPrimvar( "arnold:roundness" ).Get(), 2.0 )
4128+
del stage, shadeAPI, primvarsAPI
4129+
4130+
root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Read )
4131+
self.assertEqual( root.child( "light" ).readAttribute( "light", 0 ), lightShader )
4132+
4133+
def testTreatLightAsPointOrLine( self ) :
4134+
4135+
# `treatAsPoint` and `treatAsLine` aren't defined as UsdShade inputs but we store
4136+
# them as regular shader parameter, so they need special handling when writing to USD.
4137+
4138+
sphereLightShader = IECoreScene.ShaderNetwork(
4139+
shaders = {
4140+
"sphereLight" : IECoreScene.Shader(
4141+
"SphereLight", "light",
4142+
parameters = {
4143+
"treatAsPoint" : True,
4144+
}
4145+
)
4146+
},
4147+
output = "sphereLight",
4148+
)
4149+
4150+
cylinderLightShader = IECoreScene.ShaderNetwork(
4151+
shaders = {
4152+
"cylinderLight" : IECoreScene.Shader(
4153+
"CylinderLight", "light",
4154+
parameters = {
4155+
"treatAsLine" : True,
4156+
}
4157+
)
4158+
},
4159+
output = "cylinderLight",
4160+
)
4161+
4162+
fileName = os.path.join( self.temporaryDirectory(), "test.usda" )
4163+
root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Write )
4164+
root.createChild( "sphereLight" ).writeAttribute( "light", sphereLightShader, 0 )
4165+
root.createChild( "cylinderLight" ).writeAttribute( "light", cylinderLightShader, 0 )
4166+
del root
4167+
4168+
stage = pxr.Usd.Stage.Open( fileName )
4169+
self.assertEqual( pxr.UsdLux.SphereLight( stage.GetPrimAtPath( "/sphereLight" ) ).GetTreatAsPointAttr().Get(), True )
4170+
self.assertEqual( pxr.UsdLux.CylinderLight( stage.GetPrimAtPath( "/cylinderLight" ) ).GetTreatAsLineAttr().Get(), True )
4171+
del stage
4172+
4173+
root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Read )
4174+
self.assertEqual( root.child( "sphereLight" ).readAttribute( "light", 0 ), sphereLightShader )
4175+
self.assertEqual( root.child( "cylinderLight" ).readAttribute( "light", 0 ), cylinderLightShader )
4176+
40944177
if __name__ == "__main__":
40954178
unittest.main()

0 commit comments

Comments
 (0)