1 //
2 // Copyright 2019 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/pxr.h"
25 #include "pxr/usd/usd/primCompositionQuery.h"
26 #include "pxr/usd/usd/stage.h"
27 
28 #include "pxr/usd/pcp/layerStack.h"
29 
30 PXR_NAMESPACE_OPEN_SCOPE
31 
32 /////////////////////////////////////////////
33 // UsdPrimCompositionQueryArc
34 //
35 
UsdPrimCompositionQueryArc(const PcpNodeRef & node)36 UsdPrimCompositionQueryArc::UsdPrimCompositionQueryArc(const PcpNodeRef &node)
37     : _node(node), _originalIntroducedNode(node)
38 {
39     // Only the query itself can construct these, so we expect the node must be
40     // valid
41     if (!TF_VERIFY(_node)) {
42         return;
43     }
44 
45     _originalIntroducedNode = _node;
46 
47     // The root node of introduces itself
48     if (_node.IsRootNode()) {
49         _introducingNode = _node;
50         return;
51     }
52 
53     // In most cases this node's arc originates from its parent node and this
54     // node is the originally introduced node for the arc. But when this node
55     // has a non-parent origin it must be an implicit or copied node that has
56     // not been explicitly added by its parent node. In this case the root of
57     // the origin chain is is originally introduced node of the arc that causes
58     // this node to exist and therefore that node's parent is the introducing
59     // node of this arc.
60     if (_node.GetOriginNode() != _node.GetParentNode()) {
61         _originalIntroducedNode = _node.GetOriginRootNode();
62     }
63     _introducingNode = _originalIntroducedNode.GetParentNode();
64 }
65 
66 PcpNodeRef
GetTargetNode() const67 UsdPrimCompositionQueryArc::GetTargetNode() const
68 {
69     return _node;
70 }
71 
72 PcpNodeRef
GetIntroducingNode() const73 UsdPrimCompositionQueryArc::GetIntroducingNode() const
74 {
75     return _introducingNode;
76 }
77 
78 // The Pcp list op field compose functions differ only by name and result
79 // vector type
80 template <class ResultType>
81 using _PcpComposeFunc = void (*)(PcpLayerStackRefPtr const &,
82                                  SdfPath const &,
83                                  std::vector<ResultType> *,
84                                  PcpSourceArcInfoVector *);
85 
86 // Helper for getting the corresponding list entry and arc source info from
87 // the composed list op of an arc introducing node for all list op types.
88 template <class ResultType>
89 static
90 bool
_GetIntroducingComposeInfo(const UsdPrimCompositionQueryArc & arc,_PcpComposeFunc<ResultType> composeFunc,PcpSourceArcInfo * arcInfo,ResultType * entry)91 _GetIntroducingComposeInfo(const UsdPrimCompositionQueryArc &arc,
92                            _PcpComposeFunc<ResultType> composeFunc,
93                            PcpSourceArcInfo *arcInfo,
94                            ResultType *entry)
95 {
96     // Run the Pcp compose func to get the parallel vectors of composed list
97     // entries and arc source info.
98     PcpSourceArcInfoVector info;
99     std::vector<ResultType> result;
100     composeFunc(arc.GetIntroducingNode().GetLayerStack(),
101                 arc.GetIntroducingPrimPath(),
102                 &result, &info);
103     if (!TF_VERIFY(result.size() == info.size())) {
104         return false;
105     }
106 
107     // We can use the sibling num at origin to find exactly which entry in the
108     // list corresponds to our arc's target node.
109     const int index = arc.GetTargetNode().GetSiblingNumAtOrigin();
110     if (static_cast<size_t>(index) >= info.size()) {
111         TF_CODING_ERROR("Node sibling number of target node is out of range "
112                         "introducing composed list op");
113         return false;
114     }
115 
116     if (arcInfo) {
117         *arcInfo = info[index];
118     }
119     if (entry) {
120         *entry = result[index];
121     }
122     return true;
123 }
124 
125 SdfLayerHandle
GetIntroducingLayer() const126 UsdPrimCompositionQueryArc::GetIntroducingLayer() const
127 {
128     // The arc source info returned by the various Pcp compose functions for
129     // list op fields will hold the layer whose prim spec adds this arc to the
130     // list. Just need to call the correct function for each arc type.
131     PcpSourceArcInfo info;
132     bool foundInfo = false;
133     switch (_node.GetArcType()) {
134     case PcpArcTypeReference:
135         foundInfo = _GetIntroducingComposeInfo<SdfReference>(
136             *this, &PcpComposeSiteReferences, &info, nullptr);
137         break;
138     case PcpArcTypePayload:
139         foundInfo = _GetIntroducingComposeInfo<SdfPayload>(
140             *this, &PcpComposeSitePayloads, &info, nullptr);
141         break;
142     case PcpArcTypeInherit:
143         foundInfo = _GetIntroducingComposeInfo<SdfPath>(
144             *this, &PcpComposeSiteInherits, &info, nullptr);
145         break;
146     case PcpArcTypeSpecialize:
147         foundInfo = _GetIntroducingComposeInfo<SdfPath>(
148             *this, &PcpComposeSiteSpecializes, &info, nullptr);
149         break;
150     case PcpArcTypeVariant:
151         foundInfo = _GetIntroducingComposeInfo<std::string>(
152             *this, &PcpComposeSiteVariantSets, &info, nullptr);
153         break;
154     default:
155         break;
156     }
157     if (foundInfo) {
158         return info.layer;
159     }
160     // Empty layer for root arc and unsupported arc types.
161     return SdfLayerHandle();
162 }
163 
164 SdfPath
GetIntroducingPrimPath() const165 UsdPrimCompositionQueryArc::GetIntroducingPrimPath() const
166 {
167     // Special case for the root node. It doesn't have an introducing prim path.
168     if (_node.IsRootNode()) {
169         return SdfPath();
170     }
171     // We ask the introduced node for its GetIntroPath which gets its parent's
172     // path when it introduced this node. Note that cannot use the introducing
173     // node's GetPathAtIntroduction as that would get the introducing node's
174     // path when it itself was introduced by its own parent.
175     return _originalIntroducedNode.GetIntroPath();
176 }
177 
178 // Returns the introducing prim spec for the arc given the composed source
179 // arc info.
180 static
181 SdfPrimSpecHandle
_GetIntroducingPrimSpec(const UsdPrimCompositionQueryArc & arc,const PcpSourceArcInfo & info)182 _GetIntroducingPrimSpec(const UsdPrimCompositionQueryArc &arc,
183                         const PcpSourceArcInfo &info)
184 {
185     return info.layer->GetPrimAtPath(arc.GetIntroducingPrimPath());
186 }
187 
188 bool
GetIntroducingListEditor(SdfReferenceEditorProxy * editor,SdfReference * ref) const189 UsdPrimCompositionQueryArc::GetIntroducingListEditor(
190     SdfReferenceEditorProxy *editor, SdfReference *ref) const
191 {
192     if (GetArcType() != PcpArcTypeReference) {
193         TF_CODING_ERROR("Cannot retrieve a reference list editor and reference "
194                         "for arc types other than PcpArcTypeReference");
195         return false;
196     }
197 
198     // Compose the references on the introducing node.
199     PcpSourceArcInfo info;
200     if (!_GetIntroducingComposeInfo<SdfReference>(
201         *this, &PcpComposeSiteReferences, &info, ref)) {
202         return false;
203     }
204     // Get the refence editor from the prim spec.
205     *editor = _GetIntroducingPrimSpec(*this, info)->GetReferenceList();
206     // The composed reference has its asset path and layer offset resolved.
207     // We want the reference we return to be the authored value in the list op
208     // itself which we can get back from the source arc info.
209     ref->SetAssetPath(info.authoredAssetPath);
210     ref->SetLayerOffset(info.layerOffset);
211     return true;
212 }
213 
214 bool
GetIntroducingListEditor(SdfPayloadEditorProxy * editor,SdfPayload * payload) const215 UsdPrimCompositionQueryArc::GetIntroducingListEditor(
216     SdfPayloadEditorProxy *editor, SdfPayload *payload) const
217 {
218     if (GetArcType() != PcpArcTypePayload) {
219         TF_CODING_ERROR("Cannot retrieve a payload list editor and payload "
220                         "for arc types other than PcpArcTypePayload");
221         return false;
222     }
223 
224     // Compose the payloads on the introducing node.
225     PcpSourceArcInfo info;
226     if (!_GetIntroducingComposeInfo<SdfPayload>(
227         *this, &PcpComposeSitePayloads, &info, payload)) {
228         return false;
229     }
230     // Get the payload editor from the prim spec.
231     *editor = _GetIntroducingPrimSpec(*this, info)->GetPayloadList();
232     // The composed payload has its asset path and layer offset resolved.
233     // We want the payload we return to be the authored value in the list op
234     // itself which we can get back from the source arc info.
235     payload->SetAssetPath(info.authoredAssetPath);
236     payload->SetLayerOffset(info.layerOffset);
237     return true;
238 }
239 
240 bool
GetIntroducingListEditor(SdfPathEditorProxy * editor,SdfPath * path) const241 UsdPrimCompositionQueryArc::GetIntroducingListEditor(
242     SdfPathEditorProxy *editor, SdfPath *path) const
243 {
244     if (GetArcType() != PcpArcTypeInherit &&
245         GetArcType() != PcpArcTypeSpecialize) {
246         TF_CODING_ERROR("Cannot retrieve a path list editor and path "
247                         "for arc types other than PcpArcTypeInherit and "
248                         "PcpArcTypeSpecialize");
249         return false;
250     }
251 
252     PcpSourceArcInfo info;
253     if (GetArcType() == PcpArcTypeInherit) {
254         // Compose the inherit paths on the introducing node.
255         if (!_GetIntroducingComposeInfo<SdfPath>(
256             *this, &PcpComposeSiteInherits, &info, path)) {
257             return false;
258         }
259         // Get the inherit path editor from the prim spec.
260         *editor = _GetIntroducingPrimSpec(*this, info)->GetInheritPathList();
261     } else {
262         // Compose the specialize paths on the introducing node.
263         if (!_GetIntroducingComposeInfo<SdfPath>(
264             *this, &PcpComposeSiteSpecializes, &info, path)) {
265             return false;
266         }
267         // Get the specialize path editor from the prim spec.
268         *editor = _GetIntroducingPrimSpec(*this, info)->GetSpecializesList();
269     }
270 
271     return true;
272 }
273 
274 bool
GetIntroducingListEditor(SdfNameEditorProxy * editor,std::string * name) const275 UsdPrimCompositionQueryArc::GetIntroducingListEditor(
276     SdfNameEditorProxy *editor, std::string *name) const
277 {
278     if (GetArcType() != PcpArcTypeVariant) {
279         TF_CODING_ERROR("Cannot retrieve a name list editor and name "
280                         "for arc types other than PcpArcTypeVariant");
281         return false;
282     }
283 
284     // Compose the variant set names on the introducing node.
285     PcpSourceArcInfo info;
286     if (!_GetIntroducingComposeInfo<std::string>(
287         *this, &PcpComposeSiteVariantSets, &info, name)) {
288         return false;
289     }
290     // Get the variant set name editor from the prim spec.
291     *editor = _GetIntroducingPrimSpec(*this, info)->GetVariantSetNameList();
292     return true;
293 }
294 
295 
296 PcpArcType
GetArcType() const297 UsdPrimCompositionQueryArc::GetArcType() const
298 {
299     return _node.GetArcType();
300 }
301 
302 bool
IsImplicit() const303 UsdPrimCompositionQueryArc::IsImplicit() const
304 {
305     // An implicit node is a node that wasn't introduced by its parent and
306     // has a different site than its origin node. This is distinguished from
307     // explicit nodes (which are introduced by their parents) and copied nodes
308     // (which have been copied directly from their origins for strength
309     // ordering)
310     return !_node.IsRootNode() &&
311         _node.GetParentNode() != _introducingNode &&
312         _node.GetOriginNode().GetSite() != _node.GetSite();
313 }
314 
315 bool
IsAncestral() const316 UsdPrimCompositionQueryArc::IsAncestral() const
317 {
318     return _node.IsDueToAncestor();
319 }
320 
321 bool
HasSpecs() const322 UsdPrimCompositionQueryArc::HasSpecs() const
323 {
324     return _node.HasSpecs();
325 }
326 
327 bool
IsIntroducedInRootLayerStack() const328 UsdPrimCompositionQueryArc::IsIntroducedInRootLayerStack() const
329 {
330     // We say the root node of the graph is always introduced in the root layer
331     // stack
332     if (_node.IsRootNode()) {
333         return true;
334     }
335     // We can't just compare the introducing layer stack with the root
336     // node layer stack directly as a reference or payload that specifically
337     // targets the root layer by name will have a layer stack that does not
338     // contain a session layer. This means that its layer stack won't
339     // necessarily exactly match the root node's layer stack which may have a
340     // session layer. Thus we compare just the root layers of the stacks which
341     // is semantically what we're lookin for here.
342     return _introducingNode.GetLayerStack()->GetIdentifier().rootLayer ==
343          _node.GetRootNode().GetLayerStack()->GetIdentifier().rootLayer;
344 }
345 
346 bool
IsIntroducedInRootLayerPrimSpec() const347 UsdPrimCompositionQueryArc::IsIntroducedInRootLayerPrimSpec() const
348 {
349     return _introducingNode.IsRootNode();
350 }
351 
352 /////////////////////////////////////////////
353 // UsdPrimCompositionQuery
354 //
355 
UsdPrimCompositionQuery(const UsdPrim & prim,const Filter & filter)356 UsdPrimCompositionQuery::UsdPrimCompositionQuery(const UsdPrim & prim,
357                                                  const Filter &filter)
358     : _prim(prim), _filter(filter)
359 {
360     // We need the unculled prim index so that we can query all possible
361     // composition dependencies even if they don't currently contribute
362     // opinions.
363     _expandedPrimIndex = _prim.ComputeExpandedPrimIndex();
364 
365     // Compute the unfiltered list of composition arcs from all non-inert nodes.
366     // We still skip inert nodes in the unfiltered query so we don't pick up
367     // things like the original copies of specialize nodes that have been
368     // moved for strength ordering purposes.
369     for(const PcpNodeRef &node: _expandedPrimIndex.GetNodeRange()) {
370         if (!node.IsInert()) {
371             _unfilteredArcs.push_back(UsdPrimCompositionQueryArc(node));
372         }
373     }
374 }
375 
376 /*static*/
GetDirectReferences(const UsdPrim & prim)377 UsdPrimCompositionQuery UsdPrimCompositionQuery::GetDirectReferences(
378     const UsdPrim & prim)
379 {
380     Filter filter;
381     filter.dependencyTypeFilter = DependencyTypeFilter::Direct;
382     filter.arcTypeFilter = ArcTypeFilter::Reference;
383     return UsdPrimCompositionQuery(prim, filter);
384 }
385 
386 /*static*/
GetDirectInherits(const UsdPrim & prim)387 UsdPrimCompositionQuery UsdPrimCompositionQuery::GetDirectInherits(
388     const UsdPrim & prim)
389 {
390     Filter filter;
391     filter.dependencyTypeFilter = DependencyTypeFilter::Direct;
392     filter.arcTypeFilter = ArcTypeFilter::Inherit;
393     return UsdPrimCompositionQuery(prim, filter);
394 }
395 
396 /*static*/
GetDirectRootLayerArcs(const UsdPrim & prim)397 UsdPrimCompositionQuery UsdPrimCompositionQuery::GetDirectRootLayerArcs(
398     const UsdPrim & prim)
399 {
400     Filter filter;
401     filter.dependencyTypeFilter = DependencyTypeFilter::Direct;
402     filter.arcIntroducedFilter = ArcIntroducedFilter::IntroducedInRootLayerStack;
403     return UsdPrimCompositionQuery(prim, filter);
404 }
405 
SetFilter(const Filter & filter)406 void UsdPrimCompositionQuery::SetFilter(const Filter &filter)
407 {
408     _filter = filter;
409 }
410 
GetFilter() const411 UsdPrimCompositionQuery::Filter UsdPrimCompositionQuery::GetFilter() const
412 {
413     return _filter;
414 }
415 
416 static bool
_TestArcType(const UsdPrimCompositionQueryArc & compArc,const UsdPrimCompositionQuery::Filter & filter)417 _TestArcType(const UsdPrimCompositionQueryArc &compArc,
418              const UsdPrimCompositionQuery::Filter &filter)
419 {
420     using ArcTypeFilter = UsdPrimCompositionQuery::ArcTypeFilter;
421 
422     // Convert to a bit mask so we filter by multiple arc types.
423     int arcMask = 0;
424     switch (filter.arcTypeFilter) {
425     case ArcTypeFilter::All:
426         return true;
427     case ArcTypeFilter::Reference:
428         arcMask = 1 << PcpArcTypeReference;
429         break;
430     case ArcTypeFilter::Payload:
431         arcMask = 1 << PcpArcTypePayload;
432         break;
433     case ArcTypeFilter::Inherit:
434         arcMask = 1 << PcpArcTypeInherit;
435         break;
436     case ArcTypeFilter::Specialize:
437         arcMask = 1 << PcpArcTypeSpecialize;
438         break;
439     case ArcTypeFilter::Variant:
440         arcMask = 1 << PcpArcTypeVariant;
441         break;
442     case ArcTypeFilter::ReferenceOrPayload:
443         arcMask = (1 << PcpArcTypeReference) | (1 << PcpArcTypePayload);
444         break;
445     case ArcTypeFilter::InheritOrSpecialize:
446         arcMask = (1 << PcpArcTypeInherit) | (1 << PcpArcTypeSpecialize);
447         break;
448     case ArcTypeFilter::NotReferenceOrPayload:
449         arcMask = ~((1 << PcpArcTypeReference) | (1 << PcpArcTypePayload));
450         break;
451     case ArcTypeFilter::NotInheritOrSpecialize:
452         arcMask = ~((1 << PcpArcTypeInherit) | (1 << PcpArcTypeSpecialize));
453         break;
454     case ArcTypeFilter::NotVariant:
455         arcMask = ~(1 << PcpArcTypeVariant);
456         break;
457     }
458 
459     return arcMask & (1 << compArc.GetArcType());
460 }
461 
462 
463 static bool
_TestDependencyType(const UsdPrimCompositionQueryArc & compArc,const UsdPrimCompositionQuery::Filter & filter)464 _TestDependencyType(const UsdPrimCompositionQueryArc &compArc,
465                     const UsdPrimCompositionQuery::Filter &filter)
466 {
467     using DependencyTypeFilter = UsdPrimCompositionQuery::DependencyTypeFilter;
468 
469     switch (filter.dependencyTypeFilter) {
470     case DependencyTypeFilter::All:
471         return true;
472     case DependencyTypeFilter::Direct:
473         return !compArc.IsAncestral();
474     case DependencyTypeFilter::Ancestral:
475         return compArc.IsAncestral();
476     };
477     return true;
478 }
479 
480 static bool
_TestArcIntroduced(const UsdPrimCompositionQueryArc & compArc,const UsdPrimCompositionQuery::Filter & filter)481 _TestArcIntroduced(const UsdPrimCompositionQueryArc &compArc,
482                    const UsdPrimCompositionQuery::Filter &filter)
483 {
484     using ArcIntroducedFilter = UsdPrimCompositionQuery::ArcIntroducedFilter;
485 
486     switch (filter.arcIntroducedFilter) {
487     case ArcIntroducedFilter::All:
488         return true;
489     case ArcIntroducedFilter::IntroducedInRootLayerStack:
490         return compArc.IsIntroducedInRootLayerStack();
491     case ArcIntroducedFilter::IntroducedInRootLayerPrimSpec:
492         return compArc.IsIntroducedInRootLayerPrimSpec();
493     };
494     return true;
495 }
496 
497 static bool
_TestHasSpecs(const UsdPrimCompositionQueryArc & compArc,const UsdPrimCompositionQuery::Filter & filter)498 _TestHasSpecs(const UsdPrimCompositionQueryArc &compArc,
499               const UsdPrimCompositionQuery::Filter &filter)
500 {
501     using HasSpecsFilter = UsdPrimCompositionQuery::HasSpecsFilter;
502 
503     switch (filter.hasSpecsFilter) {
504     case HasSpecsFilter::All:
505         return true;
506     case HasSpecsFilter::HasSpecs:
507         return compArc.HasSpecs();
508     case HasSpecsFilter::HasNoSpecs:
509         return !compArc.HasSpecs();
510     };
511     return true;
512 }
513 
514 std::vector<UsdPrimCompositionQueryArc>
GetCompositionArcs()515 UsdPrimCompositionQuery::GetCompositionArcs()
516 {
517     // Create a list of the filter test functions we actually need to run;
518     // there's no point in testing filters that include all.
519     using _TestFunc =
520         std::function<bool (const UsdPrimCompositionQueryArc &)> ;
521 
522     std::vector<_TestFunc> filterTests;
523     if (_filter.arcTypeFilter != ArcTypeFilter::All) {
524         filterTests.push_back(std::bind(_TestArcType,
525             std::placeholders::_1, _filter));
526     }
527     if (_filter.dependencyTypeFilter != DependencyTypeFilter::All) {
528         filterTests.push_back(std::bind(_TestDependencyType,
529             std::placeholders::_1, _filter));
530     }
531     if (_filter.arcIntroducedFilter != ArcIntroducedFilter::All) {
532         filterTests.push_back(std::bind(_TestArcIntroduced,
533             std::placeholders::_1, _filter));
534     }
535     if (_filter.hasSpecsFilter != HasSpecsFilter::All) {
536         filterTests.push_back(std::bind(_TestHasSpecs,
537             std::placeholders::_1, _filter));
538     }
539 
540     // No test, return unfiltered resuslts.
541     if (filterTests.empty()) {
542         return _unfilteredArcs;
543     }
544 
545     // Runs the filter tests on an arc, failing in any test fails
546     auto _RunFilterTests =
547         [&filterTests](const UsdPrimCompositionQueryArc &compArc)
548         {
549             for (auto test : filterTests) {
550                 if (!test(compArc)) {
551                     return false;
552                 }
553             }
554             return true;
555         };
556 
557     // Create the filtered arc list from the unfiltered arcs.
558     std::vector<UsdPrimCompositionQueryArc> result;
559     for (const UsdPrimCompositionQueryArc &compArc : _unfilteredArcs) {
560         if (_RunFilterTests(compArc)) {
561             result.push_back(compArc);
562         }
563     }
564     return result;
565 }
566 
567 PXR_NAMESPACE_CLOSE_SCOPE
568 
569