Skip to content

Commit bc72de8

Browse files
authored
Merge pull request #1438 from johnhaddon/shaderAlgoImprovements
ShaderNetworkAlgo improvements
2 parents 53bd043 + 38c7fdb commit bc72de8

File tree

13 files changed

+734
-212
lines changed

13 files changed

+734
-212
lines changed

Changes

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,15 @@
33

44
Improvements
55
------------
6+
67
- USDScene : PointInstancers are now loaded with invisibleIds and inactiveIds as primitive variables.
8+
- IECoreUSD::DataAlgo :
9+
- Made `valueTypeName` argument to `fromUSD( const VtValue & )` optional. This allows VtValue to be converted without having additional type information available.
10+
- Added conversions between `VtDictionary` and `CompoundData`.
11+
- IECoreUSD::ShaderAlgo :
12+
- Stopped writing `cortex_autoAdaptor` metadata, which would cause errors in DCCs without the definition registered.
13+
- Added round-tripping of all blind data stored on Shaders.
14+
- IECoreScene::ShaderNetworkAlgo : Added a mechanism for customising the adapter shaders used by `addComponentConnectionAdapters()`.
715

816
10.5.9.5 (relative to 10.5.9.4)
917
========

contrib/IECoreUSD/include/IECoreUSD/DataAlgo.h

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,12 @@ typename USDTypeTraits<T>::CortexType fromUSD( const T &value );
6666
template<typename T>
6767
boost::intrusive_ptr< typename USDTypeTraits<T>::CortexVectorDataType > fromUSD( const pxr::VtArray<T> &array );
6868

69-
/// Converts USD `value` to Cortex Data, applying any additional
70-
/// geometric interpretation implied by `valueTypeName`. If
71-
/// `arrayAccepted` is false, then converts single element arrays
72-
/// to simple data and emits a warning and returns nullptr for
73-
/// all other arrays. Returns nullptr if no appropriate conversion
74-
/// exists.
75-
IECOREUSD_API IECore::DataPtr fromUSD( const pxr::VtValue &value, const pxr::SdfValueTypeName &valueTypeName, bool arrayAccepted = true );
69+
/// Converts USD `value` to Cortex Data, applying any additional geometric
70+
/// interpretation implied by `valueTypeName` if it is provided. If
71+
/// `arrayAccepted` is false, then converts single element arrays to simple data
72+
/// and emits a warning and returns nullptr for all other arrays. Returns
73+
/// nullptr if no appropriate conversion exists.
74+
IECOREUSD_API IECore::DataPtr fromUSD( const pxr::VtValue &value, const pxr::SdfValueTypeName &valueTypeName = pxr::SdfValueTypeName(), bool arrayAccepted = true );
7675

7776
/// Converts the value of `attribute` at the specified time, using the attribute's
7877
/// type name to apply geometric interpretation. The meaning of `arrayAccepted` is

contrib/IECoreUSD/resources/plugInfo.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
"target": "usd"
1818
}
1919
},
20+
## \todo This metadata causes problems because other DCCs are
21+
# unlikely to have our `plugInfo.json` available. Replace it
22+
# with custom data instead (`UsdObject::SetCustomData()`), since
23+
# that doesn't require central registration.
2024
"SdfMetadata": {
2125
"cortex_isConstantPrimitiveVariable": {
2226
"type": "bool",
@@ -37,9 +41,7 @@
3741
"type": "bool",
3842
"appliesTo": "prims"
3943
},
40-
# Label a shader that was created automatically to hold
41-
# a connection that USD can't support directly - if we
42-
# want to round trip, we have to remove these on input
44+
# Legacy metadata once used by ShaderAlgo.
4345
"cortex_autoAdapter": {
4446
"type": "bool",
4547
"appliesTo": "prims"

contrib/IECoreUSD/src/IECoreUSD/DataAlgo.cpp

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
#include "IECoreUSD/DataAlgo.h"
3636

37+
#include "IECore/CompoundData.h"
3738
#include "IECore/DataAlgo.h"
3839
#include "IECore/MessageHandler.h"
3940

@@ -213,6 +214,19 @@ IECore::DataPtr dataFromSdfAssetPath( const pxr::VtValue &value, GeometricData::
213214
return dataFromSdfAssetPath( value.UncheckedGet<SdfAssetPath>() );
214215
}
215216

217+
IECore::DataPtr dataFromDictionary( const pxr::VtValue &value, GeometricData::Interpretation interpretation, bool arrayAccepted )
218+
{
219+
CompoundDataPtr result = new CompoundData;
220+
for( const auto &[name, v] : value.Get<VtDictionary>() )
221+
{
222+
if( IECore::DataPtr d = IECoreUSD::DataAlgo::fromUSD( v, pxr::SdfValueTypeName() ) )
223+
{
224+
result->writable()[name] = d;
225+
}
226+
}
227+
return result;
228+
}
229+
216230
static const std::map<pxr::TfType, IECore::DataPtr (*)( const pxr::VtValue &, GeometricData::Interpretation, bool )> g_fromVtValueConverters = {
217231

218232
// Numeric types
@@ -279,7 +293,11 @@ static const std::map<pxr::TfType, IECore::DataPtr (*)( const pxr::VtValue &, Ge
279293
{ TfType::Find<VtArray<string>>(), &dataFromArray<string> },
280294
{ TfType::Find<TfToken>(), &dataFromValue<TfToken> },
281295
{ TfType::Find<VtArray<TfToken>>(), &dataFromArray<TfToken> },
282-
{ TfType::Find<SdfAssetPath>(), &dataFromSdfAssetPath }
296+
{ TfType::Find<SdfAssetPath>(), &dataFromSdfAssetPath },
297+
298+
// Dictionary
299+
300+
{ TfType::Find<VtDictionary>(), &dataFromDictionary }
283301

284302
};
285303

@@ -323,20 +341,32 @@ static const std::map<pxr::TfType, std::function<IECore::DataPtr ( const pxr::Vt
323341

324342
IECore::DataPtr IECoreUSD::DataAlgo::fromUSD( const pxr::VtValue &value, const pxr::SdfValueTypeName &valueTypeName, bool arrayAccepted )
325343
{
326-
const GeometricData::Interpretation i = interpretation( valueTypeName.GetRole() );
344+
GeometricData::Interpretation i;
345+
TfType type;
346+
if( valueTypeName )
347+
{
348+
i = interpretation( valueTypeName.GetRole() );
349+
type = valueTypeName.GetType();
350+
}
351+
else
352+
{
353+
i = GeometricData::Interpretation::None;
354+
type = value.GetType();
355+
}
356+
327357
if( i == GeometricData::Color )
328358
{
329359
// Colors can not be identified by TfType because they borrow GfVec3,
330360
// so they require their own dispatch table.
331-
const auto it = g_fromVtValueColorConverters.find( valueTypeName.GetType() );
361+
const auto it = g_fromVtValueColorConverters.find( type );
332362
if( it == g_fromVtValueColorConverters.end() )
333363
{
334364
return nullptr;
335365
}
336366
return it->second( value, arrayAccepted );
337367
}
338368

339-
const auto it = g_fromVtValueConverters.find( valueTypeName.GetType() );
369+
const auto it = g_fromVtValueConverters.find( type );
340370
if( it == g_fromVtValueConverters.end() )
341371
{
342372
return nullptr;
@@ -420,6 +450,26 @@ struct VtValueFromData
420450
return VtValue( DataAlgo::toUSD( data->readable() ) );
421451
}
422452

453+
VtValue operator()( const IECore::CompoundData *data, bool arrayRequired )
454+
{
455+
if( arrayRequired )
456+
{
457+
return VtValue();
458+
}
459+
460+
VtDictionary result;
461+
for( const auto &[name, value] : data->readable() )
462+
{
463+
VtValue v = DataAlgo::toUSD( value.get() );
464+
if( !v.IsEmpty() )
465+
{
466+
result[name] = v;
467+
}
468+
}
469+
470+
return VtValue( result );
471+
}
472+
423473
VtValue operator()( const IECore::Data *data, bool arrayRequired ) const
424474
{
425475
return VtValue();
@@ -433,7 +483,13 @@ pxr::VtValue IECoreUSD::DataAlgo::toUSD( const IECore::Data *data, bool arrayReq
433483
{
434484
try
435485
{
436-
return IECore::dispatch( data, VtValueFromData(), arrayRequired );
486+
VtValueFromData valueFromData;
487+
if( auto cd = runTimeCast<const CompoundData>( data ) )
488+
{
489+
// Manual dispatch since CompoundData not handled by `dispatch()`.
490+
return valueFromData( cd, arrayRequired );
491+
}
492+
return IECore::dispatch( data, valueFromData, arrayRequired );
437493
}
438494
catch( const IECore::Exception & )
439495
{

contrib/IECoreUSD/src/IECoreUSD/ShaderAlgo.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@
6464
namespace
6565
{
6666

67-
pxr::TfToken g_adapterLabelToken( IECoreScene::ShaderNetworkAlgo::componentConnectionAdapterLabel().string() );
67+
const pxr::TfToken g_blindDataToken( "cortex:blindData" );
68+
pxr::TfToken g_legacyAdapterLabelToken( IECoreScene::ShaderNetworkAlgo::componentConnectionAdapterLabel().string() );
6869

6970
std::pair<pxr::TfToken, std::string> shaderIdAndType( const pxr::UsdShadeConnectableAPI &connectable )
7071
{
@@ -249,11 +250,26 @@ IECore::InternedString readShaderNetworkWalk( const pxr::SdfPath &anchorPath, co
249250
readNonStandardLightParameters( usdShader.GetPrim(), parameters );
250251

251252
IECoreScene::ShaderPtr newShader = new IECoreScene::Shader( shaderName, shaderType, parametersData );
253+
254+
// General purpose support for any Cortex blind data.
255+
256+
const pxr::VtValue blindDataValue = usdShader.GetPrim().GetCustomDataByKey( g_blindDataToken );
257+
if( !blindDataValue.IsEmpty() )
258+
{
259+
if( auto blindData = IECore::runTimeCast<IECore::CompoundData>( IECoreUSD::DataAlgo::fromUSD( blindDataValue ) ) )
260+
{
261+
newShader->blindData()->writable() = blindData->readable();
262+
}
263+
}
264+
265+
// Legacy support for `cortex_autoAdaptor` metadata.
266+
252267
pxr::VtValue metadataValue;
253-
if( usdShader.GetPrim().GetMetadata( g_adapterLabelToken, &metadataValue ) && metadataValue.Get<bool>() )
268+
if( usdShader.GetPrim().GetMetadata( g_legacyAdapterLabelToken, &metadataValue ) && metadataValue.Get<bool>() )
254269
{
255270
newShader->blindData()->writable()[ IECoreScene::ShaderNetworkAlgo::componentConnectionAdapterLabel() ] = new IECore::BoolData( true );
256271
}
272+
257273
shaderNetwork.addShader( handle, std::move( newShader ) );
258274

259275
// Can only add connections after we've added the shader.
@@ -350,10 +366,12 @@ void writeShaderParameterValues( const IECoreScene::Shader *shader, pxr::UsdShad
350366
input.Set( IECoreUSD::DataAlgo::toUSD( p.second.get() ) );
351367
}
352368

353-
const IECore::BoolData *adapterMeta = shader->blindData()->member<IECore::BoolData>( IECoreScene::ShaderNetworkAlgo::componentConnectionAdapterLabel() );
354-
if( adapterMeta && adapterMeta->readable() )
369+
if( shader->blindData()->readable().size() )
355370
{
356-
usdShader.GetPrim().SetMetadata( g_adapterLabelToken, true );
371+
usdShader.GetPrim().SetCustomDataByKey(
372+
g_blindDataToken,
373+
IECoreUSD::DataAlgo::toUSD( shader->blindData() )
374+
);
357375
}
358376
}
359377

contrib/IECoreUSD/test/IECoreUSD/DataAlgoTest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def testToUSDBinding( self ) :
8383
( IECore.FloatData( 2.5 ), 2.5 ),
8484
( IECore.IntVectorData( [ 1, 2, 3 ] ), [ 1, 2, 3 ] ),
8585
( IECore.PathMatcherData(), None ),
86-
( IECore.CompoundData(), None ),
86+
( IECore.CompoundData(), {} ),
8787
] :
8888
self.assertEqual( IECoreUSD.DataAlgo.toUSD( data ), value )
8989

contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3534,6 +3534,55 @@ def testColor4fShaderParameterComponentConnections( self ) :
35343534
root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Read )
35353535
self.assertEqual( root.child( "object" ).readAttribute( "ai:surface", 0 ), network )
35363536

3537+
def testLegacyComponentConnections( self ) :
3538+
3539+
expectedNetwork = IECoreScene.ShaderNetwork(
3540+
shaders = {
3541+
"source" : IECoreScene.Shader( "noise" ),
3542+
"output" : IECoreScene.Shader(
3543+
"color_correct",
3544+
parameters = {
3545+
"input" : imath.Color4f( 1 ),
3546+
}
3547+
),
3548+
},
3549+
connections = [
3550+
( ( "source", "r" ), ( "output", "input.g" ) ),
3551+
( ( "source", "g" ), ( "output", "input.b" ) ),
3552+
( ( "source", "b" ), ( "output", "input.r" ) ),
3553+
( ( "source", "r" ), ( "output", "input.a" ) ),
3554+
],
3555+
output = "output",
3556+
)
3557+
3558+
root = IECoreScene.SceneInterface.create( os.path.join( os.path.dirname( __file__ ), "data", "legacyComponentConnections.usda" ), IECore.IndexedIO.OpenMode.Read )
3559+
self.assertEqual( root.child( "object" ).readAttribute( "ai:surface", 0 ), expectedNetwork )
3560+
3561+
def testShaderBlindData( self ) :
3562+
3563+
shader = IECoreScene.Shader( "test" )
3564+
shader.blindData()["testInt"] = IECore.IntData( 10 )
3565+
shader.blindData()["testFloatVector"] = IECore.FloatVectorData( [ 1, 2, 3, ] )
3566+
shader.blindData()["test:colon"] = IECore.BoolData( True )
3567+
shader.blindData()["testCompound"] = IECore.CompoundData( {
3568+
"testString" : "test",
3569+
"testStringVector" : IECore.StringVectorData( [ "one", "two" ] )
3570+
} )
3571+
3572+
network = IECoreScene.ShaderNetwork(
3573+
shaders = { "test" : shader },
3574+
output = ( "test", "out" )
3575+
)
3576+
3577+
fileName = os.path.join( self.temporaryDirectory(), "testShaderBlindData.usda" )
3578+
root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Write )
3579+
object = root.createChild( "object" )
3580+
object.writeAttribute( "surface", network, 0.0 )
3581+
del object, root
3582+
3583+
root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Read )
3584+
self.assertEqual( root.child( "object" ).readAttribute( "surface", 0 ), network )
3585+
35373586
def testMaterialPurpose( self ) :
35383587

35393588
def assertExpected( root ) :
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#usda 1.0
2+
3+
def Xform "object" (
4+
prepend apiSchemas = ["MaterialBindingAPI"]
5+
)
6+
{
7+
rel material:binding = </object/materials/material_aaad0a40dfc4f67f90ca42cbc732dec4>
8+
9+
def Scope "materials" (
10+
cortex_autoMaterials = true
11+
)
12+
{
13+
def Material "material_aaad0a40dfc4f67f90ca42cbc732dec4"
14+
{
15+
token outputs:arnold:surface.connect = </object/materials/material_aaad0a40dfc4f67f90ca42cbc732dec4/arnold_surface_shaders/output.outputs:DEFAULT_OUTPUT>
16+
17+
def Scope "arnold_surface_shaders"
18+
{
19+
def Shader "output"
20+
{
21+
uniform token info:id = "color_correct"
22+
color4f inputs:input = (1, 1, 1, 1)
23+
color4f inputs:input.connect = </object/materials/material_aaad0a40dfc4f67f90ca42cbc732dec4/arnold_surface_shaders/pack.outputs:out>
24+
token outputs:DEFAULT_OUTPUT
25+
}
26+
27+
def Shader "pack" (
28+
cortex_autoAdapter = true
29+
)
30+
{
31+
uniform token info:id = "osl:MaterialX/mx_pack_color"
32+
float inputs:in1 = 1
33+
float inputs:in1.connect = </object/materials/material_aaad0a40dfc4f67f90ca42cbc732dec4/arnold_surface_shaders/source.outputs:b>
34+
float inputs:in2 = 1
35+
float inputs:in2.connect = </object/materials/material_aaad0a40dfc4f67f90ca42cbc732dec4/arnold_surface_shaders/source.outputs:r>
36+
float inputs:in3 = 1
37+
float inputs:in3.connect = </object/materials/material_aaad0a40dfc4f67f90ca42cbc732dec4/arnold_surface_shaders/source.outputs:g>
38+
float inputs:in4 = 1
39+
float inputs:in4.connect = </object/materials/material_aaad0a40dfc4f67f90ca42cbc732dec4/arnold_surface_shaders/source.outputs:r>
40+
color4f outputs:out
41+
}
42+
43+
def Shader "source"
44+
{
45+
uniform token info:id = "noise"
46+
float outputs:b
47+
float outputs:g
48+
float outputs:r
49+
}
50+
}
51+
}
52+
}
53+
}
54+

include/IECoreScene/ShaderNetworkAlgo.h

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,39 @@ IECORESCENE_API void removeUnusedShaders( ShaderNetwork *network );
6363
template<typename Visitor>
6464
void depthFirstTraverse( const ShaderNetwork *network, Visitor &&visitor, IECore::InternedString shader = "" );
6565

66-
/// Replace connections between sub components of colors or vectors with connections to whole parameters
67-
/// on adapter shaders. Currently uses the OSL shaders mx_pack_color and mx_swizzle_color_float as adapters.
68-
/// The newly created shaders will be labelled with a blind data so they can be identified.
69-
/// If `targetPrefix` is given, only translates connections to shaders with a type starting with this string
66+
/// Replaces connections between sub components of colors or vectors with
67+
/// connections to whole parameters on adapter shaders. If `targetPrefix` is
68+
/// given, only translates connections to shaders with a type starting with this
69+
/// string.
7070
IECORESCENE_API void addComponentConnectionAdapters( ShaderNetwork *network, std::string targetPrefix = "" );
7171

72-
/// Find adapters that were created by addComponentConnectionAdapters ( based on the blind data label ),
73-
/// and remove them, replacing them with the original component connections
72+
/// Finds adapters that were created by addComponentConnectionAdapters, and
73+
/// removes them, replacing them with the original component connections.
7474
IECORESCENE_API void removeComponentConnectionAdapters( ShaderNetwork *network );
7575

76-
/// The name of the boolean blindData label used by add/removeComponentConnectionAdapters
76+
/// Registers an adapter to split a component from a color or vector output, ready for connection into
77+
/// a scalar input. Used by `addComponentConnectionAdapters()`.
78+
///
79+
/// - `destinationShaderType` : The type prefix for the shader receiving the connection - e.g. "ai", "osl".
80+
/// - `component` : "r", "g", "b", "a", "x", "y", or "z".
81+
/// - `adapter` : The shader to be used as the adapter.
82+
/// - `inParameter` : The parameter that receives the color or vector input.
83+
/// - `outParameter` : The parameter that outputs the component.
84+
IECORESCENE_API void registerSplitAdapter( const std::string &destinationShaderType, IECore::InternedString component, const IECoreScene::Shader *adapter, IECore::InternedString inParameter, IECore::InternedString outParameter );
85+
/// Removes an adapter registration.
86+
IECORESCENE_API void deregisterSplitAdapter( const std::string &destinationShaderType, IECore::InternedString component );
87+
88+
/// Registers an adapter to join multiple scalar components into a color or vector output. Used by `addComponentConnectionAdapters()`.
89+
///
90+
/// - `destinationShaderType` : The type prefix for the shader receiving the connection - e.g. "ai", "osl".
91+
/// - `destinationParameterType` : `(V2i|V3i|V2f|V3f|Color3f|Color4f)DataTypeId`.
92+
/// - `inParameters` : The parameters that receives the individual components of the vector or color.
93+
/// - `outParameter` : The parameter that outputs the vector or color.
94+
IECORESCENE_API void registerJoinAdapter( const std::string &destinationShaderType, IECore::TypeId destinationParameterType, const IECoreScene::Shader *adapter, const std::array<IECore::InternedString, 4> &inParameters, IECore::InternedString outParameter );
95+
/// Removes an adapter registration.
96+
IECORESCENE_API void deregisterJoinAdapter( const std::string &destinationShaderType, IECore::TypeId destinationParameterType );
97+
98+
/// \deprecated
7799
IECORESCENE_API const IECore::InternedString &componentConnectionAdapterLabel();
78100

79101
/// Converts various aspects of how shaders are stored to be ready to pass directly to OSL.

0 commit comments

Comments
 (0)