1 //
2 // Copyright 2018 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #include "pxr/usd/usdShade/shaderDefUtils.h"
25
26 #include "pxr/usd/usdShade/shader.h"
27
28 #include "pxr/base/tf/stringUtils.h"
29
30 #include "pxr/usd/ar/resolver.h"
31
32 #include "pxr/usd/sdf/assetPath.h"
33 #include "pxr/usd/sdf/types.h"
34
35 #include "pxr/usd/sdr/shaderMetadataHelpers.h"
36 #include "pxr/usd/sdr/shaderNode.h"
37 #include "pxr/usd/sdr/shaderProperty.h"
38
39 #include <cctype>
40
41 PXR_NAMESPACE_OPEN_SCOPE
42
43 TF_DEFINE_PRIVATE_TOKENS(
44 _tokens,
45
46 /* property metadata */
47 (primvarProperty)
48 (defaultInput)
49 (implementationName)
50 );
51
_IsNumber(const std::string & s)52 static bool _IsNumber(const std::string& s)
53 {
54 return !s.empty() &&
55 std::find_if(s.begin(), s.end(),
56 [](unsigned char c) { return !std::isdigit(c); })
57 == s.end();
58 }
59
60 /* static */
61 bool
SplitShaderIdentifier(const TfToken & identifier,TfToken * familyName,TfToken * implementationName,NdrVersion * version)62 UsdShadeShaderDefUtils::SplitShaderIdentifier(
63 const TfToken &identifier,
64 TfToken *familyName,
65 TfToken *implementationName,
66 NdrVersion *version)
67 {
68 std::vector<std::string> tokens = TfStringTokenize(identifier.GetString(),
69 "_");
70
71 if (tokens.empty()) {
72 return false;
73 }
74
75 *familyName = TfToken(tokens[0]);
76
77 if (tokens.size() == 1) {
78 *familyName = identifier;
79 *implementationName = identifier;
80 *version = NdrVersion();
81 } else if (tokens.size() == 2) {
82 if (_IsNumber(tokens[tokens.size()-1])) {
83 int major = std::stoi(*tokens.rbegin());
84 *version = NdrVersion(major);
85 *implementationName = *familyName;
86 } else {
87 *version = NdrVersion();
88 *implementationName = identifier;
89 }
90 } else if (tokens.size() > 2) {
91 bool lastTokenIsNumber = _IsNumber(tokens[tokens.size()-1]);
92 bool penultimateTokenIsNumber = _IsNumber(tokens[tokens.size()-2]);
93
94 if (penultimateTokenIsNumber && !lastTokenIsNumber) {
95 TF_WARN("Invalid shader identifier '%s'.", identifier.GetText());
96 return false;
97 }
98
99 if (lastTokenIsNumber && penultimateTokenIsNumber) {
100 *version = NdrVersion(std::stoi(tokens[tokens.size()-2]),
101 std::stoi(tokens[tokens.size()-1]));
102 *implementationName = TfToken(TfStringJoin(tokens.begin(),
103 tokens.begin() + (tokens.size() - 2), "_"));
104 } else if (lastTokenIsNumber) {
105 *version = NdrVersion(std::stoi(tokens[tokens.size()-1]));
106 *implementationName = TfToken(TfStringJoin(tokens.begin(),
107 tokens.begin() + (tokens.size() - 1), "_"));
108 } else {
109 // No version information is available.
110 *implementationName = identifier;
111 *version = NdrVersion();
112 }
113 }
114
115 return true;
116 }
117
118 /* static */
119 NdrNodeDiscoveryResultVec
GetNodeDiscoveryResults(const UsdShadeShader & shaderDef,const std::string & sourceUri)120 UsdShadeShaderDefUtils::GetNodeDiscoveryResults(
121 const UsdShadeShader &shaderDef,
122 const std::string &sourceUri)
123 {
124 NdrNodeDiscoveryResultVec result;
125
126 // Implementation source must be sourceAsset for the shader to represent
127 // nodes in Sdr.
128 if (shaderDef.GetImplementationSource() != UsdShadeTokens->sourceAsset)
129 return result;
130
131 const UsdPrim shaderDefPrim = shaderDef.GetPrim();
132 const TfToken &identifier = shaderDefPrim.GetName();
133
134 // Get the family name, shader name and version information from the
135 // identifier.
136 TfToken family;
137 TfToken name;
138 NdrVersion version;
139 if (!SplitShaderIdentifier(shaderDefPrim.GetName(),
140 &family, &name, &version)) {
141 // A warning has already been issued by SplitShaderIdentifier.
142 return result;
143 }
144
145 static const std::string infoNamespace("info:");
146 static const std::string baseSourceAsset(":sourceAsset");
147
148 // This vector will contain all the info:*:sourceAsset properties.
149 std::vector<UsdProperty> sourceAssetProperties =
150 shaderDefPrim.GetAuthoredProperties(
151 [](const TfToken &propertyName) {
152 const std::string &propertyNameStr = propertyName.GetString();
153 return TfStringStartsWith(propertyNameStr, infoNamespace) &&
154 TfStringEndsWith(propertyNameStr, baseSourceAsset);
155 });
156
157 const TfToken discoveryType(ArGetResolver().GetExtension(sourceUri));
158
159 for (auto &prop : sourceAssetProperties) {
160 UsdAttribute attr = prop.As<UsdAttribute>();
161 if (!attr) {
162 continue;
163 }
164
165 SdfAssetPath sourceAssetPath;
166 if (attr.Get(&sourceAssetPath) &&
167 !sourceAssetPath.GetAssetPath().empty()) {
168
169 auto nameTokens =
170 SdfPath::TokenizeIdentifierAsTokens(attr.GetName());
171 if (nameTokens.size() != 3) {
172 continue;
173 }
174
175 const std::string &resolvedUri = sourceAssetPath.GetResolvedPath();
176
177 // Create a discoveryResult only if the referenced sourceAsset
178 // can be resolved.
179 // XXX: Should we do this regardless and expect the parser to be
180 // able to resolve the unresolved asset path?
181 if (!resolvedUri.empty()) {
182 const TfToken &sourceType = nameTokens[1];
183
184 // Use the prim name as the identifier since it is
185 // guaranteed to be unique in the file.
186 // Use the shader id as the name of the shader.
187 result.emplace_back(
188 identifier,
189 version.GetAsDefault(),
190 name,
191 family,
192 discoveryType,
193 sourceType,
194 /* uri */ sourceUri,
195 /* resolvedUri */ sourceUri);
196 } else {
197 TF_WARN("Unable to resolve info:sourceAsset <%s> with value "
198 "@%s@.", attr.GetPath().GetText(),
199 sourceAssetPath.GetAssetPath().c_str());
200 }
201 }
202 }
203
204 return result;
205 }
206
207 // Called within _GetShaderPropertyTypeAndArraySize in order to fix up the
208 // default value's type if it was originally a token type because Sdr doesn't
209 // support token types
210 static
211 void
_ConformStringTypeDefaultValue(const SdfValueTypeName & typeName,VtValue * defaultValue)212 _ConformStringTypeDefaultValue(
213 const SdfValueTypeName& typeName,
214 VtValue* defaultValue)
215 {
216 // If the SdrPropertyType would be string but the SdfTypeName is token,
217 // we need to convert the default value's type from token to string so that
218 // there is no inconsistency between the property type and the default value
219 if (defaultValue && !defaultValue->IsEmpty()) {
220 if (typeName == SdfValueTypeNames->Token) {
221 if (defaultValue->IsHolding<TfToken>()) {
222 const TfToken& tokenVal =
223 defaultValue->UncheckedGet<TfToken>();
224 *defaultValue = VtValue(tokenVal.GetString());
225 }
226 } else if (typeName == SdfValueTypeNames->TokenArray) {
227 if (defaultValue->IsHolding< VtArray<TfToken> >()) {
228 const VtArray<TfToken>& tokenVals =
229 defaultValue->UncheckedGet< VtArray<TfToken> >();
230 VtStringArray stringVals;
231 stringVals.reserve(tokenVals.size());
232 for (const TfToken& tokenVal : tokenVals) {
233 stringVals.push_back(tokenVal.GetString());
234 }
235 *defaultValue = VtValue::Take(stringVals);
236 }
237 }
238 }
239 }
240
241 // Called within _GetShaderPropertyTypeAndArraySize in order to fix up the
242 // default value's type if it was originally a bool type because Sdr doesn't
243 // support bool types
244 static
245 void
_ConformIntTypeDefaultValue(const SdfValueTypeName & typeName,VtValue * defaultValue)246 _ConformIntTypeDefaultValue(
247 const SdfValueTypeName& typeName,
248 VtValue* defaultValue)
249 {
250 // If the SdrPropertyType would be int but the SdfTypeName is bool,
251 // we need to convert the default value's type from bool to int so that
252 // there is no inconsistency between the property type and the default value
253 if (defaultValue && !defaultValue->IsEmpty()) {
254 if (typeName == SdfValueTypeNames->Bool) {
255 if (defaultValue->IsHolding<bool>()) {
256 const bool& boolVal =
257 defaultValue->UncheckedGet<bool>();
258 *defaultValue = VtValue(boolVal ? 1 : 0);
259 }
260 } else if (typeName == SdfValueTypeNames->BoolArray) {
261 if (defaultValue->IsHolding< VtArray<bool> >()) {
262 const VtArray<bool>& boolVals =
263 defaultValue->UncheckedGet< VtArray<bool> >();
264 VtIntArray intVals;
265 intVals.reserve(boolVals.size());
266 for (const bool& boolVal : boolVals) {
267 intVals.push_back(boolVal ? 1 : 0);
268 }
269 *defaultValue = VtValue::Take(intVals);
270 }
271 }
272 }
273 }
274
275 // Called within _GetShaderPropertyTypeAndArraySize in order to return the
276 // correct array size as determined by the given default value
277 static
278 size_t
_GetArraySize(VtValue * defaultValue)279 _GetArraySize(VtValue* defaultValue) {
280 if (defaultValue && !defaultValue->IsEmpty()
281 && defaultValue->IsArrayValued()) {
282 return defaultValue->GetArraySize();
283 }
284 return 0;
285 }
286
287 // This function is called to determine a shader property's type and array size,
288 // and it will also conform the default value to the correct type if needed
289 static
290 std::pair<TfToken, size_t>
_GetShaderPropertyTypeAndArraySize(const SdfValueTypeName & typeName,const NdrTokenMap & metadata,VtValue * defaultValue)291 _GetShaderPropertyTypeAndArraySize(
292 const SdfValueTypeName &typeName,
293 const NdrTokenMap& metadata,
294 VtValue* defaultValue)
295 {
296 // XXX Note that the shaderDefParser does not currently parse 'struct' or
297 // 'vstruct' types.
298 // Structs are not supported in USD but are allowed as an Sdr property
299 // type. Vstructs are not parsed at the moment because we have no need
300 // for them in USD-backed shaders currently.
301
302 // Determine SdrPropertyType from metadata first, since metadata can
303 // override the type dictated otherwise by the SdfValueTypeName
304 if (ShaderMetadataHelpers::IsPropertyATerminal(metadata)) {
305 return std::make_pair(SdrPropertyTypes->Terminal,
306 _GetArraySize(defaultValue));
307 }
308
309 // Determine SdrPropertyType from given SdfValueTypeName
310 if (typeName == SdfValueTypeNames->Int ||
311 typeName == SdfValueTypeNames->IntArray ||
312 typeName == SdfValueTypeNames->Bool ||
313 typeName == SdfValueTypeNames->BoolArray) {
314 _ConformIntTypeDefaultValue(typeName, defaultValue);
315 return std::make_pair(SdrPropertyTypes->Int,
316 _GetArraySize(defaultValue));
317 } else if (typeName == SdfValueTypeNames->String ||
318 typeName == SdfValueTypeNames->Token ||
319 typeName == SdfValueTypeNames->Asset ||
320 typeName == SdfValueTypeNames->StringArray ||
321 typeName == SdfValueTypeNames->TokenArray ||
322 typeName == SdfValueTypeNames->AssetArray) {
323 _ConformStringTypeDefaultValue(typeName, defaultValue);
324 return std::make_pair(SdrPropertyTypes->String,
325 _GetArraySize(defaultValue));
326 } else if (typeName == SdfValueTypeNames->Float ||
327 typeName == SdfValueTypeNames->FloatArray) {
328 return std::make_pair(SdrPropertyTypes->Float,
329 _GetArraySize(defaultValue));
330 } else if (typeName == SdfValueTypeNames->Float2 ||
331 typeName == SdfValueTypeNames->Float2Array) {
332 return std::make_pair(SdrPropertyTypes->Float, 2);
333 } else if (typeName == SdfValueTypeNames->Float3 ||
334 typeName == SdfValueTypeNames->Float3Array) {
335 return std::make_pair(SdrPropertyTypes->Float, 3);
336 } else if (typeName == SdfValueTypeNames->Float4 ||
337 typeName == SdfValueTypeNames->Float4Array) {
338 return std::make_pair(SdrPropertyTypes->Float, 4);
339 } else if (typeName == SdfValueTypeNames->Color3f ||
340 typeName == SdfValueTypeNames->Color3fArray) {
341 return std::make_pair(SdrPropertyTypes->Color,
342 _GetArraySize(defaultValue));
343 } else if (typeName == SdfValueTypeNames->Point3f ||
344 typeName == SdfValueTypeNames->Point3fArray) {
345 return std::make_pair(SdrPropertyTypes->Point,
346 _GetArraySize(defaultValue));
347 } else if (typeName == SdfValueTypeNames->Vector3f ||
348 typeName == SdfValueTypeNames->Vector3fArray) {
349 return std::make_pair(SdrPropertyTypes->Vector,
350 _GetArraySize(defaultValue));
351 } else if (typeName == SdfValueTypeNames->Normal3f||
352 typeName == SdfValueTypeNames->Normal3fArray) {
353 return std::make_pair(SdrPropertyTypes->Normal,
354 _GetArraySize(defaultValue));
355 } else if (typeName == SdfValueTypeNames->Matrix4d ||
356 typeName == SdfValueTypeNames->Matrix4dArray) {
357 return std::make_pair(SdrPropertyTypes->Matrix,
358 _GetArraySize(defaultValue));
359 } else {
360 TF_RUNTIME_ERROR("Shader property has unsupported type '%s'",
361 typeName.GetAsToken().GetText());
362 return std::make_pair(SdrPropertyTypes->Unknown, 0);
363 }
364 }
365
366 template <class ShaderProperty>
367 static
368 SdrShaderPropertyUniquePtr
_CreateSdrShaderProperty(const ShaderProperty & shaderProperty,bool isOutput,const VtValue & shaderDefaultValue,const NdrTokenMap & shaderMetadata)369 _CreateSdrShaderProperty(
370 const ShaderProperty& shaderProperty,
371 bool isOutput,
372 const VtValue& shaderDefaultValue,
373 const NdrTokenMap& shaderMetadata)
374 {
375 const std::string propName = shaderProperty.GetBaseName();
376 VtValue defaultValue = shaderDefaultValue;
377 NdrTokenMap metadata = shaderMetadata;
378 NdrTokenMap hints;
379 NdrOptionVec options;
380
381 // Update metadata if string should represent a SdfAssetPath
382 if (shaderProperty.GetTypeName() == SdfValueTypeNames->Asset ||
383 shaderProperty.GetTypeName() == SdfValueTypeNames->AssetArray) {
384 metadata[SdrPropertyMetadata->IsAssetIdentifier] = "1";
385 }
386
387 TfToken propertyType;
388 size_t arraySize;
389 std::tie(propertyType, arraySize) = _GetShaderPropertyTypeAndArraySize(
390 shaderProperty.GetTypeName(), shaderMetadata, &defaultValue);
391
392 return SdrShaderPropertyUniquePtr(new SdrShaderProperty(
393 shaderProperty.GetBaseName(),
394 propertyType,
395 defaultValue,
396 isOutput,
397 arraySize,
398 metadata, hints, options));
399 }
400
401 /*static*/
402 NdrPropertyUniquePtrVec
GetShaderProperties(const UsdShadeConnectableAPI & shaderDef)403 UsdShadeShaderDefUtils::GetShaderProperties(
404 const UsdShadeConnectableAPI &shaderDef)
405 {
406 NdrPropertyUniquePtrVec result;
407 for (auto &shaderInput : shaderDef.GetInputs(/* onlyAuthored */ false)) {
408 // Only inputs will have default value provided
409 VtValue defaultValue;
410 shaderInput.Get(&defaultValue);
411
412 NdrTokenMap metadata = shaderInput.GetSdrMetadata();
413
414 // Only inputs might have this metadata key
415 auto iter = metadata.find(_tokens->defaultInput);
416 if (iter != metadata.end()) {
417 metadata[SdrPropertyMetadata->DefaultInput] = "1";
418 metadata.erase(_tokens->defaultInput);
419 }
420
421 // Only inputs have the GetConnectability method
422 metadata[SdrPropertyMetadata->Connectable] =
423 shaderInput.GetConnectability() == UsdShadeTokens->interfaceOnly ?
424 "0" : "1";
425
426 auto implementationName = metadata.find(_tokens->implementationName);
427 if (implementationName != metadata.end()){
428 metadata[SdrPropertyMetadata->ImplementationName] =
429 implementationName->second;
430 metadata.erase(implementationName);
431 }
432
433 result.emplace_back(
434 _CreateSdrShaderProperty(
435 /* shaderProperty */ shaderInput,
436 /* isOutput */ false,
437 /* shaderDefaultValue */ defaultValue,
438 /* shaderMetadata */ metadata));
439 }
440
441 for (auto &shaderOutput : shaderDef.GetOutputs(/* onlyAuthored */ false)) {
442 result.emplace_back(
443 _CreateSdrShaderProperty(
444 /* shaderProperty */ shaderOutput,
445 /* isOutput */ true,
446 /* shaderDefaultValue */ VtValue() ,
447 /* shaderMetadata */ shaderOutput.GetSdrMetadata()));
448 }
449
450 return result;
451 }
452
453 /*static*/
454 std::string
GetPrimvarNamesMetadataString(const NdrTokenMap metadata,const UsdShadeConnectableAPI & shaderDef)455 UsdShadeShaderDefUtils::GetPrimvarNamesMetadataString(
456 const NdrTokenMap metadata,
457 const UsdShadeConnectableAPI &shaderDef)
458 {
459 // If there's an existing value in the definition, we must append to it.
460 std::vector<std::string> primvarNames;
461 if (metadata.count(SdrNodeMetadata->Primvars)) {
462 primvarNames.push_back(metadata.at(SdrNodeMetadata->Primvars));
463 }
464
465 for (auto &shdInput : shaderDef.GetInputs(/* onlyAuthored */ false)) {
466 if (shdInput.HasSdrMetadataByKey(_tokens->primvarProperty)) {
467 // Check if the input holds a string here and issue a warning if it
468 // doesn't.
469 if (_GetShaderPropertyTypeAndArraySize(
470 shdInput.GetTypeName(),
471 shdInput.GetSdrMetadata(),
472 nullptr).first !=
473 SdrPropertyTypes->String) {
474 TF_WARN("Shader input <%s> is tagged as a primvarProperty, "
475 "but isn't string-valued.",
476 shdInput.GetAttr().GetPath().GetText());
477 }
478
479 primvarNames.push_back("$" + shdInput.GetBaseName().GetString());
480 }
481 }
482
483 return TfStringJoin(primvarNames, "|");
484 }
485
486
487 PXR_NAMESPACE_CLOSE_SCOPE
488
489