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