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