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 #ifndef PXR_USD_PCP_INSTANCING_H
25 #define PXR_USD_PCP_INSTANCING_H
26
27 /// \file pcp/instancing.h
28 ///
29 /// A collection of private helper utilities to support instancing
30 /// functionality.
31
32 #include "pxr/pxr.h"
33 #include "pxr/usd/pcp/composeSite.h"
34 #include "pxr/usd/pcp/node_Iterator.h"
35 #include "pxr/usd/pcp/primIndex.h"
36
37 PXR_NAMESPACE_OPEN_SCOPE
38
39 /// Helper function to determine whether the given prim index is
40 /// instanceable. An instanceable prim index must have instanceable
41 /// nodes and must have been tagged so that the composed value of
42 /// the metadata field 'instance' is true.
43 bool
44 Pcp_PrimIndexIsInstanceable(
45 const PcpPrimIndex& primIndex);
46
47 /// Helper function for traversing a prim index in strong-to-weak
48 /// order while identifying instanceable nodes. This function passes
49 /// each node in \p primIndex to the supplied \p visitor along with
50 /// a flag indicating whether that node is instanceable.
51 ///
52 /// The visitor needs to implement this interface:
53 ///
54 /// struct Visitor {
55 /// bool Visit(PcpNodeRef node, bool nodeIsInstanceable)
56 /// { ... }
57 /// }
58 ///
59 /// If the Visit function returns false, traversal will be pruned at that
60 /// node and none of the node's children will be visited.
61 ///
62 template <class Visitor>
63 void
64 Pcp_TraverseInstanceableStrongToWeak(
65 const PcpPrimIndex& primIndex,
66 Visitor* visitor);
67
68 /// Helper function for traversing a prim index in weak-to-strong
69 /// order while identifying instanceable nodes. This function passes
70 /// each node in \p primIndex to the supplied \p visitor along with
71 /// a flag indicating whether that node is instanceable.
72 ///
73 /// The visitor needs to implement this interface:
74 ///
75 /// struct Visitor {
76 /// void Visit(PcpNodeRef node, bool nodeIsInstanceable)
77 /// { ... }
78 /// }
79 ///
80 template <class Visitor>
81 void
82 Pcp_TraverseInstanceableWeakToStrong(
83 const PcpPrimIndex& primIndex,
84 Visitor* visitor);
85
86 // Implementation ----------------------------------------
87
88 inline bool
Pcp_ChildNodeIsInstanceable(const PcpNodeRef & node,bool * hasAnyDirectArcsInNodeChain)89 Pcp_ChildNodeIsInstanceable(
90 const PcpNodeRef& node,
91 bool *hasAnyDirectArcsInNodeChain)
92 {
93 // Non-ancestral nodes are instanceable: they represent a direct
94 // composition arc to a portion of scenegraph that could be shared
95 // with other prim indexes, as long as the other criteria laid out
96 // in PcpInstanceKey are met. But there may also be ancestral nodes that
97 // exist in the graph because they were composed in a subtree of direct
98 // arc to a subroot path. These nodes are also instanceable as they are
99 // considered part of the direct arc that brought them in. This is why we
100 // keep track of and check whether there are any direct arcs in the node's
101 // chain up to the root node when determining if a node is instanceable.
102 *hasAnyDirectArcsInNodeChain =
103 *hasAnyDirectArcsInNodeChain || !node.IsDueToAncestor();
104
105 // If a node has no specs, we do not consider it instanceable since
106 // it has no opinions to contribute to the prim index. In particular,
107 // this allows prim indexes with implied arcs in different layer stacks
108 // that have no overrides to still be considered equivalent for sharing.
109 return *hasAnyDirectArcsInNodeChain && node.HasSpecs();
110 }
111
112 inline bool
Pcp_ChildNodeIsDirectOrInDirectArcSubtree(const PcpNodeRef & node)113 Pcp_ChildNodeIsDirectOrInDirectArcSubtree(
114 const PcpNodeRef& node)
115 {
116 if (node.IsRootNode() || !node.IsDueToAncestor()) {
117 return true;
118 }
119 for (PcpNodeRef parent = node.GetParentNode();
120 !parent.IsRootNode();
121 parent = parent.GetParentNode()) {
122 if (!parent.IsDueToAncestor()) {
123 return true;
124 }
125 }
126 return false;
127 }
128
129 inline bool
Pcp_ChildNodeInstanceableChanged(const PcpNodeRef & node)130 Pcp_ChildNodeInstanceableChanged(
131 const PcpNodeRef& node)
132 {
133 return Pcp_ChildNodeIsDirectOrInDirectArcSubtree(node) &&
134 (PcpComposeSiteHasPrimSpecs(node) != node.HasSpecs());
135 }
136
137 template <class Visitor>
138 inline void
Pcp_TraverseInstanceableStrongToWeakHelper(const PcpNodeRef & node,Visitor * visitor,bool hasAnyDirectArcsInNodeChain)139 Pcp_TraverseInstanceableStrongToWeakHelper(
140 const PcpNodeRef& node,
141 Visitor* visitor,
142 bool hasAnyDirectArcsInNodeChain)
143 {
144 // If the node is culled, the entire subtree rooted at this node
145 // does not contribute to the prim index, so we can prune the
146 // traversal.
147 if (node.IsCulled()) {
148 return;
149 }
150
151 const bool isInstanceable =
152 Pcp_ChildNodeIsInstanceable(node, &hasAnyDirectArcsInNodeChain);
153 if (!visitor->Visit(node, isInstanceable)) {
154 return;
155 }
156
157 TF_FOR_ALL(childIt, Pcp_GetChildrenRange(node)) {
158 const PcpNodeRef& childNode = *childIt;
159 Pcp_TraverseInstanceableStrongToWeakHelper(
160 childNode, visitor, hasAnyDirectArcsInNodeChain);
161 }
162 }
163
164 template <class Visitor>
165 inline void
Pcp_TraverseInstanceableStrongToWeak(const PcpPrimIndex & primIndex,Visitor * visitor)166 Pcp_TraverseInstanceableStrongToWeak(
167 const PcpPrimIndex& primIndex,
168 Visitor* visitor)
169 {
170 const PcpNodeRef& rootNode = primIndex.GetRootNode();
171 if (!visitor->Visit(rootNode, /* nodeIsInstanceable = */ false)) {
172 return;
173 }
174
175 TF_FOR_ALL(childIt, Pcp_GetChildrenRange(rootNode)) {
176 const PcpNodeRef& childNode = *childIt;
177 Pcp_TraverseInstanceableStrongToWeakHelper(
178 childNode, visitor, /* hasAnyDirectArcsInNodeChain = */ false);
179 }
180 }
181
182 template <class Visitor>
183 inline void
Pcp_TraverseInstanceableWeakToStrongHelper(const PcpNodeRef & node,Visitor * visitor,bool hasAnyDirectArcsInNodeChain)184 Pcp_TraverseInstanceableWeakToStrongHelper(
185 const PcpNodeRef& node,
186 Visitor* visitor,
187 bool hasAnyDirectArcsInNodeChain)
188 {
189 // If the node is culled, the entire subtree rooted at this node
190 // does not contribute to the prim index, so we can prune the
191 // traversal.
192 if (node.IsCulled()) {
193 return;
194 }
195
196 const bool isInstanceable =
197 Pcp_ChildNodeIsInstanceable(node, &hasAnyDirectArcsInNodeChain);
198
199 TF_REVERSE_FOR_ALL(childIt, Pcp_GetChildrenRange(node)) {
200 const PcpNodeRef& childNode = *childIt;
201 Pcp_TraverseInstanceableWeakToStrongHelper(
202 childNode, visitor, hasAnyDirectArcsInNodeChain);
203 }
204
205 visitor->Visit(node, isInstanceable);
206 }
207
208 template <class Visitor>
209 inline void
Pcp_TraverseInstanceableWeakToStrong(const PcpPrimIndex & primIndex,Visitor * visitor)210 Pcp_TraverseInstanceableWeakToStrong(
211 const PcpPrimIndex& primIndex,
212 Visitor* visitor)
213 {
214 const PcpNodeRef& rootNode = primIndex.GetRootNode();
215 TF_REVERSE_FOR_ALL(childIt, Pcp_GetChildrenRange(rootNode)) {
216 const PcpNodeRef& childNode = *childIt;
217 Pcp_TraverseInstanceableWeakToStrongHelper(
218 childNode, visitor, /* hasAnyDirectArcsInNodeChain = */ false);
219 }
220
221 visitor->Visit(rootNode, /* nodeIsInstanceable = */ false);
222 }
223
224 PXR_NAMESPACE_CLOSE_SCOPE
225
226 #endif // PXR_USD_PCP_INSTANCING_H
227