From 643f962d8fcebbc44a7e47113e07c53d50c424ff Mon Sep 17 00:00:00 2001 From: Jakub Svoboda <132791205+rolledhand@users.noreply.github.com> Date: Sun, 31 May 2026 20:04:34 +0200 Subject: [PATCH 1/2] Houdini 20.5: expose MoonRay Sdr Python stubs Signed-off-by: Jakub Svoboda <132791205+rolledhand@users.noreply.github.com> --- CMakeLists.txt | 4 ++++ moonrayShaderDiscovery/CMakeLists.txt | 3 +++ moonrayShaderParser/CMakeLists.txt | 3 +++ sitecustomize.py | 19 +++++++++++++++++++ 4 files changed, 29 insertions(+) create mode 100644 sitecustomize.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 2af290a..961c5ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,3 +20,7 @@ find_package(Python REQUIRED COMPONENTS Development) add_subdirectory(moonrayShaderDiscovery) add_subdirectory(moonrayShaderParser) + +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/sitecustomize.py + DESTINATION lib/python +) diff --git a/moonrayShaderDiscovery/CMakeLists.txt b/moonrayShaderDiscovery/CMakeLists.txt index b262d79..91d4e57 100644 --- a/moonrayShaderDiscovery/CMakeLists.txt +++ b/moonrayShaderDiscovery/CMakeLists.txt @@ -72,3 +72,6 @@ install(FILES ${plugInfoFile} DESTINATION plugin/pxr/moonrayShaderDiscovery ) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py + DESTINATION lib/python/pxr/MoonrayShaderDiscovery +) diff --git a/moonrayShaderParser/CMakeLists.txt b/moonrayShaderParser/CMakeLists.txt index a5a2365..709a2f8 100644 --- a/moonrayShaderParser/CMakeLists.txt +++ b/moonrayShaderParser/CMakeLists.txt @@ -72,4 +72,7 @@ install(FILES ${plugInfoFile} DESTINATION plugin/pxr/moonrayShaderParser ) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py + DESTINATION lib/python/pxr/MoonrayShaderParser +) diff --git a/sitecustomize.py b/sitecustomize.py new file mode 100644 index 0000000..538df21 --- /dev/null +++ b/sitecustomize.py @@ -0,0 +1,19 @@ +"""Expose MoonRay's USD Python plugin stubs to Houdini's pxr package. + +Houdini ships pxr as a regular Python package instead of a namespace package, so +adding MoonRay's lib/python directory to PYTHONPATH is not enough for imports +such as pxr.MoonrayShaderParser. The Sdr plugins are native USD plugins; these +small Python stubs only prevent Plug from reporting missing Python modules when +the native parser/discovery plugins are loaded. +""" + +import os + +try: + import pxr + + moonray_pxr_path = os.path.join(os.path.dirname(__file__), "pxr") + if hasattr(pxr, "__path__") and moonray_pxr_path not in pxr.__path__: + pxr.__path__.append(moonray_pxr_path) +except Exception: + pass From 3c7f9b65df9fb8f05f037e2a9cb2aa625e434113 Mon Sep 17 00:00:00 2001 From: Jakub Svoboda <132791205+rolledhand@users.noreply.github.com> Date: Sat, 20 Jun 2026 18:17:11 +0200 Subject: [PATCH 2/2] Fix MoonRay SDR type metadata for Houdini 20.5 Signed-off-by: Jakub Svoboda <132791205+rolledhand@users.noreply.github.com> --- README.md | 27 ++++++++++++ moonrayShaderParser/parserPlugin.cpp | 62 ++++++++++++++++++++++++---- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 1a3c57e..8598e37 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,30 @@ which is required to use them as shader nodes in USD/Hydra. This repository is part of the larger MoonRay/Arras codebase. It is included as a submodule in the top-level OpenMoonRay repository located here: [OpenMoonRay](https://github.com/dreamworksanimation/openmoonray) + +## Houdini 20.5 SDR Type Contract + +The MoonRay parser maps RDL attribute metadata into Sdr properties consumed by +Houdini, Hydra, and USD shader discovery. For Houdini 20.5 the parser must +provide an Sdf type and default value with matching shape for every mapped RDL +type. + +Important mappings: + +- `Bool` and `BoolVector` keep real boolean defaults. +- `Rgb` / `Rgba` map to `color3f` / `color4f`. +- `RgbVector` / `RgbaVector` map to `color3f[]` / `color4f[]` with dynamic + array metadata. +- Numeric vector arrays map to `float2[]`, `float3[]`, `float4[]`, + `double2[]`, `double3[]`, and `double4[]`. +- `Mat4f` / `Mat4d` and their vector forms use `matrix4d` / `matrix4d[]`, + because Houdini 20.5 does not expose a valid `matrix4f` Sdf type. +- Scene object pointer/indexable vectors use token-shaped fallbacks. + +Ramp-like MoonRay attributes preserve the coredata grouping metadata +`structure_name`, `structure_path`, and `structure_type` in the Sdr property +metadata. This keeps light-filter and material ramps discoverable as grouped +ramps while preserving valid Sdf/default shapes. For example, +`ColorRampLightFilter.colors` and Dwa material `iridescence_colors` are dynamic +`color3f[]` arrays, while Rod/VDB light-filter non-color ramp values are dynamic +`float[]` arrays and their interpolation controls are dynamic `int[]` arrays. diff --git a/moonrayShaderParser/parserPlugin.cpp b/moonrayShaderParser/parserPlugin.cpp index 2e6bf88..5004064 100644 --- a/moonrayShaderParser/parserPlugin.cpp +++ b/moonrayShaderParser/parserPlugin.cpp @@ -88,7 +88,7 @@ VtValue convertVector(const JsValue& val, VtValue convertDefault(const JsValue& val, const std::string& attrType) { - if (attrType == "Bool") return VtValue(val.GetBool() ? 0 : 1); + if (attrType == "Bool") return VtValue(val.GetBool()); if (attrType == "Int") return VtValue(val.GetInt()); if (attrType == "Long") return VtValue(val.GetInt64()); if (attrType == "Float") return VtValue((float)val.GetReal()); @@ -124,7 +124,7 @@ VtValue convertDefault(const JsValue& val, for (const JsValue& val : arr) { data.emplace_back(val.GetArrayOf()); } - return VtValue(GfMatrix4f(data)); + return VtValue(GfMatrix4d(data)); } if (attrType == "Mat4d") { std::vector> data; @@ -138,7 +138,7 @@ VtValue convertDefault(const JsValue& val, // can't initialize to anything except null return VtValue(nullSceneObjectPtr); } - if (attrType == "BoolVector") return convertVector(val,"Bool"); + if (attrType == "BoolVector") return convertVector(val,"Bool"); if (attrType == "IntVector") return convertVector(val,"Int"); if (attrType == "LongVector") return convertVector(val,"Long"); if (attrType == "FloatVector") return convertVector(val,"Float"); @@ -146,14 +146,13 @@ VtValue convertDefault(const JsValue& val, if (attrType == "StringVector") return convertVector(val,"String"); if (attrType == "RgbVector") return convertVector(val,"Rgb"); if (attrType == "Vec3fVector") return convertVector(val,"Vec3f"); - if (attrType == "RgbaVector") return convertVector(val,"Rgba"); - if (attrType == "Vec4f") return convertVector(val,"Vec4f"); + if (attrType == "RgbaVector") return convertVector(val,"Rgba"); if (attrType == "Vec2fVector") return convertVector(val,"Vec2f"); if (attrType == "Vec2dVector") return convertVector(val,"Vec2d"); if (attrType == "Vec3dVector") return convertVector(val,"Vec3d"); if (attrType == "Vec4fVector") return convertVector(val,"Vec4f"); if (attrType == "Vec4dVector") return convertVector(val,"Vec4d"); - if (attrType == "Mat4fVector") return convertVector(val,"Mat4f"); + if (attrType == "Mat4fVector") return convertVector(val,"Mat4f"); if (attrType == "Mat4dVector") return convertVector(val,"Mat4d"); if (attrType == "SceneObjectVector" || attrType == "SceneObjectIndexable") return convertVector(val,"SceneObject"); @@ -165,6 +164,9 @@ VtValue convertVector(const JsValue& val, const std::string& baseType) { VtArray arrayOut; + if (val.IsNull()) { + return VtValue(arrayOut); + } const JsArray& arrayIn = val.GetJsArray(); for (const JsValue& elem : arrayIn) { VtValue vtElem = convertDefault(elem,baseType); @@ -175,10 +177,44 @@ VtValue convertVector(const JsValue& val, bool isDynamicVector(const std::string& type) { + if (type == "SceneObjectIndexable") { + return true; + } return (type.size() > 6) && (type.substr(type.size()-6,std::string::npos) == "Vector"); } +TfToken getSdfType(const std::string& attrType) +{ + if (attrType == "Bool") return TfToken("bool"); + if (attrType == "Long") return TfToken("int64"); + if (attrType == "Double") return TfToken("double"); + if (attrType == "Rgb") return TfToken("color3f"); + if (attrType == "Rgba") return TfToken("color4f"); + if (attrType == "Vec2d") return TfToken("double2"); + if (attrType == "Vec3d") return TfToken("double3"); + if (attrType == "Vec4d") return TfToken("double4"); + if (attrType == "Mat4f" || attrType == "Mat4d") return TfToken("matrix4d"); + if (attrType == "SceneObject*") return TfToken("token"); + if (attrType == "BoolVector") return TfToken("bool[]"); + if (attrType == "IntVector") return TfToken("int[]"); + if (attrType == "LongVector") return TfToken("int64[]"); + if (attrType == "FloatVector") return TfToken("float[]"); + if (attrType == "DoubleVector") return TfToken("double[]"); + if (attrType == "StringVector") return TfToken("string[]"); + if (attrType == "RgbVector") return TfToken("color3f[]"); + if (attrType == "RgbaVector") return TfToken("color4f[]"); + if (attrType == "Vec2fVector") return TfToken("float2[]"); + if (attrType == "Vec3fVector") return TfToken("float3[]"); + if (attrType == "Vec4fVector") return TfToken("float4[]"); + if (attrType == "Vec2dVector") return TfToken("double2[]"); + if (attrType == "Vec3dVector") return TfToken("double3[]"); + if (attrType == "Vec4dVector") return TfToken("double4[]"); + if (attrType == "Mat4fVector" || attrType == "Mat4dVector") return TfToken("matrix4d[]"); + if (attrType == "SceneObjectVector" || attrType == "SceneObjectIndexable") return TfToken("token[]"); + return TfToken(); +} + const TfToken getNodeContext(const JsObject& definition) { std::string nodeType = definition.at("type").GetString(); @@ -262,6 +298,12 @@ getNodeProperties(const NdrNodeDiscoveryResult& discoveryResult, if (mdIt != attrMetadata.end()) metadata[SdrPropertyMetadata->Label] = mdIt->second.GetString(); mdIt = attrMetadata.find("comment"); if (mdIt != attrMetadata.end()) metadata[SdrPropertyMetadata->Help] = mdIt->second.GetString(); + mdIt = attrMetadata.find("structure_name"); + if (mdIt != attrMetadata.end()) metadata[TfToken("structure_name")] = mdIt->second.GetString(); + mdIt = attrMetadata.find("structure_path"); + if (mdIt != attrMetadata.end()) metadata[TfToken("structure_path")] = mdIt->second.GetString(); + mdIt = attrMetadata.find("structure_type"); + if (mdIt != attrMetadata.end()) metadata[TfToken("structure_type")] = mdIt->second.GetString(); } // "page" metadata is set from group name @@ -270,8 +312,14 @@ getNodeProperties(const NdrNodeDiscoveryResult& discoveryResult, metadata[SdrPropertyMetadata->Page] = groupIt->second; } - if (isDynamicVector(attrType)) + const TfToken sdfType = getSdfType(attrType); + if (!sdfType.IsEmpty()) { + metadata[SdrPropertyMetadata->SdrUsdDefinitionType] = sdfType; + } + + if (isDynamicVector(attrType)) { metadata[SdrPropertyMetadata->IsDynamicArray] = TfToken("true"); + } auto bindIt = attrData.find("bindable"); if (bindIt != attrData.end() &&