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