4343
4444#if PXR_VERSION >= 2111
4545#include " pxr/usd/usdLux/cylinderLight.h"
46+ #include " pxr/usd/usdLux/nonboundableLightBase.h"
4647#include " pxr/usd/usdLux/sphereLight.h"
48+
49+ #include " pxr/usd/usd/schemaRegistry.h"
4750#endif
4851
4952#include " boost/algorithm/string/replace.hpp"
@@ -217,87 +220,89 @@ IECoreScene::ShaderNetwork::Parameter readShaderNetworkWalk( const pxr::SdfPath
217220 }
218221}
219222
220- } // namespace
221-
222- pxr::UsdShadeOutput IECoreUSD::ShaderAlgo::writeShaderNetwork ( const IECoreScene::ShaderNetwork *shaderNetwork, pxr::UsdPrim shaderContainer )
223+ IECoreScene::ConstShaderNetworkPtr adaptShaderNetworkForWriting ( const IECoreScene::ShaderNetwork *shaderNetwork )
223224{
224- IECoreScene::ShaderNetworkPtr shaderNetworkWithAdapters = shaderNetwork->copy ();
225- IECoreScene::ShaderNetworkAlgo::expandSplines ( shaderNetworkWithAdapters.get () );
226- IECoreScene::ShaderNetworkAlgo::addComponentConnectionAdapters ( shaderNetworkWithAdapters.get () );
225+ IECoreScene::ShaderNetworkPtr result = shaderNetwork->copy ();
226+ IECoreScene::ShaderNetworkAlgo::expandSplines ( result.get () );
227+ IECoreScene::ShaderNetworkAlgo::addComponentConnectionAdapters ( result.get () );
228+ return result;
229+ }
227230
228- IECoreScene::ShaderNetwork::Parameter networkOutput = shaderNetworkWithAdapters->getOutput ();
229- if ( networkOutput.shader .string () == " " )
231+ pxr::UsdShadeConnectableAPI createShaderPrim ( const IECoreScene::Shader *shader, const pxr::UsdStagePtr &stage, const pxr::SdfPath &path )
232+ {
233+ pxr::UsdShadeShader usdShader = pxr::UsdShadeShader::Define ( stage, path );
234+ if ( !usdShader )
230235 {
231- // This could theoretically happen, but a shader network with no output is not useful in any way
232- IECore::msg (
233- IECore::Msg::Warning, " IECoreUSD::ShaderAlgo::writeShaderNetwork" ,
234- " No output shader in network"
235- );
236+ throw IECore::Exception ( " Could not create shader at " + path.GetAsString () );
236237 }
237-
238- pxr::UsdShadeOutput networkOutUsd;
239- for ( const auto &shader : shaderNetworkWithAdapters->shaders () )
238+ const std::string type = shader->getType ();
239+ std::string typePrefix;
240+ size_t typeColonPos = type.find ( " :" );
241+ if ( typeColonPos != std::string::npos )
240242 {
241- pxr::SdfPath usdShaderPath = shaderContainer.GetPath ().AppendChild ( pxr::TfToken ( pxr::TfMakeValidIdentifier ( shader.first .string () ) ) );
242- pxr::UsdShadeShader usdShader = pxr::UsdShadeShader::Define ( shaderContainer.GetStage (), usdShaderPath );
243- if ( !usdShader )
243+ typePrefix = type.substr ( 0 , typeColonPos ) + " :" ;
244+ if ( typePrefix == " ai:" )
244245 {
245- throw IECore::Exception ( " Could not create shader at: " + shaderContainer.GetPath ().GetString () + " / " + shader.first .string () );
246- }
247- std::string type = shader.second ->getType ();
248- std::string typePrefix;
249- size_t typeColonPos = type.find ( " :" );
250- if ( typeColonPos != std::string::npos )
251- {
252- typePrefix = type.substr ( 0 , typeColonPos ) + " :" ;
253- if ( typePrefix == " ai:" )
254- {
255- typePrefix = " arnold:" ;
256- }
246+ typePrefix = " arnold:" ;
257247 }
258- usdShader.SetShaderId ( pxr::TfToken ( typePrefix + shader.second ->getName () ) );
248+ }
249+ usdShader.SetShaderId ( pxr::TfToken ( typePrefix + shader->getName () ) );
250+
251+ return usdShader.ConnectableAPI ();
252+ }
259253
260- for ( const auto &p : shader.second ->parametersData ()->readable () )
254+ void writeShaderParameterValues ( const IECoreScene::Shader *shader, pxr::UsdShadeConnectableAPI usdShader )
255+ {
256+ for ( const auto &p : shader->parametersData ()->readable () )
257+ {
258+ const pxr::TfToken usdParameterName = toUSDParameterName ( p.first );
259+ pxr::UsdShadeInput input = usdShader.GetInput ( usdParameterName );
260+ if ( !input )
261261 {
262- pxr::UsdShadeInput input = usdShader.CreateInput (
262+ input = usdShader.CreateInput (
263263 toUSDParameterName ( p.first ),
264- DataAlgo::valueTypeName ( p.second .get () )
264+ IECoreUSD:: DataAlgo::valueTypeName ( p.second .get () )
265265 );
266- input.Set ( DataAlgo::toUSD ( p.second .get () ) );
267266 }
268-
269- if ( networkOutput.shader == shader.first )
267+ if ( auto *s = IECore::runTimeCast<IECore::StringData>( p.second .get () ) )
270268 {
271- pxr::TfToken outName ( networkOutput. name . string () );
272- if ( outName. GetString (). size () == 0 )
269+ // USD has several "stringy" types - convert if necessary.
270+ if ( input. GetTypeName () == pxr::SdfValueTypeNames-> Token )
273271 {
274- outName = pxr::TfToken ( " DEFAULT_OUTPUT" );
272+ input.Set ( pxr::TfToken ( s->readable () ) );
273+ continue ;
274+ }
275+ else if ( input.GetTypeName ().GetType ().IsA <pxr::SdfAssetPath>() )
276+ {
277+ input.Set ( pxr::SdfAssetPath ( s->readable () ) );
278+ continue ;
275279 }
276-
277- // \todo - we should probably be correctly tracking the output type if it is typed?
278- // Currently, we don't really track output types in Gaffer.
279- networkOutUsd = usdShader.CreateOutput ( outName, pxr::SdfValueTypeNames->Token );
280280 }
281+ input.Set ( IECoreUSD::DataAlgo::toUSD ( p.second .get () ) );
282+ }
281283
282- const IECore::BoolData* adapterMeta = shader.second ->blindData ()->member <IECore::BoolData>( IECoreScene::ShaderNetworkAlgo::componentConnectionAdapterLabel () );
283- if ( adapterMeta && adapterMeta->readable () )
284- {
285- usdShader.GetPrim ().SetMetadata ( g_adapterLabelToken, true );
286- }
284+ const IECore::BoolData *adapterMeta = shader->blindData ()->member <IECore::BoolData>( IECoreScene::ShaderNetworkAlgo::componentConnectionAdapterLabel () );
285+ if ( adapterMeta && adapterMeta->readable () )
286+ {
287+ usdShader.GetPrim ().SetMetadata ( g_adapterLabelToken, true );
287288 }
289+ }
288290
289- for ( const auto &shader : shaderNetworkWithAdapters->shaders () )
291+ using ShaderMap = std::unordered_map<IECore::InternedString, pxr::UsdShadeConnectableAPI>;
292+ void writeShaderConnections ( const IECoreScene::ShaderNetwork *shaderNetwork, const ShaderMap &usdShaders )
293+ {
294+ for ( const auto &shader : shaderNetwork->shaders () )
290295 {
291- pxr::UsdShadeShader usdShader = pxr::UsdShadeShader::Get ( shaderContainer. GetStage (), shaderContainer. GetPath (). AppendChild ( pxr::TfToken ( pxr::TfMakeValidIdentifier ( shader.first . string () ) ) ) );
292- for ( const auto &c : shaderNetworkWithAdapters ->inputConnections ( shader.first ) )
296+ pxr::UsdShadeConnectableAPI usdShader = usdShaders. at ( shader.first );
297+ for ( const auto &c : shaderNetwork ->inputConnections ( shader.first ) )
293298 {
294299 pxr::UsdShadeInput dest = usdShader.GetInput ( pxr::TfToken ( c.destination .name .string () ) );
295- if ( ! dest. GetPrim (). IsValid () )
300+ if ( !dest )
296301 {
297302 dest = usdShader.CreateInput ( toUSDParameterName ( c.destination .name ), pxr::SdfValueTypeNames->Token );
298303 }
299304
300- pxr::UsdShadeShader sourceUsdShader = pxr::UsdShadeShader::Get ( shaderContainer. GetStage (), shaderContainer. GetPath (). AppendChild ( pxr::TfToken ( pxr::TfMakeValidIdentifier ( c.source .shader . string () ) ) ) );
305+ pxr::UsdShadeShader sourceUsdShader = usdShaders. at ( c.source .shader );
301306 std::string sourceOutputName = c.source .name .string ();
302307 if ( sourceOutputName.size () == 0 )
303308 {
@@ -306,9 +311,51 @@ pxr::UsdShadeOutput IECoreUSD::ShaderAlgo::writeShaderNetwork( const IECoreScene
306311 pxr::UsdShadeOutput source = sourceUsdShader.CreateOutput ( pxr::TfToken ( sourceOutputName ), dest.GetTypeName () );
307312 dest.ConnectToSource ( source );
308313 }
314+ }
315+ }
316+
317+ } // namespace
318+
319+ pxr::UsdShadeOutput IECoreUSD::ShaderAlgo::writeShaderNetwork ( const IECoreScene::ShaderNetwork *shaderNetwork, pxr::UsdPrim shaderContainer )
320+ {
321+ IECoreScene::ConstShaderNetworkPtr adaptedNetwork = adaptShaderNetworkForWriting ( shaderNetwork );
322+ shaderNetwork = adaptedNetwork.get ();
309323
324+ IECoreScene::ShaderNetwork::Parameter networkOutput = shaderNetwork->getOutput ();
325+ if ( networkOutput.shader .string () == " " )
326+ {
327+ // This could theoretically happen, but a shader network with no output is not useful in any way
328+ IECore::msg (
329+ IECore::Msg::Warning, " IECoreUSD::ShaderAlgo::writeShaderNetwork" ,
330+ " No output shader in network"
331+ );
332+ }
333+
334+ ShaderMap usdShaders;
335+ pxr::UsdShadeOutput networkOutUsd;
336+ for ( const auto &shader : shaderNetwork->shaders () )
337+ {
338+ const pxr::SdfPath usdShaderPath = shaderContainer.GetPath ().AppendChild ( pxr::TfToken ( pxr::TfMakeValidIdentifier ( shader.first .string () ) ) );
339+ pxr::UsdShadeConnectableAPI usdShader = createShaderPrim ( shader.second .get (), shaderContainer.GetStage (), usdShaderPath );
340+ writeShaderParameterValues ( shader.second .get (), usdShader );
341+ usdShaders[shader.first ] = usdShader;
342+
343+ if ( networkOutput.shader == shader.first )
344+ {
345+ pxr::TfToken outName ( networkOutput.name .string () );
346+ if ( outName.GetString ().size () == 0 )
347+ {
348+ outName = pxr::TfToken ( " DEFAULT_OUTPUT" );
349+ }
350+
351+ // \todo - we should probably be correctly tracking the output type if it is typed?
352+ // Currently, we don't really track output types in Gaffer.
353+ networkOutUsd = usdShader.CreateOutput ( outName, pxr::SdfValueTypeNames->Token );
354+ }
310355 }
311356
357+ writeShaderConnections ( shaderNetwork, usdShaders );
358+
312359 return networkOutUsd;
313360}
314361
@@ -357,7 +404,63 @@ IECoreScene::ShaderNetworkPtr IECoreUSD::ShaderAlgo::readShaderNetwork( const px
357404
358405#if PXR_VERSION >= 2111
359406
360- IECoreScene::ShaderNetworkPtr IECoreUSD::ShaderAlgo::readShaderNetwork ( const pxr::UsdLuxLightAPI &light )
407+ // This is very similar to `writeShaderNetwork` but with these key differences :
408+ //
409+ // - The output shader is written as a UsdLight-derived prim rather than a UsdShadeShader.
410+ // - The other shaders are parented inside the light.
411+ // - We don't need to create a UsdShadeOutput to return.
412+ void IECoreUSD::ShaderAlgo::writeLight ( const IECoreScene::ShaderNetwork *shaderNetwork, pxr::UsdPrim prim )
413+ {
414+ IECoreScene::ConstShaderNetworkPtr adaptedNetwork = adaptShaderNetworkForWriting ( shaderNetwork );
415+ shaderNetwork = adaptedNetwork.get ();
416+
417+ // Verify that the light shader corresponds to a valid USD light type.
418+
419+ const IECoreScene::Shader *outputShader = shaderNetwork->outputShader ();
420+ if ( !outputShader )
421+ {
422+ IECore::msg ( IECore::Msg::Warning, " ShaderAlgo::writeLight" , " No output shader" );
423+ return ;
424+ }
425+
426+ pxr::TfType type = pxr::UsdSchemaRegistry::GetInstance ().GetTypeFromName ( pxr::TfToken ( outputShader->getName () ) );
427+ if (
428+ !type.IsA <pxr::UsdLuxBoundableLightBase>() &&
429+ !type.IsA <pxr::UsdLuxNonboundableLightBase>()
430+ )
431+ {
432+ IECore::msg ( IECore::Msg::Warning, " ShaderAlgo::writeLight" , boost::format ( " Shader `%1%` is not a valid UsdLux light type" ) % outputShader->getName () );
433+ return ;
434+ }
435+
436+ // Write the light itself onto the prim we've been given.
437+
438+ ShaderMap usdShaders;
439+ prim.SetTypeName ( pxr::TfToken ( outputShader->getName () ) );
440+ writeShaderParameterValues ( outputShader, pxr::UsdShadeConnectableAPI ( prim ) );
441+ usdShaders[shaderNetwork->getOutput ().shader ] = pxr::UsdShadeConnectableAPI ( prim );
442+
443+ // Then write any other shaders as child prims so they are
444+ // encapsulated within the light.
445+
446+ for ( const auto &shader : shaderNetwork->shaders () )
447+ {
448+ if ( shader.second == outputShader )
449+ {
450+ continue ;
451+ }
452+ const pxr::SdfPath usdShaderPath = prim.GetPath ().AppendChild ( pxr::TfToken ( pxr::TfMakeValidIdentifier ( shader.first .string () ) ) );
453+ pxr::UsdShadeConnectableAPI usdShader = createShaderPrim ( shader.second .get (), prim.GetStage (), usdShaderPath );
454+ writeShaderParameterValues ( shader.second .get (), usdShader );
455+ usdShaders[shader.first ] = usdShader;
456+ }
457+
458+ // Finally, connect everything up.
459+
460+ writeShaderConnections ( shaderNetwork, usdShaders );
461+ }
462+
463+ IECoreScene::ShaderNetworkPtr IECoreUSD::ShaderAlgo::readLight ( const pxr::UsdLuxLightAPI &light )
361464{
362465 IECoreScene::ShaderNetworkPtr result = new IECoreScene::ShaderNetwork ();
363466 IECoreScene::ShaderNetwork::Parameter lightHandle = readShaderNetworkWalk ( light.GetPath ().GetParentPath (), pxr::UsdShadeConnectableAPI ( light ), *result );
0 commit comments