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