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,15 +220,89 @@ IECoreScene::ShaderNetwork::Parameter readShaderNetworkWalk( const pxr::SdfPath
217220 }
218221}
219222
223+ IECoreScene::ConstShaderNetworkPtr adaptShaderNetworkForWriting ( const IECoreScene::ShaderNetwork *shaderNetwork )
224+ {
225+ IECoreScene::ShaderNetworkPtr result = shaderNetwork->copy ();
226+ IECoreScene::ShaderNetworkAlgo::expandSplines ( result.get () );
227+ IECoreScene::ShaderNetworkAlgo::addComponentConnectionAdapters ( result.get () );
228+ return result;
229+ }
230+
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 )
235+ {
236+ throw IECore::Exception ( " Could not create shader at " + path.GetAsString () );
237+ }
238+ const std::string type = shader->getType ();
239+ std::string typePrefix;
240+ size_t typeColonPos = type.find ( " :" );
241+ if ( typeColonPos != std::string::npos )
242+ {
243+ typePrefix = type.substr ( 0 , typeColonPos ) + " :" ;
244+ if ( typePrefix == " ai:" )
245+ {
246+ typePrefix = " arnold:" ;
247+ }
248+ }
249+ usdShader.SetShaderId ( pxr::TfToken ( typePrefix + shader->getName () ) );
250+
251+ return usdShader.ConnectableAPI ();
252+ }
253+
254+ void writeShaderParameterValues ( const IECoreScene::Shader *shader, pxr::UsdShadeConnectableAPI usdShader )
255+ {
256+ for ( const auto &p : shader->parametersData ()->readable () )
257+ {
258+ pxr::UsdShadeInput input = usdShader.CreateInput (
259+ toUSDParameterName ( p.first ),
260+ IECoreUSD::DataAlgo::valueTypeName ( p.second .get () )
261+ );
262+ input.Set ( IECoreUSD::DataAlgo::toUSD ( p.second .get () ) );
263+ }
264+
265+ const IECore::BoolData *adapterMeta = shader->blindData ()->member <IECore::BoolData>( IECoreScene::ShaderNetworkAlgo::componentConnectionAdapterLabel () );
266+ if ( adapterMeta && adapterMeta->readable () )
267+ {
268+ usdShader.GetPrim ().SetMetadata ( g_adapterLabelToken, true );
269+ }
270+ }
271+
272+ using ShaderMap = std::unordered_map<IECore::InternedString, pxr::UsdShadeConnectableAPI>;
273+ void writeShaderConnections ( const IECoreScene::ShaderNetwork *shaderNetwork, const ShaderMap &usdShaders )
274+ {
275+ for ( const auto &shader : shaderNetwork->shaders () )
276+ {
277+ pxr::UsdShadeConnectableAPI usdShader = usdShaders.at ( shader.first );
278+ for ( const auto &c : shaderNetwork->inputConnections ( shader.first ) )
279+ {
280+ pxr::UsdShadeInput dest = usdShader.GetInput ( pxr::TfToken ( c.destination .name .string () ) );
281+ if ( !dest )
282+ {
283+ dest = usdShader.CreateInput ( toUSDParameterName ( c.destination .name ), pxr::SdfValueTypeNames->Token );
284+ }
285+
286+ pxr::UsdShadeShader sourceUsdShader = usdShaders.at ( c.source .shader );
287+ std::string sourceOutputName = c.source .name .string ();
288+ if ( sourceOutputName.size () == 0 )
289+ {
290+ sourceOutputName = " DEFAULT_OUTPUT" ;
291+ }
292+ pxr::UsdShadeOutput source = sourceUsdShader.CreateOutput ( pxr::TfToken ( sourceOutputName ), dest.GetTypeName () );
293+ dest.ConnectToSource ( source );
294+ }
295+ }
296+ }
297+
220298} // namespace
221299
222300pxr::UsdShadeOutput IECoreUSD::ShaderAlgo::writeShaderNetwork ( const IECoreScene::ShaderNetwork *shaderNetwork, pxr::UsdPrim shaderContainer )
223301{
224- IECoreScene::ShaderNetworkPtr shaderNetworkWithAdapters = shaderNetwork->copy ();
225- IECoreScene::ShaderNetworkAlgo::expandSplines ( shaderNetworkWithAdapters.get () );
226- IECoreScene::ShaderNetworkAlgo::addComponentConnectionAdapters ( shaderNetworkWithAdapters.get () );
302+ IECoreScene::ConstShaderNetworkPtr adaptedNetwork = adaptShaderNetworkForWriting ( shaderNetwork );
303+ shaderNetwork = adaptedNetwork.get ();
227304
228- IECoreScene::ShaderNetwork::Parameter networkOutput = shaderNetworkWithAdapters ->getOutput ();
305+ IECoreScene::ShaderNetwork::Parameter networkOutput = shaderNetwork ->getOutput ();
229306 if ( networkOutput.shader .string () == " " )
230307 {
231308 // This could theoretically happen, but a shader network with no output is not useful in any way
@@ -235,36 +312,14 @@ pxr::UsdShadeOutput IECoreUSD::ShaderAlgo::writeShaderNetwork( const IECoreScene
235312 );
236313 }
237314
315+ ShaderMap usdShaders;
238316 pxr::UsdShadeOutput networkOutUsd;
239- for ( const auto &shader : shaderNetworkWithAdapters ->shaders () )
317+ for ( const auto &shader : shaderNetwork ->shaders () )
240318 {
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 )
244- {
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- }
257- }
258- usdShader.SetShaderId ( pxr::TfToken ( typePrefix + shader.second ->getName () ) );
259-
260- for ( const auto &p : shader.second ->parametersData ()->readable () )
261- {
262- pxr::UsdShadeInput input = usdShader.CreateInput (
263- toUSDParameterName ( p.first ),
264- DataAlgo::valueTypeName ( p.second .get () )
265- );
266- input.Set ( DataAlgo::toUSD ( p.second .get () ) );
267- }
319+ const pxr::SdfPath usdShaderPath = shaderContainer.GetPath ().AppendChild ( pxr::TfToken ( pxr::TfMakeValidIdentifier ( shader.first .string () ) ) );
320+ pxr::UsdShadeConnectableAPI usdShader = createShaderPrim ( shader.second .get (), shaderContainer.GetStage (), usdShaderPath );
321+ writeShaderParameterValues ( shader.second .get (), usdShader );
322+ usdShaders[shader.first ] = usdShader;
268323
269324 if ( networkOutput.shader == shader.first )
270325 {
@@ -278,36 +333,9 @@ pxr::UsdShadeOutput IECoreUSD::ShaderAlgo::writeShaderNetwork( const IECoreScene
278333 // Currently, we don't really track output types in Gaffer.
279334 networkOutUsd = usdShader.CreateOutput ( outName, pxr::SdfValueTypeNames->Token );
280335 }
281-
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- }
287336 }
288337
289- for ( const auto &shader : shaderNetworkWithAdapters->shaders () )
290- {
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 ) )
293- {
294- pxr::UsdShadeInput dest = usdShader.GetInput ( pxr::TfToken ( c.destination .name .string () ) );
295- if ( ! dest.GetPrim ().IsValid () )
296- {
297- dest = usdShader.CreateInput ( toUSDParameterName ( c.destination .name ), pxr::SdfValueTypeNames->Token );
298- }
299-
300- pxr::UsdShadeShader sourceUsdShader = pxr::UsdShadeShader::Get ( shaderContainer.GetStage (), shaderContainer.GetPath ().AppendChild ( pxr::TfToken ( pxr::TfMakeValidIdentifier ( c.source .shader .string () ) ) ) );
301- std::string sourceOutputName = c.source .name .string ();
302- if ( sourceOutputName.size () == 0 )
303- {
304- sourceOutputName = " DEFAULT_OUTPUT" ;
305- }
306- pxr::UsdShadeOutput source = sourceUsdShader.CreateOutput ( pxr::TfToken ( sourceOutputName ), dest.GetTypeName () );
307- dest.ConnectToSource ( source );
308- }
309-
310- }
338+ writeShaderConnections ( shaderNetwork, usdShaders );
311339
312340 return networkOutUsd;
313341}
@@ -357,7 +385,63 @@ IECoreScene::ShaderNetworkPtr IECoreUSD::ShaderAlgo::readShaderNetwork( const px
357385
358386#if PXR_VERSION >= 2111
359387
360- IECoreScene::ShaderNetworkPtr IECoreUSD::ShaderAlgo::readShaderNetwork ( const pxr::UsdLuxLightAPI &light )
388+ // This is very similar to `writeShaderNetwork` but with these key differences :
389+ //
390+ // - The output shader is written as a UsdLight-derived prim rather than a UsdShadeShader.
391+ // - The other shaders are parented inside the light.
392+ // - We don't need to create a UsdShadeOutput to return.
393+ void IECoreUSD::ShaderAlgo::writeLight ( const IECoreScene::ShaderNetwork *shaderNetwork, pxr::UsdPrim prim )
394+ {
395+ IECoreScene::ConstShaderNetworkPtr adaptedNetwork = adaptShaderNetworkForWriting ( shaderNetwork );
396+ shaderNetwork = adaptedNetwork.get ();
397+
398+ // Verify that the light shader corresponds to a valid USD light type.
399+
400+ const IECoreScene::Shader *outputShader = shaderNetwork->outputShader ();
401+ if ( !outputShader )
402+ {
403+ IECore::msg ( IECore::Msg::Warning, " ShaderAlgo::writeLight" , " No output shader" );
404+ return ;
405+ }
406+
407+ pxr::TfType type = pxr::UsdSchemaRegistry::GetInstance ().GetTypeFromName ( pxr::TfToken ( outputShader->getName () ) );
408+ if (
409+ !type.IsA <pxr::UsdLuxBoundableLightBase>() &&
410+ !type.IsA <pxr::UsdLuxNonboundableLightBase>()
411+ )
412+ {
413+ IECore::msg ( IECore::Msg::Warning, " ShaderAlgo::writeLight" , boost::format ( " Shader `%1%` is not a valid UsdLux light type" ) % outputShader->getName () );
414+ return ;
415+ }
416+
417+ // Write the light itself onto the prim we've been given.
418+
419+ ShaderMap usdShaders;
420+ prim.SetTypeName ( pxr::TfToken ( outputShader->getName () ) );
421+ writeShaderParameterValues ( outputShader, pxr::UsdShadeConnectableAPI ( prim ) );
422+ usdShaders[shaderNetwork->getOutput ().shader ] = pxr::UsdShadeConnectableAPI ( prim );
423+
424+ // Then write any other shaders as child prims so they are
425+ // encapsulated within the light.
426+
427+ for ( const auto &shader : shaderNetwork->shaders () )
428+ {
429+ if ( shader.second == outputShader )
430+ {
431+ continue ;
432+ }
433+ const pxr::SdfPath usdShaderPath = prim.GetPath ().AppendChild ( pxr::TfToken ( pxr::TfMakeValidIdentifier ( shader.first .string () ) ) );
434+ pxr::UsdShadeConnectableAPI usdShader = createShaderPrim ( shader.second .get (), prim.GetStage (), usdShaderPath );
435+ writeShaderParameterValues ( shader.second .get (), usdShader );
436+ usdShaders[shader.first ] = usdShader;
437+ }
438+
439+ // Finally, connect everything up.
440+
441+ writeShaderConnections ( shaderNetwork, usdShaders );
442+ }
443+
444+ IECoreScene::ShaderNetworkPtr IECoreUSD::ShaderAlgo::readLight ( const pxr::UsdLuxLightAPI &light )
361445{
362446 IECoreScene::ShaderNetworkPtr result = new IECoreScene::ShaderNetwork ();
363447 IECoreScene::ShaderNetwork::Parameter lightHandle = readShaderNetworkWalk ( light.GetPath ().GetParentPath (), pxr::UsdShadeConnectableAPI ( light ), *result );
0 commit comments