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/usd/usdSkel/cacheImpl.h"
25 
26 #include "pxr/base/arch/hints.h"
27 
28 #include "pxr/usd/usd/attributeQuery.h"
29 #include "pxr/usd/usd/primRange.h"
30 
31 #include "pxr/usd/usdGeom/boundable.h"
32 #include "pxr/usd/usdGeom/imageable.h"
33 
34 #include "pxr/usd/usdSkel/bindingAPI.h"
35 #include "pxr/usd/usdSkel/debugCodes.h"
36 #include "pxr/usd/usdSkel/animation.h"
37 #include "pxr/usd/usdSkel/root.h"
38 #include "pxr/usd/usdSkel/utils.h"
39 
40 PXR_NAMESPACE_OPEN_SCOPE
41 
42 // ------------------------------------------------------------
43 // UsdSkel_CacheImpl::WriteScope
44 // ------------------------------------------------------------
45 
46 
WriteScope(UsdSkel_CacheImpl * cache)47 UsdSkel_CacheImpl::WriteScope::WriteScope(UsdSkel_CacheImpl* cache)
48     : _cache(cache), _lock(cache->_mutex, /*write*/ true)
49 {}
50 
51 
52 void
Clear()53 UsdSkel_CacheImpl::WriteScope::Clear()
54 {
55     _cache->_animQueryCache.clear();
56     _cache->_skelDefinitionCache.clear();
57     _cache->_skelQueryCache.clear();
58     _cache->_primSkinningQueryCache.clear();
59 }
60 
61 
62 // ------------------------------------------------------------
63 // UsdSkel_CacheImpl::ReadScope
64 // ------------------------------------------------------------
65 
66 
ReadScope(UsdSkel_CacheImpl * cache)67 UsdSkel_CacheImpl::ReadScope::ReadScope(UsdSkel_CacheImpl* cache)
68     : _cache(cache), _lock(cache->_mutex, /*write*/ false)
69 {}
70 
71 
72 UsdSkelAnimQuery
FindOrCreateAnimQuery(const UsdPrim & prim)73 UsdSkel_CacheImpl::ReadScope::FindOrCreateAnimQuery(const UsdPrim& prim)
74 {
75     TRACE_FUNCTION();
76 
77     if (ARCH_UNLIKELY(!prim || !prim.IsActive()))
78         return UsdSkelAnimQuery();
79 
80     if (prim.IsInstanceProxy())
81         return FindOrCreateAnimQuery(prim.GetPrimInPrototype());
82 
83     {
84         _PrimToAnimMap::const_accessor a;
85         if (_cache->_animQueryCache.find(a, prim))
86             return UsdSkelAnimQuery(a->second);
87     }
88 
89     if (UsdSkelIsSkelAnimationPrim(prim)) {
90         _PrimToAnimMap::accessor a;
91         if (_cache->_animQueryCache.insert(a, prim)) {
92             a->second = UsdSkel_AnimQueryImpl::New(prim);
93         }
94         return UsdSkelAnimQuery(a->second);
95     }
96     return UsdSkelAnimQuery();
97 }
98 
99 
100 UsdSkel_SkelDefinitionRefPtr
FindOrCreateSkelDefinition(const UsdPrim & prim)101 UsdSkel_CacheImpl::ReadScope::FindOrCreateSkelDefinition(const UsdPrim& prim)
102 {
103     TRACE_FUNCTION();
104 
105     if (ARCH_UNLIKELY(!prim || !prim.IsActive()))
106         return nullptr;
107 
108     if (prim.IsInstanceProxy())
109         return FindOrCreateSkelDefinition(prim.GetPrimInPrototype());
110 
111     {
112         _PrimToSkelDefinitionMap::const_accessor a;
113         if (_cache->_skelDefinitionCache.find(a, prim))
114             return a->second;
115     }
116 
117     if (prim.IsA<UsdSkelSkeleton>()) {
118         _PrimToSkelDefinitionMap::accessor a;
119         if (_cache->_skelDefinitionCache.insert(a, prim)) {
120             a->second = UsdSkel_SkelDefinition::New(UsdSkelSkeleton(prim));
121         }
122         return a->second;
123     }
124     return nullptr;
125 }
126 
127 
128 UsdSkelSkeletonQuery
FindOrCreateSkelQuery(const UsdPrim & prim)129 UsdSkel_CacheImpl::ReadScope::FindOrCreateSkelQuery(const UsdPrim& prim)
130 {
131     TRACE_FUNCTION();
132 
133     {
134         _PrimToSkelQueryMap::const_accessor a;
135         if (_cache->_skelQueryCache.find(a, prim))
136             return a->second;
137     }
138 
139     if (auto skelDef = FindOrCreateSkelDefinition(prim)) {
140         _PrimToSkelQueryMap::accessor a;
141         if (_cache->_skelQueryCache.insert(a, prim)) {
142 
143             UsdSkelAnimQuery animQuery =
144                 FindOrCreateAnimQuery(
145                     UsdSkelBindingAPI(prim).GetInheritedAnimationSource());
146 
147             a->second = UsdSkelSkeletonQuery(skelDef, animQuery);
148         }
149         return a->second;
150     }
151     return UsdSkelSkeletonQuery();
152 }
153 
154 
155 UsdSkelSkinningQuery
GetSkinningQuery(const UsdPrim & prim) const156 UsdSkel_CacheImpl::ReadScope::GetSkinningQuery(const UsdPrim& prim) const
157 {
158     _PrimToSkinningQueryMap::const_accessor a;
159     if (_cache->_primSkinningQueryCache.find(a, prim))
160         return a->second;
161     return UsdSkelSkinningQuery();
162 }
163 
164 
165 UsdSkelSkinningQuery
_FindOrCreateSkinningQuery(const UsdPrim & skinnedPrim,const _SkinningQueryKey & key)166 UsdSkel_CacheImpl::ReadScope::_FindOrCreateSkinningQuery(
167     const UsdPrim& skinnedPrim,
168     const _SkinningQueryKey& key)
169 {
170     UsdSkelSkeletonQuery skelQuery = FindOrCreateSkelQuery(key.skel);
171     const UsdSkelAnimQuery& animQuery = skelQuery.GetAnimQuery();
172 
173     // TODO: Consider some form of deduplication.
174     return UsdSkelSkinningQuery(
175         skinnedPrim,
176         skelQuery ? skelQuery.GetJointOrder() : VtTokenArray(),
177         animQuery ? animQuery.GetBlendShapeOrder() : VtTokenArray(),
178         key.jointIndicesAttr, key.jointWeightsAttr,
179         key.geomBindTransformAttr, key.jointsAttr,
180         key.blendShapesAttr, key.blendShapeTargetsRel);
181 }
182 
183 
184 namespace {
185 
186 /// Create a string representing an indent.
187 std::string
_MakeIndent(size_t count,int indentSize=2)188 _MakeIndent(size_t count, int indentSize=2)
189 {
190     return std::string(count*indentSize, ' ');
191 }
192 
193 void
_DeprecatedBindingCheck(bool hasBindingAPI,const UsdProperty & prop)194 _DeprecatedBindingCheck(bool hasBindingAPI, const UsdProperty& prop)
195 {
196     if (!hasBindingAPI) {
197         TF_WARN("Found binding property <%s>, but the SkelBindingAPI was not "
198                 "applied on the owning prim. In the future, binding properties "
199                 "will be ignored unless the SkelBindingAPI is applied "
200                 "(see UsdSkelBindingAPI::Apply)", prop.GetPath().GetText());
201     }
202 }
203 
204 /// If \p attr is an attribute on an instance proxy, return the attr on the
205 /// instance prototype. Otherwise return the original attr.
206 UsdAttribute
_GetAttrInPrototype(const UsdAttribute & attr)207 _GetAttrInPrototype(const UsdAttribute& attr)
208 {
209     if (attr && attr.GetPrim().IsInstanceProxy()) {
210         return attr.GetPrim().GetPrimInPrototype().GetAttribute(
211             attr.GetName());
212     }
213     return attr;
214 }
215 
216 /// If \p rel is an attribute on an instance proxy, return the rel on the
217 /// instance prototype. Otherwise return the original rel.
218 UsdRelationship
_GetRelInPrototype(const UsdRelationship & rel)219 _GetRelInPrototype(const UsdRelationship& rel)
220 {
221     if (rel && rel.GetPrim().IsInstanceProxy()) {
222         return rel.GetPrim().GetPrimInPrototype().GetRelationship(
223             rel.GetName());
224     }
225     return rel;
226 }
227 
228 } // namespace
229 
230 
231 bool
Populate(const UsdSkelRoot & root,Usd_PrimFlagsPredicate predicate)232 UsdSkel_CacheImpl::ReadScope::Populate(const UsdSkelRoot& root,
233                                        Usd_PrimFlagsPredicate predicate)
234 {
235     TRACE_FUNCTION();
236 
237     TF_DEBUG(USDSKEL_CACHE).Msg("[UsdSkelCache] Populate map from <%s>\n",
238                                 root.GetPrim().GetPath().GetText());
239 
240     if (!root) {
241         TF_CODING_ERROR("'root' is invalid.");
242         return false;
243     }
244 
245     std::vector<std::pair<_SkinningQueryKey,UsdPrim> > stack(1);
246 
247     const UsdPrimRange range =
248         UsdPrimRange::PreAndPostVisit(root.GetPrim(), predicate);
249 
250     for (auto it = range.begin(); it != range.end(); ++it) {
251 
252         if (it.IsPostVisit()) {
253             if (stack.size() > 0 && stack.back().second == *it) {
254                 stack.pop_back();
255             }
256             continue;
257         }
258 
259         if (ARCH_UNLIKELY(!it->IsA<UsdGeomImageable>())) {
260             TF_DEBUG(USDSKEL_CACHE).Msg(
261                 "[UsdSkelCache]  %sPruning traversal at <%s> "
262                 "(prim is not UsdGeomImageable)\n",
263                 _MakeIndent(stack.size()).c_str(), it->GetPath().GetText());
264 
265             it.PruneChildren();
266             continue;
267         }
268 
269         // XXX: For backwards-compatibility, must potentially look for
270         // UsdSkelBindingAPI properties, even if the API schema was not
271         // applied to the prim.
272 
273         const bool hasBindingAPI = it->HasAPI<UsdSkelBindingAPI>();
274 
275         _SkinningQueryKey key(stack.back().first);
276 
277         const UsdSkelBindingAPI binding(*it);
278 
279         UsdSkelSkeleton skel;
280         if (binding.GetSkeleton(&skel)) {
281             key.skel = skel.GetPrim();
282         }
283 
284         // XXX: When looking for binding properties, only include
285         // properties that have an authored value. Properties with
286         // no authored value are treated as if they do not exist.
287 
288         if (UsdAttribute attr = _GetAttrInPrototype(
289                 binding.GetJointIndicesAttr())) {
290             if (attr.HasAuthoredValue()) {
291                 _DeprecatedBindingCheck(hasBindingAPI, attr);
292                 key.jointIndicesAttr = std::move(attr);
293             }
294         }
295 
296         if (UsdAttribute attr = _GetAttrInPrototype(
297                 binding.GetJointWeightsAttr())) {
298             if (attr.HasAuthoredValue()) {
299                 _DeprecatedBindingCheck(hasBindingAPI, attr);
300                 key.jointWeightsAttr = std::move(attr);
301             }
302         }
303 
304         if (UsdAttribute attr = _GetAttrInPrototype(
305                 binding.GetGeomBindTransformAttr())) {
306             if (attr.HasAuthoredValue()) {
307                 _DeprecatedBindingCheck(hasBindingAPI, attr);
308                 key.geomBindTransformAttr = std::move(attr);
309             }
310         }
311 
312         if (UsdAttribute attr = _GetAttrInPrototype(
313                 binding.GetJointsAttr())) {
314             if (attr.HasAuthoredValue()) {
315                 _DeprecatedBindingCheck(hasBindingAPI, attr);
316                 key.jointsAttr = std::move(attr);
317             }
318         }
319 
320         const bool isSkinnable = UsdSkelIsSkinnablePrim(*it);
321 
322         // Unlike other binding properties above, skel:blendShapes and
323         // skel:blendShapeTargets are *not* inherited, so we only check
324         // for them on skinnable prims.
325         if (isSkinnable) {
326             if (UsdAttribute attr = _GetAttrInPrototype(
327                     binding.GetBlendShapesAttr())) {
328                 if (attr.HasAuthoredValue()) {
329                     _DeprecatedBindingCheck(hasBindingAPI, attr);
330                     key.blendShapesAttr = std::move(attr);
331                 }
332             }
333 
334             if (UsdRelationship rel = _GetRelInPrototype(
335                     binding.GetBlendShapeTargetsRel())) {
336                 if (rel.HasAuthoredTargets()) {
337                     _DeprecatedBindingCheck(hasBindingAPI, rel);
338                     key.blendShapeTargetsRel = std::move(rel);
339                 }
340             }
341         }
342 
343         if (isSkinnable) {
344             // Append a skinning query using the resolved binding properties.
345 
346             _PrimToSkinningQueryMap::accessor a;
347             if (_cache->_primSkinningQueryCache.insert(a, *it)) {
348                 a->second = _FindOrCreateSkinningQuery(*it, key);
349             }
350 
351             TF_DEBUG(USDSKEL_CACHE).Msg(
352                 "[UsdSkelCache] %sAdded skinning query for prim <%s>\n",
353                 _MakeIndent(stack.size()).c_str(),
354                 it->GetPath().GetText());
355 
356             // Don't allow skinnable prims to be nested.
357             it.PruneChildren();
358         }
359 
360         stack.emplace_back(key, *it);
361     }
362     return true;
363 }
364 
365 PXR_NAMESPACE_CLOSE_SCOPE
366