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