Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libs/common/constant_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ ASTR(plugin_searchpath);
ASTR(point_light);
ASTR(points);
ASTR(polymesh);
ASTR(portal_light);
ASTR(procedural_custom);
ASTR(procedural_searchpath);
ASTR(progressive);
Expand Down
31 changes: 27 additions & 4 deletions plugins/node_registry/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ TF_DEFINE_PRIVATE_TOKENS(_tokens,
(uisoftmax)
(enumValues)
(attrsOrder)
(nodeType)
(usdSchema)
(light)
(binary));
// clang-format on

Expand Down Expand Up @@ -408,21 +411,41 @@ ShaderNodeUniquePtr NdrArnoldParserPlugin::PARSE_FUNC(const ShaderNodeDiscoveryR
_ReadShaderAttribute(attr, properties, "");
}

// Resolve the SdrShaderNode context. The discovery plugin emits
// `discoveryResult.discoveryType == "arnold"` so the parser plugin is
// routed correctly (see GetDiscoveryTypes() below), but the *context*
// we register the node under should reflect its role in a USD material
// network. Lights tagged with `nodeType == "light"` in customData are
// registered under SdrNodeContext->Light ("light") so DCC-agnostic
// tools can enumerate them via `Sdr.Registry.GetShaderNodesByContext("light")`.
// Everything else stays under the legacy "arnold" context for backwards
// compatibility with existing consumers.
TfToken context = discoveryResult.discoveryType;
auto nodeTypeIt = primCustomData.find(_tokens->nodeType);
if (nodeTypeIt != primCustomData.end() &&
nodeTypeIt->second.IsHolding<std::string>() &&
nodeTypeIt->second.UncheckedGet<std::string>() == _tokens->light.GetString()) {
context = _tokens->light;
}

// Now handle the metadatas at the node level
ShaderTokenMap metadata;
for (const auto &it : primCustomData) {
// uigroups was handled above
if (it.first == _tokens->uigroups || it.first == _tokens->attrsOrder)
// uigroups was handled above; nodeType is an internal routing marker
// (already consumed to compute `context`) and must not leak into the
// user-visible metadata map.
if (it.first == _tokens->uigroups || it.first == _tokens->attrsOrder ||
it.first == _tokens->nodeType)
continue;
metadata.insert({TfToken(it.first), TfStringify(it.second)});
}

return ShaderNodeUniquePtr(new SdrShaderNode(
discoveryResult.identifier, // identifier
discoveryResult.version, // version
discoveryResult.name, // name
discoveryResult.family, // family
discoveryResult.discoveryType, // context
context, // context
discoveryResult.sourceType, // sourceType
discoveryResult.uri, // uri
discoveryResult.uri, // resolvedUri
Expand Down
39 changes: 38 additions & 1 deletion plugins/node_registry/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,37 @@ TF_DEFINE_PRIVATE_TOKENS(_tokens,
(uisoftmax)
(enumValues)
(attrsOrder)
(nodeType)
(usdSchema)
(light)
);
// clang-format on

namespace {

// Maps an Arnold light node entry name to the corresponding stock USD light
// prim type (UsdLux / UsdImaging). Recorded as the "usdSchema" node-level
// metadata on the registered SdrShaderNode so DCC-agnostic tools can write
// arnold lights to USD layers without guessing. Lights not in this table
// (e.g. third-party lights surfaced via AI_NODE_LIGHT) are still registered
// under context "light" but without a usdSchema hint.
const std::unordered_map<std::string, std::string>& _GetArnoldLightToUsdPrimType()
{
static const std::unordered_map<std::string, std::string> ret = {
{"distant_light", "DistantLight"},
{"disk_light", "DiskLight"},
{"quad_light", "RectLight"},
{"cylinder_light", "CylinderLight"},
{"skydome_light", "DomeLight"},
{"point_light", "SphereLight"},
{"spot_light", "SphereLight"},
{"photometric_light", "SphereLight"},
{"mesh_light", "GeometryLight"},
{"portal_light", "PortalLight"},
};
return ret;
}

// TODO(pal): All this should be moved to a schema API.

// The conversion classes store both the sdf type and a simple function pointer
Expand Down Expand Up @@ -411,6 +437,16 @@ void _ReadArnoldShaderDef(UsdStageRefPtr stage, const AtNodeEntry* nodeEntry)
} else if (nodeEntryType == AI_NODE_IMAGER || nodeEntryType == AI_NODE_OPERATOR) {
// create an output type for imagers
prim.CreateAttribute(_tokens->output, SdfValueTypeNames->String, false);
} else if (nodeEntryType == AI_NODE_LIGHT) {
// Tag the prim so the parser registers it under SdrNodeContext->Light
// and record the corresponding stock USD light prim type, if any.
primCustomData[_tokens->nodeType] = _tokens->light.GetString();
const auto& usdPrimTypeMap = _GetArnoldLightToUsdPrimType();
const std::string nodeName = AiNodeEntryGetName(nodeEntry);
auto it = usdPrimTypeMap.find(nodeName);
if (it != usdPrimTypeMap.end()) {
primCustomData[_tokens->usdSchema] = it->second;
}
}

VtArray<std::string> attrsOrder;
Expand Down Expand Up @@ -567,7 +603,8 @@ UsdStageRefPtr NodeRegistryArnoldGetShaderDefs()
#endif
}

auto* nodeIter = AiUniverseGetNodeEntryIterator(AI_NODE_SHADER | AI_NODE_IMAGER | AI_NODE_OPERATOR);
auto* nodeIter = AiUniverseGetNodeEntryIterator(
AI_NODE_SHADER | AI_NODE_IMAGER | AI_NODE_OPERATOR | AI_NODE_LIGHT);

while (!AiNodeEntryIteratorFinished(nodeIter)) {
_ReadArnoldShaderDef(stage, AiNodeEntryIteratorGetNext(nodeIter));
Expand Down