Skip to content

Commit 06919d9

Browse files
authored
Merge pull request #1324 from johnhaddon/usdConnectionIndices
USD ShaderAlgo : Support Arnold-USD's convention for array connections
2 parents a299df4 + 80661be commit 06919d9

File tree

3 files changed

+101
-4
lines changed

3 files changed

+101
-4
lines changed

contrib/IECoreUSD/src/IECoreUSD/ShaderAlgo.cpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
#include "boost/algorithm/string/replace.hpp"
5050
#include "boost/pointer_cast.hpp"
5151

52+
#include <regex>
53+
5254
#if PXR_VERSION < 2102
5355
#define IsContainer IsNodeGraph
5456
#endif
@@ -98,6 +100,25 @@ void readAdditionalLightParameters( const pxr::UsdPrim &prim, IECore::CompoundDa
98100
#endif
99101
}
100102

103+
const std::regex g_arrayIndexFromUSDRegex( ":i([0-9]+)$" );
104+
const std::string g_arrayIndexFromUSDFormat( "[$1]" );
105+
IECore::InternedString fromUSDParameterName( const pxr::TfToken &usdName )
106+
{
107+
// USD doesn't support connections to array indices. So Arnold-USD emulates
108+
// them using its own `parameter:i<N>`syntax - see https://github.com/Autodesk/arnold-usd/pull/381.
109+
// We convert these to the regular `parameter[N]` syntax during loading.
110+
return std::regex_replace( usdName.GetString(), g_arrayIndexFromUSDRegex, g_arrayIndexFromUSDFormat );
111+
}
112+
113+
const std::regex g_arrayIndexFromCortexRegex( "\\[([0-9]+)\\]$" );
114+
const std::string g_arrayIndexFromCortexFormat( ":i$1" );
115+
pxr::TfToken toUSDParameterName( IECore::InternedString cortexName )
116+
{
117+
return pxr::TfToken(
118+
std::regex_replace( cortexName.string(), g_arrayIndexFromCortexRegex, g_arrayIndexFromCortexFormat )
119+
);
120+
}
121+
101122
IECoreScene::ShaderNetwork::Parameter readShaderNetworkWalk( const pxr::SdfPath &anchorPath, const pxr::UsdShadeOutput &output, IECoreScene::ShaderNetwork &shaderNetwork );
102123

103124
IECore::InternedString readShaderNetworkWalk( const pxr::SdfPath &anchorPath, const pxr::UsdShadeConnectableAPI &usdShader, IECoreScene::ShaderNetwork &shaderNetwork )
@@ -146,7 +167,7 @@ IECore::InternedString readShaderNetworkWalk( const pxr::SdfPath &anchorPath, co
146167
anchorPath, usdSource.GetOutput( usdSourceName ), shaderNetwork
147168
);
148169
connections.push_back( {
149-
sourceHandle, { handle, IECore::InternedString( i.GetBaseName().GetString() ) }
170+
sourceHandle, { handle, fromUSDParameterName( i.GetBaseName() ) }
150171
} );
151172
}
152173
else
@@ -160,7 +181,7 @@ IECore::InternedString readShaderNetworkWalk( const pxr::SdfPath &anchorPath, co
160181

161182
if( IECore::DataPtr d = IECoreUSD::DataAlgo::fromUSD( pxr::UsdAttribute( valueAttribute ) ) )
162183
{
163-
parameters[ i.GetBaseName().GetString() ] = d;
184+
parameters[fromUSDParameterName( i.GetBaseName() )] = d;
164185
}
165186
}
166187

@@ -244,7 +265,7 @@ pxr::UsdShadeOutput IECoreUSD::ShaderAlgo::writeShaderNetwork( const IECoreScene
244265
for( const auto &p : expandedParameters->readable() )
245266
{
246267
pxr::UsdShadeInput input = usdShader.CreateInput(
247-
pxr::TfToken( p.first.string() ),
268+
toUSDParameterName( p.first ),
248269
DataAlgo::valueTypeName( p.second.get() )
249270
);
250271
input.Set( DataAlgo::toUSD( p.second.get() ) );
@@ -278,7 +299,7 @@ pxr::UsdShadeOutput IECoreUSD::ShaderAlgo::writeShaderNetwork( const IECoreScene
278299
pxr::UsdShadeInput dest = usdShader.GetInput( pxr::TfToken( c.destination.name.string() ) );
279300
if( ! dest.GetPrim().IsValid() )
280301
{
281-
dest = usdShader.CreateInput( pxr::TfToken( c.destination.name.string() ), pxr::SdfValueTypeNames->Token );
302+
dest = usdShader.CreateInput( toUSDParameterName( c.destination.name ), pxr::SdfValueTypeNames->Token );
282303
}
283304

284305
pxr::UsdShadeShader sourceUsdShader = pxr::UsdShadeShader::Get( shaderContainer.GetStage(), shaderContainer.GetPath().AppendChild( pxr::TfToken( pxr::TfMakeValidIdentifier( c.source.shader.string() ) ) ) );

contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3271,5 +3271,34 @@ def testPointInstancerPrimvars( self ) :
32713271
self.assertEqual( points["myColor"].interpolation, IECoreScene.PrimitiveVariable.Interpolation.Vertex )
32723272
self.assertEqual( points["myColor"].indices, None )
32733273

3274+
def testArnoldArrayInputs( self ) :
3275+
3276+
def assertExpectedArrayInputs( network ) :
3277+
3278+
inputs = network.inputConnections( "rampRGB" )
3279+
self.assertEqual( len( inputs ), 2 )
3280+
self.assertEqual( inputs[0], ( ( "noise", "out" ), ( "rampRGB", "color[0]" ) ) )
3281+
self.assertEqual( inputs[1], ( ( "flat", "out" ), ( "rampRGB", "color[1]" ) ) )
3282+
3283+
# Load original USD out of USD-Arnold.
3284+
3285+
scene = IECoreScene.SceneInterface.create(
3286+
os.path.join( os.path.dirname( __file__ ), "data", "arnoldArrayInputs.usda" ),
3287+
IECore.IndexedIO.OpenMode.Read
3288+
)
3289+
network = scene.child( "sphere" ).readAttribute( "ai:surface", 0 )
3290+
3291+
assertExpectedArrayInputs( network )
3292+
3293+
# Write our own USD from that data, to check we can round-trip it.
3294+
3295+
fileName = os.path.join( self.temporaryDirectory(), "arnoldArrayInputsRewritten.usda" )
3296+
scene = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Write )
3297+
scene.createChild( "sphere" ).writeAttribute( "ai:surface", network, 0 )
3298+
3299+
del scene
3300+
scene = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Read )
3301+
assertExpectedArrayInputs( scene.child( "sphere" ).readAttribute( "ai:surface", 0 ) )
3302+
32743303
if __name__ == "__main__":
32753304
unittest.main()
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#usda 1.0
2+
3+
def Sphere "sphere"
4+
{
5+
rel material:binding = </rampSurface>
6+
}
7+
8+
def Material "rampSurface"
9+
{
10+
token outputs:arnold:surface.connect = </rampSurface/standardSurface.outputs:surface>
11+
12+
def Shader "standardSurface"
13+
{
14+
uniform token info:id = "arnold:standard_surface"
15+
color3f inputs:base_color = (0.8, 0.8, 0.8)
16+
prepend color3f inputs:base_color.connect = </rampSurface/rampRGB.outputs:out>
17+
float inputs:indirect_specular = 0
18+
token outputs:surface
19+
}
20+
21+
def Shader "rampRGB"
22+
{
23+
uniform token info:id = "arnold:ramp_rgb"
24+
color3f[] inputs:color = [(0, 0, 0), (0.5, 0.5, 0.5)]
25+
prepend color3f inputs:color:i0.connect = </rampSurface/noise.outputs:out>
26+
prepend color3f inputs:color:i1.connect = </rampSurface/flat.outputs:out>
27+
int[] inputs:interpolation = [1, 1]
28+
float[] inputs:position = [0, 1]
29+
token inputs:type = "v"
30+
color3f outputs:out
31+
}
32+
33+
def Shader "noise"
34+
{
35+
uniform token info:id = "arnold:noise"
36+
color3f inputs:color1 = (1, 0, 0)
37+
color3f inputs:color2 = (0, 1, 0)
38+
color3f outputs:out
39+
}
40+
41+
def Shader "flat"
42+
{
43+
uniform token info:id = "arnold:flat"
44+
color3f inputs:color = (0, 0, 1)
45+
color3f outputs:out
46+
}
47+
}

0 commit comments

Comments
 (0)