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/pxr.h"
25 #include "pxr/usd/pcp/instancing.h"
26
27 #include "pxr/base/tf/envSetting.h"
28 #include "pxr/base/tf/smallVector.h"
29 #include "pxr/base/trace/trace.h"
30
31 PXR_NAMESPACE_OPEN_SCOPE
32
33 TF_DEFINE_ENV_SETTING(
34 PCP_OVERRIDE_INSTANCEABLE, -1,
35 "Overrides Pcp's default computation for whether a PrimIndex is "
36 "instanceable:\n"
37 " -1: (the default) computes instanceable only in USD mode\n"
38 " 0: NEVER computes instanceable (always returns false)\n"
39 " 1: always compute instanceable, whether in USD mode or not.");
40
41 // Visitor to determine if a prim index has instanceable data.
42 // This essentially checks if a prim index had a direct composition arc
43 // (e.g. a reference or class) that could be shared with other prims.
44 struct Pcp_FindInstanceableDataVisitor
45 {
Pcp_FindInstanceableDataVisitorPcp_FindInstanceableDataVisitor46 Pcp_FindInstanceableDataVisitor() : hasInstanceableData(false) { }
VisitPcp_FindInstanceableDataVisitor47 bool Visit(PcpNodeRef node, bool nodeIsInstanceable)
48 {
49 if (nodeIsInstanceable) {
50 hasInstanceableData = true;
51 }
52
53 // We're just looking for instanceable data anywhere in the prim
54 // index, so if we've found we can return false to cut off the
55 // traversal.
56 return !hasInstanceableData;
57 }
58
59 bool hasInstanceableData;
60 };
61
62 bool
Pcp_PrimIndexIsInstanceable(const PcpPrimIndex & primIndex)63 Pcp_PrimIndexIsInstanceable(
64 const PcpPrimIndex& primIndex)
65 {
66 TRACE_FUNCTION();
67
68 // For now, instancing functionality is limited to USD mode,
69 // unless the special env var is set for testing.
70 static const int instancing(TfGetEnvSetting(PCP_OVERRIDE_INSTANCEABLE));
71
72 if ((instancing == 0) ||
73 ((!primIndex.IsUsd() && (instancing == -1)))) {
74 return false;
75 }
76
77 // Check if this prim index introduced any instanceable data.
78 // This is a cheap way of determining whether this prim index
79 // *could* be instanced without reading any scene description.
80 //
81 // Note that this means that a prim that is tagged with
82 // 'instanceable = true' will not be considered an instance if it does
83 // not introduce instanceable data.
84 Pcp_FindInstanceableDataVisitor visitor;
85 Pcp_TraverseInstanceableStrongToWeak(primIndex, &visitor);
86 if (!visitor.hasInstanceableData) {
87 return false;
88 }
89
90 // Compose the value of the 'instanceable' metadata to see if this
91 // prim has been tagged as instanceable.
92 bool isInstance = false;
93 static const TfToken instanceField = SdfFieldKeys->Instanceable;
94 // Stack of nodes left to visit, in strong-to-weak order.
95 // Strongest open node is top of the stack.
96 TfSmallVector<PcpNodeRef, 64> nodesToVisit;
97 nodesToVisit.push_back(primIndex.GetRootNode());
98 bool opinionFound = false;
99 while (!nodesToVisit.empty()) {
100 PcpNodeRef node = nodesToVisit.back();
101 nodesToVisit.pop_back();
102 if (node.CanContributeSpecs()) {
103 const PcpLayerStackSite& site = node.GetSite();
104 for (SdfLayerRefPtr const& layer: site.layerStack->GetLayers()) {
105 if (layer->HasField(site.path, instanceField, &isInstance)) {
106 opinionFound = true;
107 break;
108 }
109 }
110 if (opinionFound) {
111 break;
112 }
113 }
114 TF_REVERSE_FOR_ALL(childIt, Pcp_GetChildrenRange(node)) {
115 nodesToVisit.push_back(*childIt);
116 }
117 }
118 return isInstance;
119 }
120
121 PXR_NAMESPACE_CLOSE_SCOPE
122