|
15 | 15 | #include "material_parsing.h" |
16 | 16 |
|
17 | 17 | #include <cstdio> |
| 18 | +#include <filesystem> |
18 | 19 | #include <optional> |
19 | 20 | #include <string> |
20 | 21 |
|
|
24 | 25 | #include <pxr/base/tf/staticData.h> |
25 | 26 | #include <pxr/base/tf/staticTokens.h> |
26 | 27 | #include <pxr/base/tf/token.h> |
27 | | -#include <pxr/usd/ar/asset.h> |
28 | | -#include <pxr/usd/ar/resolvedPath.h> |
29 | | -#include <pxr/usd/ar/resolver.h> |
30 | 28 | #include <pxr/usd/usd/prim.h> |
31 | 29 | #include <pxr/usd/usdShade/input.h> |
32 | 30 | #include <pxr/usd/usdShade/material.h> |
33 | 31 | #include <pxr/usd/usdShade/shader.h> |
34 | 32 | #include <pxr/usd/usdShade/types.h> |
35 | 33 | #include <pxr/usd/usdShade/udimUtils.h> |
| 34 | +#include <pxr/usd/usdShade/utils.h> |
36 | 35 |
|
37 | 36 | // Using to satisfy TF_DEFINE_PRIVATE_TOKENS macro below and avoid operating in |
38 | 37 | // PXR_NS. |
@@ -119,84 +118,104 @@ ResolvedShaderInput<T> ReadUsdUVTexture(mjSpec* spec, |
119 | 118 | texture->colorspace = mjtColorSpace::mjCOLORSPACE_AUTO; |
120 | 119 | } |
121 | 120 |
|
122 | | - pxr::ArResolver& resolver = pxr::ArGetResolver(); |
123 | 121 | pxr::SdfAssetPath resolved_texture_asset_path; |
| 122 | + |
124 | 123 | if (auto file_input = shader.GetInput(kTokens->file)) { |
125 | | - file_input.Get(&resolved_texture_asset_path); |
| 124 | + // Use GetValueProducingAttributes to follow all connections including |
| 125 | + // material interfaces. |
| 126 | + auto value_attrs = |
| 127 | + pxr::UsdShadeUtils::GetValueProducingAttributes(file_input); |
| 128 | + for (const auto& attr : value_attrs) { |
| 129 | + if (attr.Get(&resolved_texture_asset_path) && |
| 130 | + !resolved_texture_asset_path.GetAssetPath().empty()) { |
| 131 | + break; |
| 132 | + } |
| 133 | + } |
126 | 134 | } else { |
127 | 135 | mju_error("UsdUVTexture missing inputs:file."); |
128 | 136 | return out; |
129 | 137 | } |
130 | 138 |
|
131 | | - std::string resolved_texture_path = |
132 | | - resolved_texture_asset_path.GetResolvedPath(); |
| 139 | + // Use the resolved path from USD, fall back to asset path if not resolved |
| 140 | + std::string resolved_path_str = resolved_texture_asset_path.GetResolvedPath(); |
| 141 | + if (resolved_path_str.empty()) { |
| 142 | + resolved_path_str = resolved_texture_asset_path.GetAssetPath(); |
| 143 | + } |
133 | 144 |
|
134 | | - if (pxr::UsdShadeUdimUtils::IsUdimIdentifier(resolved_texture_path)) { |
135 | | - mju_error("MuJoCo does not support UDIM textures: %s", |
136 | | - resolved_texture_path.c_str()); |
| 145 | + if (resolved_path_str.empty()) { |
| 146 | + mju_warning("UsdUVTexture %s: No texture file path specified.", |
| 147 | + shader.GetPath().GetAsString().c_str()); |
137 | 148 | return out; |
138 | 149 | } |
139 | 150 |
|
140 | | - auto extension = resolver.GetExtension(resolved_texture_path); |
| 151 | + if (pxr::UsdShadeUdimUtils::IsUdimIdentifier(resolved_path_str)) { |
| 152 | + mju_error("MuJoCo does not support UDIM textures: %s", |
| 153 | + resolved_path_str.c_str()); |
| 154 | + return out; |
| 155 | + } |
141 | 156 |
|
142 | | - FILE* fp = fopen(resolved_texture_path.c_str(), "r"); |
143 | | - if (fp == nullptr) { |
144 | | - mju_error( |
| 157 | + if (!std::filesystem::exists(resolved_path_str)) { |
| 158 | + mju_warning( |
145 | 159 | "USD decoder only supports assets that are available on the file " |
146 | | - "system"); |
| 160 | + "system. Could not open: %s", |
| 161 | + resolved_path_str.c_str()); |
147 | 162 | return out; |
148 | 163 | } |
| 164 | + |
149 | 165 | texture->nchannel = nchannels; |
150 | | - mjs_setString(texture->file, resolved_texture_path.c_str()); |
| 166 | + mjs_setString(texture->file, resolved_path_str.c_str()); |
151 | 167 | out.sampler = texture; |
152 | 168 | return out; |
153 | 169 | } |
154 | 170 |
|
155 | | -// Given an input to a shader, attempts to bake the shader and it's inputs |
156 | | -// into a singular value or texture sampler (ResolvedShaderInput). |
157 | | -template <typename T> |
158 | | -ResolvedShaderInput<T> ReadShaderInput( |
159 | | - mjSpec* spec, const pxr::UsdShadeConnectableAPI source, |
160 | | - const pxr::TfToken source_name, |
161 | | - const pxr::UsdShadeAttributeType source_type) { |
162 | | - ResolvedShaderInput<T> out; |
163 | | - pxr::UsdShadeShader shader(source.GetPrim()); |
164 | | - pxr::TfToken shader_id = GetShaderId(shader); |
165 | | - if (shader_id == kTokens->UsdUVTexture) { |
166 | | - unsigned nchannels = -1; |
167 | | - if (source_name == kTokens->rgb) { |
168 | | - nchannels = 3; |
169 | | - } else if (source_name == kTokens->r) { |
170 | | - nchannels = 1; |
171 | | - } else { |
172 | | - mju_error("Unsupported texture channel: %s", source_name.GetText()); |
173 | | - return out; |
174 | | - } |
175 | | - |
176 | | - return ReadUsdUVTexture<T>(spec, shader, nchannels); |
177 | | - } else { |
178 | | - mju_warning("Unsupported shader type: %s", shader_id.GetText()); |
179 | | - } |
180 | | - |
181 | | - return out; |
182 | | -} |
183 | | - |
184 | 171 | // Reads UsdShadeInput as a value of type T or a texture sampler. |
| 172 | +// Uses GetValueProducingAttributes to follow all connections including |
| 173 | +// material interfaces. |
185 | 174 | template <typename T> |
186 | 175 | ResolvedShaderInput<T> ReadShaderInput(mjSpec* spec, pxr::UsdShadeInput input) { |
187 | 176 | ResolvedShaderInput<T> out; |
188 | 177 | if (!input.GetPrim().IsValid()) { |
189 | 178 | return out; |
190 | 179 | } |
191 | 180 |
|
192 | | - pxr::UsdShadeConnectableAPI source; |
193 | | - pxr::TfToken source_name; |
194 | | - pxr::UsdShadeAttributeType source_type; |
195 | | - if (input.GetConnectedSource(&source, &source_name, &source_type)) { |
196 | | - out = ReadShaderInput<T>(spec, source, source_name, source_type); |
197 | | - } else { |
198 | | - out.value = ReadInput<T>(input); |
| 181 | + // GetValueProducingAttributes follows connections recursively, including |
| 182 | + // through material interfaces, and returns the attributes that produce |
| 183 | + // values. |
| 184 | + auto value_attrs = pxr::UsdShadeUtils::GetValueProducingAttributes(input); |
| 185 | + |
| 186 | + for (const auto& attr : value_attrs) { |
| 187 | + // Check if this attribute belongs to a UsdUVTexture shader (for textures) |
| 188 | + pxr::UsdPrim prim = attr.GetPrim(); |
| 189 | + pxr::UsdShadeShader shader(prim); |
| 190 | + if (shader) { |
| 191 | + pxr::TfToken shader_id = GetShaderId(shader); |
| 192 | + if (shader_id == kTokens->UsdUVTexture) { |
| 193 | + // Determine channel count from the output name |
| 194 | + pxr::TfToken attr_name = attr.GetBaseName(); |
| 195 | + unsigned nchannels = 3; // default |
| 196 | + if (attr_name == kTokens->rgb) { |
| 197 | + nchannels = 3; |
| 198 | + } else if (attr_name == kTokens->r) { |
| 199 | + nchannels = 1; |
| 200 | + } |
| 201 | + out = ReadUsdUVTexture<T>(spec, shader, nchannels); |
| 202 | + if (out.sampler.has_value()) { |
| 203 | + return out; |
| 204 | + } |
| 205 | + } |
| 206 | + // For other shader types, fall through to try reading as a value |
| 207 | + } |
| 208 | + |
| 209 | + // Try to read as a direct value |
| 210 | + out.value = ReadInput<T>(pxr::UsdShadeInput(attr)); |
| 211 | + if (out.value.has_value()) { |
| 212 | + return out; |
| 213 | + } |
199 | 214 | } |
| 215 | + |
| 216 | + // If no value-producing attributes found, try reading the input directly |
| 217 | + // (for non-connected inputs with authored values) |
| 218 | + out.value = ReadInput<T>(input); |
200 | 219 | return out; |
201 | 220 | } |
202 | 221 |
|
|
0 commit comments