1 //
2 // Copyright 2016 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/materialBindingAPI.h"
25 #include "pxr/usd/usd/schemaRegistry.h"
26 #include "pxr/usd/usd/typed.h"
27 #include "pxr/usd/usd/tokens.h"
28
29 #include "pxr/usd/sdf/types.h"
30 #include "pxr/usd/sdf/assetPath.h"
31
32 PXR_NAMESPACE_OPEN_SCOPE
33
34 // Register the schema with the TfType system.
TF_REGISTRY_FUNCTION(TfType)35 TF_REGISTRY_FUNCTION(TfType)
36 {
37 TfType::Define<UsdShadeMaterialBindingAPI,
38 TfType::Bases< UsdAPISchemaBase > >();
39
40 }
41
42 TF_DEFINE_PRIVATE_TOKENS(
43 _schemaTokens,
44 (MaterialBindingAPI)
45 );
46
47 /* virtual */
~UsdShadeMaterialBindingAPI()48 UsdShadeMaterialBindingAPI::~UsdShadeMaterialBindingAPI()
49 {
50 }
51
52 /* static */
53 UsdShadeMaterialBindingAPI
Get(const UsdStagePtr & stage,const SdfPath & path)54 UsdShadeMaterialBindingAPI::Get(const UsdStagePtr &stage, const SdfPath &path)
55 {
56 if (!stage) {
57 TF_CODING_ERROR("Invalid stage");
58 return UsdShadeMaterialBindingAPI();
59 }
60 return UsdShadeMaterialBindingAPI(stage->GetPrimAtPath(path));
61 }
62
63
64 /* virtual */
_GetSchemaKind() const65 UsdSchemaKind UsdShadeMaterialBindingAPI::_GetSchemaKind() const
66 {
67 return UsdShadeMaterialBindingAPI::schemaKind;
68 }
69
70 /* static */
71 bool
CanApply(const UsdPrim & prim,std::string * whyNot)72 UsdShadeMaterialBindingAPI::CanApply(
73 const UsdPrim &prim, std::string *whyNot)
74 {
75 return prim.CanApplyAPI<UsdShadeMaterialBindingAPI>(whyNot);
76 }
77
78 /* static */
79 UsdShadeMaterialBindingAPI
Apply(const UsdPrim & prim)80 UsdShadeMaterialBindingAPI::Apply(const UsdPrim &prim)
81 {
82 if (prim.ApplyAPI<UsdShadeMaterialBindingAPI>()) {
83 return UsdShadeMaterialBindingAPI(prim);
84 }
85 return UsdShadeMaterialBindingAPI();
86 }
87
88 /* static */
89 const TfType &
_GetStaticTfType()90 UsdShadeMaterialBindingAPI::_GetStaticTfType()
91 {
92 static TfType tfType = TfType::Find<UsdShadeMaterialBindingAPI>();
93 return tfType;
94 }
95
96 /* static */
97 bool
_IsTypedSchema()98 UsdShadeMaterialBindingAPI::_IsTypedSchema()
99 {
100 static bool isTyped = _GetStaticTfType().IsA<UsdTyped>();
101 return isTyped;
102 }
103
104 /* virtual */
105 const TfType &
_GetTfType() const106 UsdShadeMaterialBindingAPI::_GetTfType() const
107 {
108 return _GetStaticTfType();
109 }
110
111 /*static*/
112 const TfTokenVector&
GetSchemaAttributeNames(bool includeInherited)113 UsdShadeMaterialBindingAPI::GetSchemaAttributeNames(bool includeInherited)
114 {
115 static TfTokenVector localNames;
116 static TfTokenVector allNames =
117 UsdAPISchemaBase::GetSchemaAttributeNames(true);
118
119 if (includeInherited)
120 return allNames;
121 else
122 return localNames;
123 }
124
125 PXR_NAMESPACE_CLOSE_SCOPE
126
127 // ===================================================================== //
128 // Feel free to add custom code below this line. It will be preserved by
129 // the code generator.
130 //
131 // Just remember to wrap code in the appropriate delimiters:
132 // 'PXR_NAMESPACE_OPEN_SCOPE', 'PXR_NAMESPACE_CLOSE_SCOPE'.
133 // ===================================================================== //
134 // --(BEGIN CUSTOM CODE)--
135
136 #include "pxr/base/work/loops.h"
137
138 PXR_NAMESPACE_OPEN_SCOPE
139
140 TF_DEFINE_PRIVATE_TOKENS(
141 _tokens,
142 ((materialBindingFull, "material:binding:full"))
143 ((materialBindingPreview, "material:binding:preview"))
144
145 ((materialBindingCollectionFull, "material:binding:collection:full"))
146 ((materialBindingCollectionPreview, "material:binding:collection:preview"))
147 );
148
149 static
150 TfToken
_GetDirectBindingRelName(const TfToken & materialPurpose)151 _GetDirectBindingRelName(const TfToken &materialPurpose)
152 {
153 // Optimize for the three common values of materialPurpose.
154 if (materialPurpose == UsdShadeTokens->allPurpose) {
155 return UsdShadeTokens->materialBinding;
156 } else if (materialPurpose == UsdShadeTokens->preview) {
157 return _tokens->materialBindingPreview;
158 } else if (materialPurpose == UsdShadeTokens->full) {
159 return _tokens->materialBindingFull;
160 }
161 return TfToken(SdfPath::JoinIdentifier(UsdShadeTokens->materialBinding,
162 materialPurpose));
163 }
164
165 static
166 TfToken
_GetCollectionBindingRelName(const TfToken & bindingName,const TfToken & materialPurpose)167 _GetCollectionBindingRelName(const TfToken &bindingName,
168 const TfToken &materialPurpose)
169 {
170 // Optimize for the three common values of materialPurpose.
171 if (materialPurpose == UsdShadeTokens->allPurpose) {
172 return TfToken(SdfPath::JoinIdentifier(
173 UsdShadeTokens->materialBindingCollection, bindingName));
174 } else if (materialPurpose == UsdShadeTokens->preview) {
175 return TfToken(SdfPath::JoinIdentifier(
176 _tokens->materialBindingCollectionPreview, bindingName));
177 } else if (materialPurpose == UsdShadeTokens->full) {
178 return TfToken(SdfPath::JoinIdentifier(
179 _tokens->materialBindingCollectionFull, bindingName));
180 }
181 return TfToken(SdfPath::JoinIdentifier(std::vector<TfToken>{
182 UsdShadeTokens->materialBindingCollection,
183 materialPurpose, bindingName}));
184 }
185
186 UsdRelationship
GetDirectBindingRel(const TfToken & materialPurpose) const187 UsdShadeMaterialBindingAPI::GetDirectBindingRel(
188 const TfToken &materialPurpose) const
189 {
190 return GetPrim().GetRelationship(_GetDirectBindingRelName(materialPurpose));
191 }
192
193 UsdRelationship
GetCollectionBindingRel(const TfToken & bindingName,const TfToken & materialPurpose) const194 UsdShadeMaterialBindingAPI::GetCollectionBindingRel(
195 const TfToken &bindingName,
196 const TfToken &materialPurpose) const
197 {
198 return GetPrim().GetRelationship(
199 _GetCollectionBindingRelName(bindingName, materialPurpose));
200 }
201
202 // Returns the material purpose associated with the given binding relationship.
203 // This returns UsdShadeTokens->allPurpose if the binding relationship does not
204 // apply to a specific material purpose.
205 static
206 TfToken
_GetMaterialPurpose(const UsdRelationship & bindingRel)207 _GetMaterialPurpose(const UsdRelationship &bindingRel)
208 {
209 std::vector<std::string> nameTokens = bindingRel.SplitName();
210 if (nameTokens.size() == 5) {
211 return TfToken(nameTokens[3]);
212 } else if (nameTokens.size() == 3) {
213 return TfToken(nameTokens[2]);
214 }
215 return UsdShadeTokens->allPurpose;
216 }
217
218
DirectBinding(const UsdRelationship & directBindingRel)219 UsdShadeMaterialBindingAPI::DirectBinding::DirectBinding(
220 const UsdRelationship &directBindingRel):
221 _bindingRel(directBindingRel),
222 _materialPurpose(_GetMaterialPurpose(directBindingRel))
223 {
224 SdfPathVector targetPaths;
225 _bindingRel.GetForwardedTargets(&targetPaths);
226 if (targetPaths.size() == 1 &&
227 targetPaths.front().IsPrimPath()) {
228 _materialPath = targetPaths.front();
229 }
230 }
231
232 UsdShadeMaterial
GetMaterial() const233 UsdShadeMaterialBindingAPI::DirectBinding::GetMaterial() const
234 {
235 if (!_materialPath.IsEmpty()) {
236 return UsdShadeMaterial(_bindingRel.GetStage()->GetPrimAtPath(
237 _materialPath));
238 }
239 return UsdShadeMaterial();
240 }
241
242 UsdShadeMaterialBindingAPI::DirectBinding
GetDirectBinding(const TfToken & materialPurpose) const243 UsdShadeMaterialBindingAPI::GetDirectBinding(
244 const TfToken &materialPurpose) const
245 {
246 UsdRelationship directBindingRel = GetDirectBindingRel(materialPurpose);
247 return DirectBinding(directBindingRel);
248 }
249
250 std::vector<UsdRelationship>
GetCollectionBindingRels(const TfToken & materialPurpose) const251 UsdShadeMaterialBindingAPI::GetCollectionBindingRels(
252 const TfToken &materialPurpose) const
253 {
254 std::vector<UsdProperty> collectionBindingProperties =
255 GetPrim().GetAuthoredPropertiesInNamespace(
256 _GetCollectionBindingRelName(TfToken(), materialPurpose));
257
258 std::vector<UsdRelationship> result;
259 for (const UsdProperty &prop : collectionBindingProperties) {
260 if (prop.Is<UsdRelationship>()) {
261 UsdRelationship rel = prop.As<UsdRelationship>();
262 if (_GetMaterialPurpose(rel) == materialPurpose) {
263 result.push_back(prop.As<UsdRelationship>());
264 }
265 }
266 }
267
268 return result;
269 }
270
CollectionBinding(const UsdRelationship & collBindingRel)271 UsdShadeMaterialBindingAPI::CollectionBinding::CollectionBinding(
272 const UsdRelationship &collBindingRel):
273 _bindingRel(collBindingRel)
274 {
275 SdfPathVector targetPaths;
276 collBindingRel.GetForwardedTargets(&targetPaths);
277 // A collection binding relationship must have exactly two targets
278 // One of them should target a property path (i.e. the collection path)
279 // and the other must target a prim (the bound material).
280 if (targetPaths.size() == 2) {
281 bool firstTargetPathIsPrimPath = targetPaths[0].IsPrimPath();
282 bool secondTargetPathIsPrimPath = targetPaths[1].IsPrimPath();
283
284 if (firstTargetPathIsPrimPath ^ secondTargetPathIsPrimPath) {
285 _materialPath = targetPaths[firstTargetPathIsPrimPath ? 0 : 1];
286 _collectionPath = targetPaths[firstTargetPathIsPrimPath ? 1 : 0];
287 }
288 }
289 }
290
291 UsdShadeMaterial
GetMaterial() const292 UsdShadeMaterialBindingAPI::CollectionBinding::GetMaterial() const
293 {
294 if (!_materialPath.IsEmpty()) {
295 return UsdShadeMaterial(_bindingRel.GetStage()->GetPrimAtPath(
296 _materialPath));
297 }
298 return UsdShadeMaterial();
299 }
300
301 UsdCollectionAPI
GetCollection() const302 UsdShadeMaterialBindingAPI::CollectionBinding::GetCollection() const
303 {
304 if (!_collectionPath.IsEmpty()) {
305 return UsdCollectionAPI::GetCollection(_bindingRel.GetStage(),
306 _collectionPath);
307 }
308 return UsdCollectionAPI();
309 }
310
311
312 UsdShadeMaterialBindingAPI::CollectionBindingVector
GetCollectionBindings(const TfToken & materialPurpose) const313 UsdShadeMaterialBindingAPI::GetCollectionBindings(
314 const TfToken &materialPurpose) const
315 {
316 std::vector<UsdRelationship> collectionBindingRels =
317 GetCollectionBindingRels(materialPurpose);
318
319 CollectionBindingVector result;
320 result.reserve(collectionBindingRels.size());
321
322 for (const auto &collBindingRel : collectionBindingRels) {
323 result.emplace_back(collBindingRel);
324
325 // If both the collection and the material are valid, keep them in
326 // the result.
327 if (!result.back().IsValid()) {
328 result.pop_back();
329 }
330 }
331
332 return result;
333 }
334
335 UsdShadeMaterialBindingAPI::CollectionBindingVector
_GetCollectionBindings(const TfTokenVector & collBindingPropertyNames) const336 UsdShadeMaterialBindingAPI::_GetCollectionBindings(
337 const TfTokenVector &collBindingPropertyNames) const
338 {
339 CollectionBindingVector result;
340 // Assuming that all binding relationships have something bound by them.
341 result.reserve(collBindingPropertyNames.size());
342
343 for (const auto &collBindingPropName : collBindingPropertyNames) {
344 if (UsdRelationship collBindingRel =
345 GetPrim().GetRelationship(collBindingPropName)) {
346 result.emplace_back(collBindingRel);
347 // If collection binding is valid, keep it in the result.
348 if (!result.back().IsValid()) {
349 result.pop_back();
350 }
351 }
352 }
353
354 return result;
355 }
356
357 /* static */
358 TfToken
GetMaterialBindingStrength(const UsdRelationship & bindingRel)359 UsdShadeMaterialBindingAPI::GetMaterialBindingStrength(
360 const UsdRelationship &bindingRel)
361 {
362 TfToken bindingStrength;
363 bindingRel.GetMetadata(UsdShadeTokens->bindMaterialAs,
364 &bindingStrength);
365 // Default binding strength is weakerThanDescendants, as bindings authored
366 // on a prim are considered to be stronger than those authored on an
367 // ancestor, unless the ancestor binding overrides the binding strength to
368 // strongerThanDescendants.
369 return bindingStrength.IsEmpty() ? UsdShadeTokens->weakerThanDescendants
370 : bindingStrength;
371 }
372
373 /* static */
374 bool
SetMaterialBindingStrength(const UsdRelationship & bindingRel,const TfToken & bindingStrength)375 UsdShadeMaterialBindingAPI::SetMaterialBindingStrength(
376 const UsdRelationship &bindingRel,
377 const TfToken &bindingStrength)
378 {
379 if (bindingStrength == UsdShadeTokens->fallbackStrength) {
380 TfToken existingBindingStrength;
381 bindingRel.GetMetadata(UsdShadeTokens->bindMaterialAs,
382 &existingBindingStrength);
383 if (!existingBindingStrength.IsEmpty() &&
384 existingBindingStrength != UsdShadeTokens->weakerThanDescendants) {
385 return bindingRel.SetMetadata(
386 UsdShadeTokens->bindMaterialAs,
387 UsdShadeTokens->weakerThanDescendants);
388 }
389 return true;
390 }
391 return bindingRel.SetMetadata(UsdShadeTokens->bindMaterialAs,
392 bindingStrength);
393 }
394
395 UsdRelationship
_CreateDirectBindingRel(const TfToken & materialPurpose) const396 UsdShadeMaterialBindingAPI::_CreateDirectBindingRel(
397 const TfToken &materialPurpose) const
398 {
399 return GetPrim().CreateRelationship(
400 _GetDirectBindingRelName(materialPurpose), /*custom*/ false);
401 }
402
403 UsdRelationship
_CreateCollectionBindingRel(const TfToken & bindingName,const TfToken & materialPurpose) const404 UsdShadeMaterialBindingAPI::_CreateCollectionBindingRel(
405 const TfToken &bindingName,
406 const TfToken &materialPurpose) const
407 {
408 TfToken collBindingRelName = _GetCollectionBindingRelName(
409 bindingName, materialPurpose);
410 return GetPrim().CreateRelationship(collBindingRelName, /* custom */ false);
411 }
412
413 bool
Bind(const UsdShadeMaterial & material,const TfToken & bindingStrength,const TfToken & materialPurpose) const414 UsdShadeMaterialBindingAPI::Bind(
415 const UsdShadeMaterial &material,
416 const TfToken &bindingStrength,
417 const TfToken &materialPurpose) const
418 {
419 if (UsdRelationship bindingRel = _CreateDirectBindingRel(materialPurpose)) {
420 SetMaterialBindingStrength(bindingRel, bindingStrength);
421 return bindingRel.SetTargets({material.GetPath()});
422 }
423
424 return false;
425 }
426
427 bool
Bind(const UsdCollectionAPI & collection,const UsdShadeMaterial & material,const TfToken & bindingName,const TfToken & bindingStrength,const TfToken & materialPurpose) const428 UsdShadeMaterialBindingAPI::Bind(
429 const UsdCollectionAPI &collection,
430 const UsdShadeMaterial& material,
431 const TfToken &bindingName,
432 const TfToken &bindingStrength,
433 const TfToken &materialPurpose) const
434 {
435 // BindingName should not contains any namespaces.
436 // Also, we use the collection-name when bindingName is empty.
437 TfToken fixedBindingName = bindingName;
438
439 if (bindingName.IsEmpty()) {
440 fixedBindingName = SdfPath::StripNamespace(collection.GetName());
441 } else if (bindingName.GetString().find(':') != std::string::npos) {
442 TF_CODING_ERROR("Invalid bindingName '%s', as it contains namespaces. "
443 "Not binding collection <%s> to material <%s>.",
444 bindingName.GetText(), collection.GetCollectionPath().GetText(),
445 material.GetPath().GetText());
446 return false;
447 }
448
449 UsdRelationship collBindingRel = _CreateCollectionBindingRel(
450 fixedBindingName, materialPurpose);
451
452 if (collBindingRel) {
453 SetMaterialBindingStrength(collBindingRel, bindingStrength);
454 return collBindingRel.SetTargets({collection.GetCollectionPath(),
455 material.GetPath()});
456 }
457
458 return false;
459 }
460
461 bool
UnbindDirectBinding(const TfToken & materialPurpose) const462 UsdShadeMaterialBindingAPI::UnbindDirectBinding(
463 const TfToken &materialPurpose) const
464 {
465 UsdRelationship bindingRel = GetPrim().CreateRelationship(
466 _GetDirectBindingRelName(materialPurpose), /*custom*/ false);
467 return bindingRel && bindingRel.SetTargets({});
468 }
469
470 bool
UnbindCollectionBinding(const TfToken & bindingName,const TfToken & materialPurpose) const471 UsdShadeMaterialBindingAPI::UnbindCollectionBinding(
472 const TfToken &bindingName,
473 const TfToken &materialPurpose) const
474 {
475 UsdRelationship collBindingRel = GetPrim().CreateRelationship(
476 _GetCollectionBindingRelName(bindingName, materialPurpose),
477 /*custom*/ false);
478 return collBindingRel && collBindingRel.SetTargets({});
479 }
480
481 bool
UnbindAllBindings() const482 UsdShadeMaterialBindingAPI::UnbindAllBindings() const
483 {
484 std::vector<UsdProperty> allBindingProperties =
485 GetPrim().GetPropertiesInNamespace(
486 UsdShadeTokens->materialBinding);
487
488 // The relationship named material:binding (Which is the default/all-purpose
489 // direct binding relationship) isn't included in the result of
490 // GetPropertiesInNamespace. Add it here if it exists.
491 if (UsdRelationship allPurposeDirectBindingRel =
492 GetPrim().GetRelationship(UsdShadeTokens->materialBinding)) {
493 allBindingProperties.push_back(allPurposeDirectBindingRel);
494 }
495
496 bool success = true;
497 std::vector<UsdRelationship> result;
498 for (const UsdProperty &prop : allBindingProperties) {
499 if (UsdRelationship bindingRel = prop.As<UsdRelationship>()) {
500 success = bindingRel.SetTargets({}) && success;
501 }
502 }
503
504 return success;
505 }
506
507 bool
RemovePrimFromBindingCollection(const UsdPrim & prim,const TfToken & bindingName,const TfToken & materialPurpose) const508 UsdShadeMaterialBindingAPI::RemovePrimFromBindingCollection(
509 const UsdPrim &prim,
510 const TfToken &bindingName,
511 const TfToken &materialPurpose) const
512 {
513 if (UsdRelationship collBindingRel = GetCollectionBindingRel(bindingName,
514 materialPurpose)) {
515 auto collBinding = CollectionBinding(collBindingRel);
516 if (UsdCollectionAPI collection = collBinding.GetCollection()) {
517 return collection.ExcludePath(prim.GetPath());
518 }
519 }
520
521 return true;
522 }
523
524 bool
AddPrimToBindingCollection(const UsdPrim & prim,const TfToken & bindingName,const TfToken & materialPurpose) const525 UsdShadeMaterialBindingAPI::AddPrimToBindingCollection(
526 const UsdPrim &prim,
527 const TfToken &bindingName,
528 const TfToken &materialPurpose) const
529 {
530 if (UsdRelationship collBindingRel = GetCollectionBindingRel(bindingName,
531 materialPurpose)) {
532 auto collBinding = CollectionBinding(collBindingRel);
533 if (UsdCollectionAPI collection = collBinding.GetCollection()) {
534 return collection.IncludePath(prim.GetPath());
535 }
536 }
537
538 return true;
539 }
540
541 // Given all the property names that start with "material:binding", returns
542 // the subset of properties that represents collection-based bindings for the
543 // given material purpose.
544 static
545 TfTokenVector
_GetCollectionBindingPropertyNames(const TfTokenVector & matBindingPropNames,const TfToken & purpose)546 _GetCollectionBindingPropertyNames(
547 const TfTokenVector &matBindingPropNames,
548 const TfToken &purpose)
549 {
550 TfToken collBindingPrefix = _GetCollectionBindingRelName(
551 /* bindingName */ TfToken(), purpose);
552
553 TfTokenVector collBindingRelNames;
554 // Not reserving memory because we don't expect to find these on most
555 // prims.
556 size_t indexOfNSDelim = collBindingPrefix.size();
557 for (auto & matBindingPropName : matBindingPropNames) {
558 if (matBindingPropName.size() > indexOfNSDelim &&
559 matBindingPropName.GetString()[indexOfNSDelim] == ':' &&
560 TfStringStartsWith(matBindingPropName.GetString(),
561 collBindingPrefix.GetString()) &&
562 // Ensure that the material purpose matches by making sure
563 // the second half does not contain a ":".
564 (purpose != UsdShadeTokens->allPurpose ||
565 matBindingPropName.GetString().find(':', indexOfNSDelim+1) ==
566 std::string::npos)) {
567 collBindingRelNames.push_back(matBindingPropName);
568 }
569 }
570
571 return collBindingRelNames;
572 }
573
BindingsAtPrim(const UsdPrim & prim,const TfToken & materialPurpose)574 UsdShadeMaterialBindingAPI::BindingsAtPrim::BindingsAtPrim(
575 const UsdPrim &prim, const TfToken &materialPurpose)
576 {
577 // These are the properties we need to consider when looking for
578 // bindings (both direct and collection-based) at the prim itself
579 // and each ancestor prim.
580 // Note: This vector is already ordered.
581 const TfTokenVector matBindingPropNames = prim.GetAuthoredPropertyNames(
582 /*predicate*/ [](const TfToken &name) {
583 return TfStringStartsWith(name.GetString(),
584 UsdShadeTokens->materialBinding);
585 });
586
587 if (matBindingPropNames.empty())
588 return;
589
590 auto foundMatBindingProp = [&matBindingPropNames](const TfToken &relName) {
591 return std::find(matBindingPropNames.begin(), matBindingPropNames.end(),
592 relName) != matBindingPropNames.end();
593 };
594
595 TfToken directBindingRelName = _GetDirectBindingRelName(materialPurpose);
596 if (foundMatBindingProp(directBindingRelName)) {
597 UsdRelationship directBindingRel =
598 prim.GetRelationship(directBindingRelName);
599 directBinding.reset(new DirectBinding(directBindingRel));
600 }
601
602 // If there is no restricted purpose direct binding, look for an
603 // all-purpose direct-binding.
604 if (materialPurpose != UsdShadeTokens->allPurpose &&
605 (!directBinding || !directBinding->GetMaterial())) {
606
607 // This may not be necessary if a specific purpose collection-binding
608 // already includes the prim for which the resolved binding is being
609 // computed.
610 TfToken allPurposeDBRelName = _GetDirectBindingRelName(
611 UsdShadeTokens->allPurpose);
612
613 if (foundMatBindingProp(allPurposeDBRelName)) {
614 UsdRelationship directBindingRel = prim.GetRelationship(
615 allPurposeDBRelName);
616 directBinding.reset(new DirectBinding (directBindingRel));
617 }
618 }
619
620 // If the direct-binding points to an invalid material then clear it.
621 if (directBinding && !directBinding->GetMaterial()) {
622 directBinding.release();
623 }
624
625 // Check if there are any collection-based binding relationships
626 // for the current "purpose" in matBindingPropNames.
627 if (materialPurpose != UsdShadeTokens->allPurpose) {
628 TfTokenVector collBindingPropertyNames =
629 _GetCollectionBindingPropertyNames(matBindingPropNames,
630 materialPurpose);
631 if (!collBindingPropertyNames.empty()) {
632 UsdShadeMaterialBindingAPI bindingAPI(prim);
633 restrictedPurposeCollBindings =
634 bindingAPI._GetCollectionBindings(collBindingPropertyNames);
635 }
636 }
637
638
639 TfTokenVector collBindingPropertyNames =
640 _GetCollectionBindingPropertyNames(matBindingPropNames,
641 UsdShadeTokens->allPurpose);
642 if (!collBindingPropertyNames.empty()) {
643 UsdShadeMaterialBindingAPI bindingAPI(prim);
644 allPurposeCollBindings =
645 bindingAPI._GetCollectionBindings(collBindingPropertyNames);
646 }
647 }
648
649 UsdShadeMaterial
ComputeBoundMaterial(BindingsCache * bindingsCache,CollectionQueryCache * collectionQueryCache,const TfToken & materialPurpose,UsdRelationship * bindingRel) const650 UsdShadeMaterialBindingAPI::ComputeBoundMaterial(
651 BindingsCache *bindingsCache,
652 CollectionQueryCache *collectionQueryCache,
653 const TfToken &materialPurpose,
654 UsdRelationship *bindingRel) const
655 {
656 if (!GetPrim()) {
657 TF_CODING_ERROR("Invalid prim (%s)", UsdDescribe(GetPrim()).c_str());
658 return UsdShadeMaterial();
659 }
660
661 TRACE_FUNCTION();
662
663 std::vector<TfToken> materialPurposes{materialPurpose};
664 if (materialPurpose != UsdShadeTokens->allPurpose) {
665 materialPurposes.push_back(UsdShadeTokens->allPurpose);
666 }
667
668 for (auto const & purpose : materialPurposes) {
669 UsdShadeMaterial boundMaterial;
670 UsdRelationship winningBindingRel;
671
672 for (UsdPrim p = GetPrim(); !p.IsPseudoRoot(); p = p.GetParent()) {
673
674 auto bindingsIt = bindingsCache->find(p.GetPath());
675 if (bindingsIt == bindingsCache->end()) {
676 TRACE_SCOPE("UsdShadeMaterialBindingAPI::ComputeBoundMaterial "
677 "(BindingsCache)");
678
679 std::unique_ptr<BindingsAtPrim> bindingsAtP(
680 new BindingsAtPrim(p, materialPurpose));
681
682 // XXX: emplace does not work here due to a tbb bug.
683 // see https://software.intel.com/en-us/forums/
684 // intel-threading-building-blocks/topic/671548
685
686 bindingsIt = bindingsCache->insert(std::make_pair(p.GetPath(),
687 std::move(bindingsAtP))).first;
688 }
689
690 const BindingsAtPrim &bindingsAtP = *bindingsIt->second;
691
692 const DirectBindingPtr &directBindingPtr =
693 bindingsAtP.directBinding;
694 if (directBindingPtr &&
695 directBindingPtr->GetMaterialPurpose() == purpose)
696 {
697 const UsdRelationship &directBindingRel =
698 directBindingPtr->GetBindingRel();
699 if (!boundMaterial ||
700 (GetMaterialBindingStrength(directBindingRel) ==
701 UsdShadeTokens->strongerThanDescendants))
702 {
703 boundMaterial = directBindingPtr->GetMaterial();
704 winningBindingRel = directBindingRel;
705 }
706 }
707
708 const CollectionBindingVector &collBindings =
709 purpose == UsdShadeTokens->allPurpose ?
710 bindingsAtP.allPurposeCollBindings :
711 bindingsAtP.restrictedPurposeCollBindings;
712
713 for (auto &collBinding : collBindings) {
714 TRACE_SCOPE("UsdShadeMaterialBindingAPI::ComputeBoundMaterial "
715 "(IsInBoundCollection)");
716
717 const UsdCollectionAPI &collection =
718 collBinding.GetCollection();
719 const SdfPath &collectionPath = collBinding.GetCollectionPath();
720
721 auto collIt = collectionQueryCache->find(collectionPath);
722 if (collIt == collectionQueryCache->end()) {
723 TRACE_SCOPE("UsdShadeMaterialBindingAPI::"
724 "ComputeBoundMaterial (CollectionQuery)");
725
726 std::unique_ptr<UsdCollectionAPI::MembershipQuery>
727 mQuery(new UsdCollectionAPI::MembershipQuery);
728 collection.ComputeMembershipQuery(mQuery.get());
729
730 // XXX: emplace does not work here due to a tbb bug.
731 // see https://software.intel.com/en-us/forums/
732 // intel-threading-building-blocks/topic/671548
733 collIt = collectionQueryCache->insert(std::make_pair(
734 collectionPath, std::move(mQuery))).first;
735 }
736
737 bool isPrimIncludedInCollection =
738 collIt->second->IsPathIncluded(GetPath());
739 if (isPrimIncludedInCollection) {
740 const UsdRelationship &collBindingRel =
741 collBinding.GetBindingRel();
742 // If the collection binding is on the prim itself and if
743 // the prim is included in the collection, the collection-based
744 // binding is considered to be stronger than the direct binding.
745 if (!boundMaterial ||
746 (boundMaterial && winningBindingRel.GetPrim() == p) ||
747 (GetMaterialBindingStrength(collBindingRel) ==
748 UsdShadeTokens->strongerThanDescendants)) {
749 boundMaterial = collBinding.GetMaterial();
750 winningBindingRel = collBindingRel;
751
752 // The first collection binding we match will be the
753 // one we care about.
754 break;
755 }
756 }
757 }
758 }
759
760 // The first "purpose" with a valid binding wins.
761 if (boundMaterial) {
762 if (bindingRel) {
763 *bindingRel = winningBindingRel;
764 }
765
766 return boundMaterial;
767 }
768 }
769
770 return UsdShadeMaterial();
771 }
772
773 UsdShadeMaterial
ComputeBoundMaterial(const TfToken & materialPurpose,UsdRelationship * bindingRel) const774 UsdShadeMaterialBindingAPI::ComputeBoundMaterial(
775 const TfToken &materialPurpose,
776 UsdRelationship *bindingRel) const
777 {
778 BindingsCache bindingsCache;
779 CollectionQueryCache collQueryCache;
780 return ComputeBoundMaterial(&bindingsCache,
781 &collQueryCache,
782 materialPurpose,
783 bindingRel);
784 }
785
786 /* static */
787 std::vector<UsdShadeMaterial>
ComputeBoundMaterials(const std::vector<UsdPrim> & prims,const TfToken & materialPurpose,std::vector<UsdRelationship> * bindingRels)788 UsdShadeMaterialBindingAPI::ComputeBoundMaterials(
789 const std::vector<UsdPrim> &prims,
790 const TfToken &materialPurpose,
791 std::vector<UsdRelationship> *bindingRels)
792 {
793 std::vector<UsdShadeMaterial> materials(prims.size());
794
795 if (bindingRels) {
796 bindingRels->clear();
797 bindingRels->resize(prims.size());
798 }
799
800 // This ensures that bindings are only computed once per prim.
801 BindingsCache bindingsCache;
802
803 // The use of CollectionQueryCache ensures that every collection's
804 // MembershipQuery object is only constructed once.
805 CollectionQueryCache collQueryCache;
806
807 WorkParallelForN(prims.size(),
808 [&](size_t start, size_t end) {
809 for (size_t i = start; i < end; ++i) {
810 const UsdPrim &prim = prims[i];
811 UsdRelationship *bindingRel =
812 bindingRels ? &((*bindingRels)[i]) : nullptr;
813 materials[i] = UsdShadeMaterialBindingAPI(prim).
814 ComputeBoundMaterial(&bindingsCache, &collQueryCache,
815 materialPurpose, bindingRel);
816 }
817 });
818
819 return materials;
820 }
821
822 UsdGeomSubset
CreateMaterialBindSubset(const TfToken & subsetName,const VtIntArray & indices,const TfToken & elementType)823 UsdShadeMaterialBindingAPI::CreateMaterialBindSubset(
824 const TfToken &subsetName,
825 const VtIntArray &indices,
826 const TfToken &elementType)
827 {
828 UsdGeomImageable geom(GetPrim());
829
830 UsdGeomSubset result = UsdGeomSubset::CreateGeomSubset(geom, subsetName,
831 elementType, indices, UsdShadeTokens->materialBind);
832
833 TfToken familyType = UsdGeomSubset::GetFamilyType(geom,
834 UsdShadeTokens->materialBind);
835 // Subsets that have materials bound to them should have
836 // mutually exclusive sets of indices. Hence, set the familyType
837 // to "nonOverlapping" if it's unset (or explicitly set to unrestricted).
838 if (familyType.IsEmpty() ||
839 familyType == UsdGeomTokens->unrestricted) {
840 SetMaterialBindSubsetsFamilyType(UsdGeomTokens->nonOverlapping);
841 }
842
843 return result;
844 }
845
846
847 std::vector<UsdGeomSubset>
GetMaterialBindSubsets()848 UsdShadeMaterialBindingAPI::GetMaterialBindSubsets()
849 {
850 UsdGeomImageable geom(GetPrim());
851 return UsdGeomSubset::GetGeomSubsets(geom, /* elementType */ TfToken(),
852 UsdShadeTokens->materialBind);
853 }
854
855 bool
SetMaterialBindSubsetsFamilyType(const TfToken & familyType)856 UsdShadeMaterialBindingAPI::SetMaterialBindSubsetsFamilyType(
857 const TfToken &familyType)
858 {
859 if (familyType == UsdGeomTokens->unrestricted) {
860 TF_CODING_ERROR("Attempted to set invalid familyType 'unrestricted' for"
861 "the \"materialBind\" family of subsets on <%s>.",
862 GetPath().GetText());
863 return false;
864 }
865 UsdGeomImageable geom(GetPrim());
866 return UsdGeomSubset::SetFamilyType(geom, UsdShadeTokens->materialBind,
867 familyType);
868 }
869
870 TfToken
GetMaterialBindSubsetsFamilyType()871 UsdShadeMaterialBindingAPI::GetMaterialBindSubsetsFamilyType()
872 {
873 UsdGeomImageable geom(GetPrim());
874 return UsdGeomSubset::GetFamilyType(geom, UsdShadeTokens->materialBind);
875 }
876
877 /* static */
878 bool
CanContainPropertyName(const TfToken & name)879 UsdShadeMaterialBindingAPI::CanContainPropertyName(const TfToken &name)
880 {
881 return TfStringStartsWith(name, UsdShadeTokens->materialBinding);
882 }
883
884 PXR_NAMESPACE_CLOSE_SCOPE
885