Fix OpenPBR Material info not being dumped in DumpRenderScene

Deprecated old DumpRenderScene and related dump functions in render-data.cc
that didn't include OpenPBR shader output. The newer implementation in
render-scene-dump.cc already has proper support for dumping both
UsdPreviewSurface and OpenPBR materials.

The old DumpMaterial function only dumped surfaceShader field, missing
the openPBRShader field entirely. Now DumpRenderScene correctly outputs
OpenPBR material parameters including:
- Base layer (weight, color, roughness, metalness)
- Specular layer (weight, color, roughness, ior)
- Coat layer (weight, color, roughness)
- Emission (luminance, color)
- Transmission (weight)
- Subsurface (weight, color)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Syoyo Fujita
2025-11-12 12:59:54 +09:00
parent db55d902b5
commit 184d446c9a
7 changed files with 778 additions and 11 deletions

218
models/cube-mtlx-texture.usda Executable file
View File

@@ -0,0 +1,218 @@
#usda 1.0
(
defaultPrim = "root"
doc = "Blender v4.5.4 LTS"
metersPerUnit = 1
upAxis = "Z"
)
def Xform "root" (
customData = {
dictionary Blender = {
bool generated = 1
}
}
)
{
def Xform "Cube"
{
custom string userProperties:blender:object_name = "Cube"
def Mesh "Cube" (
active = true
prepend apiSchemas = ["MaterialBindingAPI"]
)
{
uniform bool doubleSided = 1
float3[] extent = [(-1, -1, -1), (1, 1, 1)]
int[] faceVertexCounts = [4, 4, 4, 4, 4, 4]
int[] faceVertexIndices = [0, 4, 6, 2, 3, 2, 6, 7, 7, 6, 4, 5, 5, 1, 3, 7, 1, 0, 2, 3, 5, 4, 0, 1]
rel material:binding = </root/_materials/Material>
normal3f[] normals = [(0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, -1, 0), (0, -1, 0), (0, -1, 0), (0, -1, 0), (-1, 0, 0), (-1, 0, 0), (-1, 0, 0), (-1, 0, 0), (0, 0, -1), (0, 0, -1), (0, 0, -1), (0, 0, -1), (1, 0, 0), (1, 0, 0), (1, 0, 0), (1, 0, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0)] (
interpolation = "faceVarying"
)
point3f[] points = [(1, 1, 1), (1, 1, -1), (1, -1, 1), (1, -1, -1), (-1, 1, 1), (-1, 1, -1), (-1, -1, 1), (-1, -1, -1)]
texCoord2f[] primvars:st = [(0.625, 0.5), (0.875, 0.5), (0.875, 0.75), (0.625, 0.75), (0.375, 0.75), (0.625, 0.75), (0.625, 1), (0.375, 1), (0.375, 0), (0.625, 0), (0.625, 0.25), (0.375, 0.25), (0.125, 0.5), (0.375, 0.5), (0.375, 0.75), (0.125, 0.75), (0.375, 0.5), (0.625, 0.5), (0.625, 0.75), (0.375, 0.75), (0.375, 0.25), (0.625, 0.25), (0.625, 0.5), (0.375, 0.5)] (
interpolation = "faceVarying"
)
uniform token subdivisionScheme = "none"
custom string userProperties:blender:data_name = "Cube"
}
}
def Scope "_materials"
{
def Material "Material" (
prepend apiSchemas = ["MaterialXConfigAPI"]
)
{
string config:mtlx:version = "1.39"
token outputs:mtlx:surface.connect = </root/_materials/Material/Principled_BSDF_mtlx1.outputs:surface>
token outputs:surface.connect = </root/_materials/Material/Principled_BSDF.outputs:surface>
custom string userProperties:blender:data_name = "Material"
def Shader "Principled_BSDF"
{
uniform token info:id = "UsdPreviewSurface"
float inputs:clearcoat = 0
float inputs:clearcoatRoughness = 0.03
color3f inputs:diffuseColor.connect = </root/_materials/Material/Image_Texture.outputs:rgb>
float inputs:ior = 1.45
float inputs:metallic = 0
float inputs:opacity = 1
float inputs:roughness = 0.5
float inputs:specular = 0.5
token outputs:surface
}
def Shader "Image_Texture"
{
uniform token info:id = "UsdUVTexture"
asset inputs:file = @./textures/texture-cat.jpg@
token inputs:sourceColorSpace = "sRGB"
float2 inputs:st.connect = </root/_materials/Material/uvmap.outputs:result>
token inputs:wrapS = "repeat"
token inputs:wrapT = "repeat"
float3 outputs:rgb
}
def Shader "uvmap"
{
uniform token info:id = "UsdPrimvarReader_float2"
string inputs:varname = "st"
float2 outputs:result
}
def Shader "Principled_BSDF_mtlx1"
{
uniform token info:id = "ND_open_pbr_surface_surfaceshader"
color3f inputs:base_color.connect = </root/_materials/Material/NodeGraphs.outputs:node_out>
float inputs:base_diffuse_roughness = 0
float inputs:base_metalness = 0
float inputs:base_weight = 1
color3f inputs:coat_color = (1, 1, 1)
float inputs:coat_darkening
float inputs:coat_ior = 1.5
float inputs:coat_roughness = 0.03
float inputs:coat_roughness_anisotropy
float inputs:coat_weight = 0
color3f inputs:emission_color = (1, 1, 1)
float inputs:emission_luminance = 0
color3f inputs:fuzz_color = (1, 1, 1)
float inputs:fuzz_roughness = 0.5
float inputs:fuzz_weight = 0
float3 inputs:geometry_coat_normal
float3 inputs:geometry_coat_tangent
float3 inputs:geometry_normal
float inputs:geometry_opacity = 1
float3 inputs:geometry_tangent.connect = </root/_materials/Material/NodeGraphs.outputs:node_004_out>
bool inputs:geometry_thin_walled
color3f inputs:specular_color = (1, 1, 1)
float inputs:specular_ior = 1.45
float inputs:specular_roughness = 0.5
float inputs:specular_roughness_anisotropy = 0
float inputs:specular_weight = 1
color3f inputs:subsurface_color.connect = </root/_materials/Material/NodeGraphs.outputs:node_out>
float inputs:subsurface_radius = 0.05
color3f inputs:subsurface_radius_scale = (1, 0.2, 0.1)
float inputs:subsurface_scatter_anisotropy = 0
float inputs:subsurface_weight = 0
float inputs:thin_film_ior = 1.33
float inputs:thin_film_thickness = 0
float inputs:thin_film_weight = 0
color3f inputs:transmission_color.connect = </root/_materials/Material/NodeGraphs.outputs:node_out>
float inputs:transmission_depth
float inputs:transmission_dispersion_abbe_number
float inputs:transmission_dispersion_scale
color3f inputs:transmission_scatter
float inputs:transmission_scatter_anisotropy
float inputs:transmission_weight = 0
token outputs:surface
}
def NodeGraph "NodeGraphs"
{
float3 outputs:node_004_out.connect = </root/_materials/Material/NodeGraphs/node_004.outputs:out>
color3f outputs:node_out.connect = </root/_materials/Material/NodeGraphs/node.outputs:out>
def Shader "node_texcoord"
{
uniform token info:id = "ND_texcoord_vector2"
float2 outputs:out
}
def Shader "Image_Texture_Color"
{
uniform token info:id = "ND_image_color4"
asset inputs:file = @./textures/texture-cat.jpg@ (
colorSpace = "srgb_texture"
)
string inputs:filtertype = "linear"
float2 inputs:texcoord.connect = </root/_materials/Material/NodeGraphs/node_texcoord.outputs:out>
string inputs:uaddressmode = "periodic"
string inputs:vaddressmode = "periodic"
color4f outputs:out
}
def Shader "node"
{
uniform token info:id = "ND_convert_color4_color3"
color4f inputs:in.connect = </root/_materials/Material/NodeGraphs/Image_Texture_Color.outputs:out>
color3f outputs:out
}
def Shader "node_001"
{
uniform token info:id = "ND_normal_vector3"
string inputs:space = "world"
float3 outputs:out
}
def Shader "node_002"
{
uniform token info:id = "ND_normalize_vector3"
float3 inputs:in.connect = </root/_materials/Material/NodeGraphs/node_001.outputs:out>
float3 outputs:out
}
def Shader "node_003"
{
uniform token info:id = "ND_tangent_vector3"
string inputs:space = "world"
float3 outputs:out
}
def Shader "node_004"
{
uniform token info:id = "ND_normalize_vector3"
float3 inputs:in.connect = </root/_materials/Material/NodeGraphs/node_003.outputs:out>
float3 outputs:out
}
def Shader "node_005"
{
uniform token info:id = "ND_rotate3d_vector3"
float inputs:amount = -90
float3 inputs:axis.connect = </root/_materials/Material/NodeGraphs/node_002.outputs:out>
float3 inputs:in.connect = </root/_materials/Material/NodeGraphs/node_004.outputs:out>
float3 outputs:out
}
def Shader "node_006"
{
uniform token info:id = "ND_normalize_vector3"
float3 inputs:in.connect = </root/_materials/Material/NodeGraphs/node_005.outputs:out>
float3 outputs:out
}
}
}
}
def DomeLight "env_light"
{
float inputs:intensity = 1
asset inputs:texture:file = @.\textures\color_121212.hdr@
float3 xformOp:rotateXYZ = (90, 1.2722219e-14, 90)
uniform token[] xformOpOrder = ["xformOp:rotateXYZ"]
}
}

View File

@@ -35,6 +35,9 @@
# pragma clang diagnostic ignored "-Wunused-label"
# pragma clang diagnostic ignored "-Wunused-macros"
# pragma clang diagnostic ignored "-Wunused-variable"
# pragma clang diagnostic ignored "-Wreserved-macro-identifier"
# pragma clang diagnostic ignored "-Wold-style-cast"
# pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
#elif defined(__GNUC__)
# pragma GCC diagnostic ignored "-Wunused-function"
# pragma GCC diagnostic ignored "-Wunused-parameter"

View File

@@ -1401,7 +1401,9 @@ std::string print_prop(const Property &prop, const std::string &prop_name,
// timeSamples and connect cannot have attrMeta
//
if (attr.metas().authored() || attr.has_value()) {
// Print attribute if it has metadata, has a value, OR is just typed
// NOTE: Some attributes (like outputs:out) may be typed but not have a value
if (attr.metas().authored() || attr.has_value() || !attr.type_name().empty()) {
ss << pprint::Indent(indent);
@@ -4065,6 +4067,86 @@ static std::string print_shader_params(const UsdUVTexture &shader,
return ss.str();
}
static std::string print_shader_params(const MtlxOpenPBRSurface &shader,
const uint32_t indent) {
std::stringstream ss;
// Base properties
ss << print_typed_attr(shader.base_weight, "inputs:base_weight", indent);
ss << print_typed_attr(shader.base_color, "inputs:base_color", indent);
ss << print_typed_attr(shader.base_metalness, "inputs:base_metalness", indent);
ss << print_typed_attr(shader.base_diffuse_roughness, "inputs:base_diffuse_roughness", indent);
// Specular properties
ss << print_typed_attr(shader.specular_weight, "inputs:specular_weight", indent);
ss << print_typed_attr(shader.specular_color, "inputs:specular_color", indent);
ss << print_typed_attr(shader.specular_roughness, "inputs:specular_roughness", indent);
ss << print_typed_attr(shader.specular_ior, "inputs:specular_ior", indent);
ss << print_typed_attr(shader.specular_anisotropy, "inputs:specular_anisotropy", indent);
ss << print_typed_attr(shader.specular_rotation, "inputs:specular_rotation", indent);
ss << print_typed_attr(shader.specular_roughness_anisotropy, "inputs:specular_roughness_anisotropy", indent);
// Transmission properties
ss << print_typed_attr(shader.transmission_weight, "inputs:transmission_weight", indent);
ss << print_typed_attr(shader.transmission_color, "inputs:transmission_color", indent);
ss << print_typed_attr(shader.transmission_depth, "inputs:transmission_depth", indent);
ss << print_typed_attr(shader.transmission_scatter, "inputs:transmission_scatter", indent);
ss << print_typed_attr(shader.transmission_scatter_anisotropy, "inputs:transmission_scatter_anisotropy", indent);
ss << print_typed_attr(shader.transmission_dispersion, "inputs:transmission_dispersion", indent);
ss << print_typed_attr(shader.transmission_dispersion_abbe_number, "inputs:transmission_dispersion_abbe_number", indent);
ss << print_typed_attr(shader.transmission_dispersion_scale, "inputs:transmission_dispersion_scale", indent);
// Subsurface properties
ss << print_typed_attr(shader.subsurface_weight, "inputs:subsurface_weight", indent);
ss << print_typed_attr(shader.subsurface_color, "inputs:subsurface_color", indent);
ss << print_typed_attr(shader.subsurface_radius, "inputs:subsurface_radius", indent);
ss << print_typed_attr(shader.subsurface_radius_scale, "inputs:subsurface_radius_scale", indent);
ss << print_typed_attr(shader.subsurface_scale, "inputs:subsurface_scale", indent);
ss << print_typed_attr(shader.subsurface_anisotropy, "inputs:subsurface_anisotropy", indent);
ss << print_typed_attr(shader.subsurface_scatter_anisotropy, "inputs:subsurface_scatter_anisotropy", indent);
// Coat properties
ss << print_typed_attr(shader.coat_weight, "inputs:coat_weight", indent);
ss << print_typed_attr(shader.coat_color, "inputs:coat_color", indent);
ss << print_typed_attr(shader.coat_roughness, "inputs:coat_roughness", indent);
ss << print_typed_attr(shader.coat_anisotropy, "inputs:coat_anisotropy", indent);
ss << print_typed_attr(shader.coat_rotation, "inputs:coat_rotation", indent);
ss << print_typed_attr(shader.coat_roughness_anisotropy, "inputs:coat_roughness_anisotropy", indent);
ss << print_typed_attr(shader.coat_ior, "inputs:coat_ior", indent);
ss << print_typed_attr(shader.coat_darkening, "inputs:coat_darkening", indent);
ss << print_typed_attr(shader.coat_affect_color, "inputs:coat_affect_color", indent);
ss << print_typed_attr(shader.coat_affect_roughness, "inputs:coat_affect_roughness", indent);
// Fuzz properties
ss << print_typed_attr(shader.fuzz_weight, "inputs:fuzz_weight", indent);
ss << print_typed_attr(shader.fuzz_color, "inputs:fuzz_color", indent);
ss << print_typed_attr(shader.fuzz_roughness, "inputs:fuzz_roughness", indent);
// Thin film properties
ss << print_typed_attr(shader.thin_film_thickness, "inputs:thin_film_thickness", indent);
ss << print_typed_attr(shader.thin_film_ior, "inputs:thin_film_ior", indent);
ss << print_typed_attr(shader.thin_film_weight, "inputs:thin_film_weight", indent);
// Emission properties
ss << print_typed_attr(shader.emission_luminance, "inputs:emission_luminance", indent);
ss << print_typed_attr(shader.emission_color, "inputs:emission_color", indent);
// Geometry properties
ss << print_typed_attr(shader.geometry_opacity, "inputs:geometry_opacity", indent);
ss << print_typed_attr(shader.geometry_thin_walled, "inputs:geometry_thin_walled", indent);
ss << print_typed_attr(shader.geometry_normal, "inputs:geometry_normal", indent);
ss << print_typed_attr(shader.geometry_tangent, "inputs:geometry_tangent", indent);
ss << print_typed_attr(shader.geometry_coat_normal, "inputs:geometry_coat_normal", indent);
ss << print_typed_attr(shader.geometry_coat_tangent, "inputs:geometry_coat_tangent", indent);
// Output
ss << print_typed_terminal_attr(shader.surface, "outputs:surface", indent);
ss << print_common_shader_params(shader, indent);
return ss.str();
}
std::string to_string(const Shader &shader, const uint32_t indent,
bool closing_brace) {
// generic Shader class
@@ -4111,7 +4193,7 @@ std::string to_string(const Shader &shader, const uint32_t indent,
ss << print_shader_params(pvs.value(), indent + 1);
} else if (auto mtlx_opbr = shader.value.get_value<MtlxOpenPBRSurface>()) {
// Blender v4.5 MaterialX OpenPBR Surface
ss << print_common_shader_params(mtlx_opbr.value(), indent + 1);
ss << print_shader_params(mtlx_opbr.value(), indent + 1);
} else if (auto pvsn = shader.value.get_value<ShaderNode>()) {
// Generic ShaderNode
ss << print_common_shader_params(pvsn.value(), indent + 1);

View File

@@ -5122,6 +5122,311 @@ nonstd::expected<bool, std::string> GetConnectedUVTexture(
prim->prim_type_name()));
}
// Helper function to find ND_image_color4 texture nodes in a MaterialX NodeGraph
// by traversing connections from the given output
template <typename T>
nonstd::expected<bool, std::string> GetConnectedMtlxTexture(
const Stage &stage, const TypedAnimatableAttributeWithFallback<T> &src,
Path *tex_abs_path, const Shader **image_shader_out,
std::string *st_varname_out, const AssetInfo **assetInfo_out) {
if (!src.is_connection()) {
return nonstd::make_unexpected("Attribute must be connection.\n");
}
if (src.get_connections().size() != 1) {
return nonstd::make_unexpected(
"Attribute connections must be single connection Path.\n");
}
const Path &path = src.get_connections()[0];
const std::string prim_part = path.prim_part();
const std::string prop_part = path.prop_part();
DCOUT("Checking MaterialX connection: " << path.full_path_name());
DCOUT(" prim_part: " << prim_part);
DCOUT(" prop_part: " << prop_part);
// The prim_part should be the NodeGraph path itself
// For </root/_materials/Material/NodeGraphs.outputs:node_out>,
// prim_part = "/root/_materials/Material/NodeGraphs"
// First, try to find via stage lookup
const Prim *ng_prim{nullptr};
std::string err;
bool found_in_stage = stage.find_prim_at_path(Path(prim_part, ""), ng_prim, &err);
// If not found in stage lookup, try to navigate through Material's children
if (!found_in_stage || !ng_prim) {
DCOUT("Prim not found in stage lookup, trying Material children approach");
// Extract Material path - it should be everything before the last element
size_t last_slash = prim_part.rfind('/');
if (last_slash == std::string::npos) {
return nonstd::make_unexpected(
fmt::format("Invalid NodeGraph path structure: {}\n", prim_part));
}
std::string material_path = prim_part.substr(0, last_slash);
std::string nodegraph_name = prim_part.substr(last_slash + 1);
DCOUT("Looking for Material at: " << material_path);
DCOUT("NodeGraph name: " << nodegraph_name);
// Find the Material
const Prim *mat_prim{nullptr};
if (!stage.find_prim_at_path(Path(material_path, ""), mat_prim, &err)) {
return nonstd::make_unexpected(
fmt::format("Material {} not found: {}\n", material_path, err));
}
// Look for NodeGraph child
if (mat_prim) {
std::string children_info = "Material has " + std::to_string(mat_prim->children().size()) + " children: ";
for (const auto& child : mat_prim->children()) {
std::string child_name = child.element_name();
std::string child_type = child.data().type_name();
children_info += "'" + child_name + "'(" + child_type + ") ";
// Check if this is a NodeGraph (by type, since name might be empty)
if (child_type == "NodeGraph") {
// If the child has no name but is the right type, use it
// This handles the case where the NodeGraph doesn't have element_name set
ng_prim = &child;
break;
} else if (child_name == nodegraph_name) {
// Also check by exact name match
ng_prim = &child;
break;
}
}
if (!ng_prim) {
return nonstd::make_unexpected(
fmt::format("NodeGraph '{}' not found. {}\n", nodegraph_name, children_info));
}
} else {
return nonstd::make_unexpected(
fmt::format("Material prim is null\n"));
}
}
DCOUT("Found prim: " << prim_part << ", type: " << (ng_prim ? ng_prim->data().type_name() : "null"));
const NodeGraph *ng = ng_prim ? ng_prim->as<NodeGraph>() : nullptr;
if (!ng) {
// Debug output to understand why it's not a NodeGraph
if (ng_prim) {
return nonstd::make_unexpected(
fmt::format("{} is not a NodeGraph, prim_type: {}\n", prim_part, ng_prim->data().type_name()));
}
return nonstd::make_unexpected(
fmt::format("{} is not a NodeGraph\n", prim_part));
}
// Find the output connection we're looking for
// The prop_part should be like "outputs:node_out"
std::string output_name = prop_part;
if (startsWith(output_name, "outputs:")) {
output_name = output_name.substr(8); // Remove "outputs:" prefix
}
// Look for the connection in props
// Try both with and without ".connect" suffix
std::string conn_prop_name = "outputs:" + output_name + ".connect";
auto it = ng->props.find(conn_prop_name);
if (it == ng->props.end()) {
// Try without .connect suffix
conn_prop_name = "outputs:" + output_name;
it = ng->props.find(conn_prop_name);
if (it == ng->props.end()) {
// List available props for debugging
std::string available_props = "Available props: ";
for (const auto& prop : ng->props) {
available_props += prop.first + " ";
}
return nonstd::make_unexpected(
fmt::format("Output connection '{}' not found in NodeGraph. {}\n",
conn_prop_name, available_props));
}
}
// NodeGraph outputs can be stored as attributes or relationships
Path current_path;
bool found_connection = false;
if (it->second.is_attribute()) {
// It's an attribute - look for connections on the attribute
const Attribute &attr = it->second.get_attribute();
if (attr.has_connections() && !attr.connections().empty()) {
current_path = attr.connections()[0];
found_connection = true;
}
} else if (it->second.is_relationship()) {
// Also support relationship format
auto targets = it->second.get_relationTargets();
if (!targets.empty()) {
current_path = targets[0];
found_connection = true;
}
}
if (!found_connection) {
return nonstd::make_unexpected(
fmt::format("Output {} has no connection targets\n", conn_prop_name));
}
const Shader *image_shader = nullptr;
// Traverse the node connections to find ND_image_color4
// Maximum depth to prevent infinite loops
int max_depth = 10;
std::string traversal_log = "Traversal: ";
while (max_depth-- > 0) {
std::string current_prim_part = current_path.prim_part();
const Prim *current_prim{nullptr};
// First, try regular stage lookup
bool found_in_stage = stage.find_prim_at_path(Path(current_prim_part, ""), current_prim, &err);
// If not found and this is under a NodeGraph, look in NodeGraph children
if (!found_in_stage || !current_prim) {
// Check if this path is under the NodeGraph we found earlier
size_t last_slash = current_prim_part.rfind('/');
if (last_slash != std::string::npos) {
std::string parent_path = current_prim_part.substr(0, last_slash);
std::string child_name = current_prim_part.substr(last_slash + 1);
// Check if parent is our NodeGraph
if (ng_prim && parent_path.find("NodeGraphs") != std::string::npos) {
// Look for the child in the NodeGraph prim
for (const auto& child : ng_prim->children()) {
if (child.element_name() == child_name) {
current_prim = &child;
break;
}
}
}
}
if (!current_prim) {
return nonstd::make_unexpected(
fmt::format("Shader {} not found\n", current_prim_part));
}
}
const Shader *current_shader = current_prim ? current_prim->as<Shader>() : nullptr;
if (!current_shader) {
return nonstd::make_unexpected(
fmt::format("{} is not a Shader. {}\n", current_prim_part, traversal_log));
}
// Log this node
traversal_log += current_shader->info_id + " -> ";
// Check if this is an ND_image_color4 node
if (current_shader->info_id == "ND_image_color4" ||
current_shader->info_id == "ND_image_color3") {
image_shader = current_shader;
if (tex_abs_path) {
*tex_abs_path = current_path;
}
if (image_shader_out) {
*image_shader_out = image_shader;
}
if (assetInfo_out) {
// get_assetInfo returns AssetInfo converted from customData/assetInfo
bool authored = false;
const AssetInfo &info = current_shader->metas().get_assetInfo(&authored);
if (authored) {
*assetInfo_out = &info;
}
}
// For MaterialX, we don't have an explicit st varname,
// so we'll use "st" as default (same as UsdPreviewSurface)
if (st_varname_out) {
*st_varname_out = "st";
}
return true;
}
// Check if this node has an input connection we should follow
// For ND_convert_color4_color3, follow inputs:in
bool found_next = false;
DCOUT("Checking shader " << current_shader->info_id << " at " << current_prim_part);
// Debug: log all properties from both Shader and ShaderNode
std::string props_list = "ShaderProps: ";
for (const auto& prop : current_shader->props) {
props_list += prop.first + " ";
}
// Check if the shader has a ShaderNode value with properties
const ShaderNode *shader_node = current_shader->value.as<ShaderNode>();
if (shader_node && !shader_node->props.empty()) {
props_list += " NodeProps: ";
for (const auto& prop : shader_node->props) {
props_list += prop.first + " ";
}
}
traversal_log += "[" + props_list + "] ";
// Helper lambda to check for connections in a property map
auto find_connection = [&](const std::map<std::string, Property>& props_map) -> bool {
for (const auto& prop : props_map) {
if (startsWith(prop.first, "inputs:")) {
bool is_connection = false;
Path next_path;
if (endsWith(prop.first, ".connect")) {
// Explicit .connect suffix
is_connection = true;
if (prop.second.is_relationship()) {
auto next_targets = prop.second.get_relationTargets();
if (!next_targets.empty()) {
next_path = next_targets[0];
}
}
} else if (prop.second.is_attribute()) {
// Check if attribute has connections
const Attribute &attr = prop.second.get_attribute();
if (attr.has_connections() && !attr.connections().empty()) {
is_connection = true;
next_path = attr.connections()[0];
}
}
if (is_connection && !next_path.full_path_name().empty()) {
DCOUT(" Following connection from " << prop.first << " to " << next_path);
current_path = next_path;
return true;
}
}
}
return false;
};
// Try shader_node->props first, then fall back to current_shader->props
if (shader_node && !shader_node->props.empty()) {
found_next = find_connection(shader_node->props);
}
if (!found_next) {
found_next = find_connection(current_shader->props);
}
if (!found_next) {
break;
}
}
return nonstd::make_unexpected(
fmt::format("No ND_image_color4 texture node found. {}\n", traversal_log));
}
static bool RawAssetRead(
const value::AssetPath &assetPath, const AssetInfo &assetInfo,
const AssetResolutionResolver &assetResolver,
@@ -5764,7 +6069,8 @@ template <typename T, typename Dty>
bool RenderSceneConverter::ConvertPreviewSurfaceShaderParam(
const RenderSceneConverterEnv &env, const Path &shader_abs_path,
const TypedAttributeWithFallback<Animatable<T>> &param,
const std::string &param_name, ShaderParam<Dty> &dst_param) {
const std::string &param_name, ShaderParam<Dty> &dst_param,
bool is_materialx) {
if (!param.authored()) {
return true;
}
@@ -5774,6 +6080,119 @@ bool RenderSceneConverter::ConvertPreviewSurfaceShaderParam(
} else if (param.is_connection()) {
DCOUT(fmt::format("{} is attribute connection.", param_name));
// Check if this is a MaterialX connection to a NodeGraph
if (is_materialx && param.get_connections().size() == 1) {
const Path &conn_path = param.get_connections()[0];
if (conn_path.prim_part().find("/NodeGraphs") != std::string::npos) {
// This is a MaterialX NodeGraph connection, traverse to find texture
const Shader *image_shader{nullptr};
Path texPath;
std::string st_varname;
const AssetInfo *assetInfo{nullptr};
auto mtlx_result = GetConnectedMtlxTexture(
env.stage, param, &texPath, &image_shader, &st_varname, &assetInfo);
if (mtlx_result) {
// Found a MaterialX texture node
DCOUT("Found MaterialX texture node: " << texPath);
// Extract the file path from the image shader
value::AssetPath texAssetPath;
bool found_file = false;
for (const auto& prop : image_shader->props) {
if (prop.first == "inputs:file" && prop.second.is_attribute()) {
const Attribute &attr = prop.second.get_attribute();
if (attr.has_value()) {
auto asset_val = attr.get_value<value::AssetPath>();
if (asset_val) {
texAssetPath = *asset_val;
found_file = true;
break;
}
}
}
}
if (!found_file) {
PUSH_WARN(fmt::format("MaterialX image node {} has no file input", texPath.prim_part()));
return true;
}
// Create a synthetic UsdUVTexture to pass to ConvertUVTexture
UsdUVTexture synth_tex;
synth_tex.file.set_value(texAssetPath);
// Map MaterialX wrap modes to USD
for (const auto& prop : image_shader->props) {
if (prop.first == "inputs:uaddressmode" && prop.second.is_attribute()) {
const Attribute &attr = prop.second.get_attribute();
if (attr.has_value()) {
auto val = attr.get_value<std::string>();
if (val) {
if (*val == "periodic") {
synth_tex.wrapS.set_value(UsdUVTexture::Wrap::Repeat);
} else if (*val == "clamp") {
synth_tex.wrapS.set_value(UsdUVTexture::Wrap::Clamp);
}
}
}
}
if (prop.first == "inputs:vaddressmode" && prop.second.is_attribute()) {
const Attribute &attr = prop.second.get_attribute();
if (attr.has_value()) {
auto val = attr.get_value<std::string>();
if (val) {
if (*val == "periodic") {
synth_tex.wrapT.set_value(UsdUVTexture::Wrap::Repeat);
} else if (*val == "clamp") {
synth_tex.wrapT.set_value(UsdUVTexture::Wrap::Clamp);
}
}
}
}
}
// Use ConvertUVTexture to properly handle the texture
UVTexture rtex;
AssetInfo mtlx_assetInfo; // Use the assetInfo if available
if (assetInfo) {
mtlx_assetInfo = *assetInfo;
}
// Handle colorSpace from attribute metadata if available
// AssetInfo doesn't have set_string, so we'll need to handle this differently
// For now, just use the assetInfo as-is
if (!ConvertUVTexture(env, texPath, mtlx_assetInfo, synth_tex, &rtex)) {
PUSH_ERROR_AND_RETURN(fmt::format(
"Failed to convert MaterialX texture for {}", param_name));
}
// Set the connected output channel and UV primvar name
rtex.connectedOutputChannel = tydra::UVTexture::Channel::RGB;
rtex.varname_uv = st_varname;
uint64_t texId = textures.size();
textures.push_back(rtex);
textureMap.add(texId, shader_abs_path.prim_part() + "." + param_name);
DCOUT(fmt::format("MaterialX TexId {}.{} = {}",
shader_abs_path.prim_part(), param_name, texId));
dst_param.texture_id = int32_t(texId);
return true;
} else {
PUSH_WARN(fmt::format("Failed to find MaterialX texture for {}: {}",
param_name, mtlx_result.error()));
}
}
}
// Fall back to standard UsdUVTexture handling
const UsdUVTexture *ptex{nullptr};
const Shader *pshader{nullptr};
Path texPath;
@@ -5958,22 +6377,26 @@ bool RenderSceneConverter::ConvertOpenPBRSurfaceShader(
// Convert base layer parameters
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.base_weight, "base_weight",
rshader.base_weight)) {
rshader.base_weight, true)) {
PushWarn(fmt::format("Failed to convert base_weight parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.base_color, "base_color",
rshader.base_color)) {
rshader.base_color, true)) {
PushWarn(fmt::format("Failed to convert base_color parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.base_roughness, "base_roughness",
rshader.base_roughness)) {
PushWarn(fmt::format("Failed to convert base_roughness parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.base_metalness, "base_metalness",
rshader.base_metalness)) {
PushWarn(fmt::format("Failed to convert base_metalness parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
@@ -5981,36 +6404,43 @@ bool RenderSceneConverter::ConvertOpenPBRSurfaceShader(
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.specular_weight, "specular_weight",
rshader.specular_weight)) {
PushWarn(fmt::format("Failed to convert specular_weight parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.specular_color, "specular_color",
rshader.specular_color)) {
PushWarn(fmt::format("Failed to convert specular_color parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.specular_roughness, "specular_roughness",
rshader.specular_roughness)) {
PushWarn(fmt::format("Failed to convert specular_roughness parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.specular_ior, "specular_ior",
rshader.specular_ior)) {
PushWarn(fmt::format("Failed to convert specular_ior parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.specular_ior_level, "specular_ior_level",
rshader.specular_ior_level)) {
PushWarn(fmt::format("Failed to convert specular_ior_level parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.specular_anisotropy, "specular_anisotropy",
rshader.specular_anisotropy)) {
PushWarn(fmt::format("Failed to convert specular_anisotropy parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.specular_rotation, "specular_rotation",
rshader.specular_rotation)) {
PushWarn(fmt::format("Failed to convert specular_rotation parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
@@ -6018,31 +6448,37 @@ bool RenderSceneConverter::ConvertOpenPBRSurfaceShader(
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.transmission_weight, "transmission_weight",
rshader.transmission_weight)) {
PushWarn(fmt::format("Failed to convert transmission_weight parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.transmission_color, "transmission_color",
rshader.transmission_color)) {
rshader.transmission_color, true)) {
PushWarn(fmt::format("Failed to convert transmission_color parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.transmission_depth, "transmission_depth",
rshader.transmission_depth)) {
PushWarn(fmt::format("Failed to convert transmission_depth parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.transmission_scatter, "transmission_scatter",
rshader.transmission_scatter)) {
PushWarn(fmt::format("Failed to convert transmission_scatter parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.transmission_scatter_anisotropy,
"transmission_scatter_anisotropy", rshader.transmission_scatter_anisotropy)) {
PushWarn(fmt::format("Failed to convert transmission_scatter_anisotropy parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.transmission_dispersion,
"transmission_dispersion", rshader.transmission_dispersion)) {
PushWarn(fmt::format("Failed to convert transmission_dispersion parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
@@ -6050,26 +6486,31 @@ bool RenderSceneConverter::ConvertOpenPBRSurfaceShader(
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.subsurface_weight, "subsurface_weight",
rshader.subsurface_weight)) {
PushWarn(fmt::format("Failed to convert subsurface_weight parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.subsurface_color, "subsurface_color",
rshader.subsurface_color)) {
rshader.subsurface_color, true)) {
PushWarn(fmt::format("Failed to convert subsurface_color parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.subsurface_radius, "subsurface_radius",
rshader.subsurface_radius)) {
PushWarn(fmt::format("Failed to convert subsurface_radius parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.subsurface_scale, "subsurface_scale",
rshader.subsurface_scale)) {
PushWarn(fmt::format("Failed to convert subsurface_scale parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.subsurface_anisotropy,
"subsurface_anisotropy", rshader.subsurface_anisotropy)) {
PushWarn(fmt::format("Failed to convert subsurface_anisotropy parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
@@ -6077,16 +6518,19 @@ bool RenderSceneConverter::ConvertOpenPBRSurfaceShader(
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.sheen_weight, "sheen_weight",
rshader.sheen_weight)) {
PushWarn(fmt::format("Failed to convert sheen_weight parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.sheen_color, "sheen_color",
rshader.sheen_color)) {
PushWarn(fmt::format("Failed to convert sheen_color parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.sheen_roughness, "sheen_roughness",
rshader.sheen_roughness)) {
PushWarn(fmt::format("Failed to convert sheen_roughness parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
@@ -6094,41 +6538,49 @@ bool RenderSceneConverter::ConvertOpenPBRSurfaceShader(
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.coat_weight, "coat_weight",
rshader.coat_weight)) {
PushWarn(fmt::format("Failed to convert coat_weight parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.coat_color, "coat_color",
rshader.coat_color)) {
PushWarn(fmt::format("Failed to convert coat_color parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.coat_roughness, "coat_roughness",
rshader.coat_roughness)) {
PushWarn(fmt::format("Failed to convert coat_roughness parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.coat_anisotropy, "coat_anisotropy",
rshader.coat_anisotropy)) {
PushWarn(fmt::format("Failed to convert coat_anisotropy parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.coat_rotation, "coat_rotation",
rshader.coat_rotation)) {
PushWarn(fmt::format("Failed to convert coat_rotation parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.coat_ior, "coat_ior",
rshader.coat_ior)) {
PushWarn(fmt::format("Failed to convert coat_ior parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.coat_affect_color, "coat_affect_color",
rshader.coat_affect_color)) {
PushWarn(fmt::format("Failed to convert coat_affect_color parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.coat_affect_roughness, "coat_affect_roughness",
rshader.coat_affect_roughness)) {
PushWarn(fmt::format("Failed to convert coat_affect_roughness parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
@@ -6136,11 +6588,13 @@ bool RenderSceneConverter::ConvertOpenPBRSurfaceShader(
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.emission_luminance, "emission_luminance",
rshader.emission_luminance)) {
PushWarn(fmt::format("Failed to convert emission_luminance parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.emission_color, "emission_color",
rshader.emission_color)) {
PushWarn(fmt::format("Failed to convert emission_color parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
@@ -6148,16 +6602,19 @@ bool RenderSceneConverter::ConvertOpenPBRSurfaceShader(
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.opacity, "opacity",
rshader.opacity)) {
PushWarn(fmt::format("Failed to convert opacity parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.normal, "normal",
rshader.normal)) {
PushWarn(fmt::format("Failed to convert normal parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
if (!ConvertPreviewSurfaceShaderParam(
env, shader_abs_path, shader.tangent, "tangent",
rshader.tangent)) {
PushWarn(fmt::format("Failed to convert tangent parameter for shader: {}", shader_abs_path.prim_part()));
return false;
}
@@ -8352,6 +8809,8 @@ bool InferColorSpace(const value::token &tok, ColorSpace *cty) {
return true;
}
#if 0 // Deprecated: Use implementation in render-scene-dump.cc instead
namespace {
template <typename T>
@@ -9062,6 +9521,8 @@ std::string DumpRenderScene(const RenderScene &scene,
return ss.str();
}
#endif // Deprecated dump functions
// Memory usage estimation implementations
size_t RenderMesh::estimate_memory_usage() const {

View File

@@ -2778,7 +2778,8 @@ class RenderSceneConverter {
bool ConvertPreviewSurfaceShaderParam(
const RenderSceneConverterEnv &env, const Path &shader_abs_path,
const TypedAttributeWithFallback<Animatable<T>> &param,
const std::string &param_name, ShaderParam<Dty> &dst_param);
const std::string &param_name, ShaderParam<Dty> &dst_param,
bool is_materialx = false);
///
/// Build (single) vertex indices for RenderMesh.
@@ -2817,9 +2818,9 @@ class RenderSceneConverter {
const XformNode &node,
Node &out_rnode);
void PushInfo(const std::string &msg) { _info += msg; }
void PushWarn(const std::string &msg) { _warn += msg; }
void PushError(const std::string &msg) { _err += msg; }
void PushInfo(const std::string &msg) { _info += msg + "\n"; }
void PushWarn(const std::string &msg) { _warn += msg + "\n"; }
void PushError(const std::string &msg) { _err += msg + "\n"; }
///
/// Call progress callback if set.

View File

@@ -1679,6 +1679,7 @@ bool USDAReader::Impl::Read(const uint32_t state_flags, bool as_primspec) {
RegisterReconstructCallback<Material>();
RegisterReconstructCallback<Shader>();
RegisterReconstructCallback<NodeGraph>();
RegisterReconstructCallback<Scope>();

View File

@@ -1898,6 +1898,7 @@ nonstd::optional<Prim> USDCReader::Impl::ReconstructPrimFromTypeName(
RECONSTRUCT_PRIM(SkelAnimation, typeName, prim_name, spec)
RECONSTRUCT_PRIM(BlendShape, typeName, prim_name, spec)
RECONSTRUCT_PRIM(Shader, typeName, prim_name, spec)
RECONSTRUCT_PRIM(NodeGraph, typeName, prim_name, spec)
RECONSTRUCT_PRIM(Material, typeName, prim_name, spec) {
PUSH_WARN("TODO or unsupported prim type: " << typeName);
if (is_unsupported_prim) {