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/usdGeom/xformable.h"
25 #include "pxr/usd/usd/schemaRegistry.h"
26 #include "pxr/usd/usd/typed.h"
27 
28 #include "pxr/usd/sdf/types.h"
29 #include "pxr/usd/sdf/assetPath.h"
30 
31 PXR_NAMESPACE_OPEN_SCOPE
32 
33 // Register the schema with the TfType system.
TF_REGISTRY_FUNCTION(TfType)34 TF_REGISTRY_FUNCTION(TfType)
35 {
36     TfType::Define<UsdGeomXformable,
37         TfType::Bases< UsdGeomImageable > >();
38 
39 }
40 
41 /* virtual */
~UsdGeomXformable()42 UsdGeomXformable::~UsdGeomXformable()
43 {
44 }
45 
46 /* static */
47 UsdGeomXformable
Get(const UsdStagePtr & stage,const SdfPath & path)48 UsdGeomXformable::Get(const UsdStagePtr &stage, const SdfPath &path)
49 {
50     if (!stage) {
51         TF_CODING_ERROR("Invalid stage");
52         return UsdGeomXformable();
53     }
54     return UsdGeomXformable(stage->GetPrimAtPath(path));
55 }
56 
57 
58 /* virtual */
_GetSchemaKind() const59 UsdSchemaKind UsdGeomXformable::_GetSchemaKind() const
60 {
61     return UsdGeomXformable::schemaKind;
62 }
63 
64 /* static */
65 const TfType &
_GetStaticTfType()66 UsdGeomXformable::_GetStaticTfType()
67 {
68     static TfType tfType = TfType::Find<UsdGeomXformable>();
69     return tfType;
70 }
71 
72 /* static */
73 bool
_IsTypedSchema()74 UsdGeomXformable::_IsTypedSchema()
75 {
76     static bool isTyped = _GetStaticTfType().IsA<UsdTyped>();
77     return isTyped;
78 }
79 
80 /* virtual */
81 const TfType &
_GetTfType() const82 UsdGeomXformable::_GetTfType() const
83 {
84     return _GetStaticTfType();
85 }
86 
87 UsdAttribute
GetXformOpOrderAttr() const88 UsdGeomXformable::GetXformOpOrderAttr() const
89 {
90     return GetPrim().GetAttribute(UsdGeomTokens->xformOpOrder);
91 }
92 
93 UsdAttribute
CreateXformOpOrderAttr(VtValue const & defaultValue,bool writeSparsely) const94 UsdGeomXformable::CreateXformOpOrderAttr(VtValue const &defaultValue, bool writeSparsely) const
95 {
96     return UsdSchemaBase::_CreateAttr(UsdGeomTokens->xformOpOrder,
97                        SdfValueTypeNames->TokenArray,
98                        /* custom = */ false,
99                        SdfVariabilityUniform,
100                        defaultValue,
101                        writeSparsely);
102 }
103 
104 namespace {
105 static inline TfTokenVector
_ConcatenateAttributeNames(const TfTokenVector & left,const TfTokenVector & right)106 _ConcatenateAttributeNames(const TfTokenVector& left,const TfTokenVector& right)
107 {
108     TfTokenVector result;
109     result.reserve(left.size() + right.size());
110     result.insert(result.end(), left.begin(), left.end());
111     result.insert(result.end(), right.begin(), right.end());
112     return result;
113 }
114 }
115 
116 /*static*/
117 const TfTokenVector&
GetSchemaAttributeNames(bool includeInherited)118 UsdGeomXformable::GetSchemaAttributeNames(bool includeInherited)
119 {
120     static TfTokenVector localNames = {
121         UsdGeomTokens->xformOpOrder,
122     };
123     static TfTokenVector allNames =
124         _ConcatenateAttributeNames(
125             UsdGeomImageable::GetSchemaAttributeNames(true),
126             localNames);
127 
128     if (includeInherited)
129         return allNames;
130     else
131         return localNames;
132 }
133 
134 PXR_NAMESPACE_CLOSE_SCOPE
135 
136 // ===================================================================== //
137 // Feel free to add custom code below this line. It will be preserved by
138 // the code generator.
139 //
140 // Just remember to wrap code in the appropriate delimiters:
141 // 'PXR_NAMESPACE_OPEN_SCOPE', 'PXR_NAMESPACE_CLOSE_SCOPE'.
142 // ===================================================================== //
143 // --(BEGIN CUSTOM CODE)--
144 
145 #include "pxr/base/tf/envSetting.h"
146 
147 #include <algorithm>
148 
149 PXR_NAMESPACE_OPEN_SCOPE
150 
151 TF_DEFINE_PRIVATE_TOKENS(
152     _tokens,
153     (transform)
154     ((invertPrefix, "!invert!"))
155 );
156 
157 using std::vector;
158 
TF_MAKE_STATIC_DATA(GfMatrix4d,_IDENTITY)159 TF_MAKE_STATIC_DATA(GfMatrix4d, _IDENTITY) {
160     *_IDENTITY = GfMatrix4d(1.0);
161 }
162 
163 bool
_GetXformOpOrderValue(VtTokenArray * xformOpOrder) const164 UsdGeomXformable::_GetXformOpOrderValue(VtTokenArray *xformOpOrder) const
165 {
166     UsdAttribute xformOpOrderAttr = GetXformOpOrderAttr();
167     if (!xformOpOrderAttr)
168         return false;
169 
170     xformOpOrderAttr.Get(xformOpOrder, UsdTimeCode::Default());
171     return true;
172 }
173 
174 UsdGeomXformOp
AddXformOp(UsdGeomXformOp::Type const opType,UsdGeomXformOp::Precision const precision,TfToken const & opSuffix,bool isInverseOp) const175 UsdGeomXformable::AddXformOp(
176     UsdGeomXformOp::Type const opType,
177     UsdGeomXformOp::Precision const precision,
178     TfToken const &opSuffix,
179     bool isInverseOp) const
180 {
181     VtTokenArray xformOpOrder;
182     _GetXformOpOrderValue(&xformOpOrder);
183 
184     // Check if the xformOp we're about to add already exists in xformOpOrder
185     TfToken opName = UsdGeomXformOp::GetOpName(opType, opSuffix, isInverseOp);
186     VtTokenArray::iterator it = std::find(xformOpOrder.begin(),
187         xformOpOrder.end(), opName);
188     if (it != xformOpOrder.end()) {
189         TF_CODING_ERROR("The xformOp '%s' already exists in xformOpOrder [%s].",
190             opName.GetText(), TfStringify(xformOpOrder).c_str());
191         return UsdGeomXformOp();
192     }
193 
194     TfToken const &xformOpAttrName = UsdGeomXformOp::GetOpName(opType, opSuffix);
195     UsdGeomXformOp result;
196     if (UsdAttribute xformOpAttr = GetPrim().GetAttribute(xformOpAttrName)) {
197         // Check if the attribute's typeName has the requested precision level.
198         UsdGeomXformOp::Precision existingPrecision =
199             UsdGeomXformOp::GetPrecisionFromValueTypeName(
200                 xformOpAttr.GetTypeName());
201 
202         if (existingPrecision != precision) {
203             TF_CODING_ERROR("XformOp <%s> has typeName '%s' which does not "
204                             "match the requested precision '%s'. Proceeding to "
205                             "use existing typeName / precision.",
206                             xformOpAttr.GetPath().GetText(),
207                             xformOpAttr.GetTypeName().GetAsToken().GetText(),
208                             TfEnum::GetName(precision).c_str());
209         }
210 
211         result = UsdGeomXformOp(xformOpAttr, isInverseOp);
212     } else {
213         result = UsdGeomXformOp(GetPrim(), opType, precision, opSuffix,
214                                 isInverseOp);
215     }
216 
217     if (result) {
218         xformOpOrder.push_back(result.GetOpName());
219         CreateXformOpOrderAttr().Set(xformOpOrder);
220     } else {
221         TF_CODING_ERROR("Unable to add xform op of type %s and precision %s on "
222             "prim at path <%s>. opSuffix=%s, isInverseOp=%d",
223             TfEnum::GetName(opType).c_str(), TfEnum::GetName(precision).c_str(),
224             GetPath().GetText(), opSuffix.GetText(), isInverseOp);
225         return UsdGeomXformOp();
226     }
227 
228     return result;
229 }
230 
231 UsdGeomXformOp
AddTranslateOp(UsdGeomXformOp::Precision const precision,TfToken const & opSuffix,bool isInverseOp) const232 UsdGeomXformable::AddTranslateOp(UsdGeomXformOp::Precision const precision,
233     TfToken const &opSuffix, bool isInverseOp) const
234 {
235     return AddXformOp(UsdGeomXformOp::TypeTranslate, precision, opSuffix,
236                       isInverseOp);
237 }
238 
239 UsdGeomXformOp
AddScaleOp(UsdGeomXformOp::Precision const precision,TfToken const & opSuffix,bool isInverseOp) const240 UsdGeomXformable::AddScaleOp(UsdGeomXformOp::Precision const precision,
241     TfToken const &opSuffix, bool isInverseOp) const
242 {
243     return AddXformOp(UsdGeomXformOp::TypeScale, precision, opSuffix,
244                       isInverseOp);
245 }
246 
247 UsdGeomXformOp
AddRotateXOp(UsdGeomXformOp::Precision const precision,TfToken const & opSuffix,bool isInverseOp) const248 UsdGeomXformable::AddRotateXOp(UsdGeomXformOp::Precision const precision,
249     TfToken const &opSuffix, bool isInverseOp) const
250 {
251     return AddXformOp(UsdGeomXformOp::TypeRotateX, precision, opSuffix,
252                       isInverseOp);
253 }
254 
255 UsdGeomXformOp
AddRotateYOp(UsdGeomXformOp::Precision const precision,TfToken const & opSuffix,bool isInverseOp) const256 UsdGeomXformable::AddRotateYOp(UsdGeomXformOp::Precision const precision,
257     TfToken const &opSuffix, bool isInverseOp) const
258 {
259     return AddXformOp(UsdGeomXformOp::TypeRotateY, precision, opSuffix,
260                       isInverseOp);
261 }
262 
263 UsdGeomXformOp
AddRotateZOp(UsdGeomXformOp::Precision const precision,TfToken const & opSuffix,bool isInverseOp) const264 UsdGeomXformable::AddRotateZOp(UsdGeomXformOp::Precision const precision,
265     TfToken const &opSuffix, bool isInverseOp) const
266 {
267     return AddXformOp(UsdGeomXformOp::TypeRotateZ, precision, opSuffix, isInverseOp);
268 }
269 
270 UsdGeomXformOp
AddRotateXYZOp(UsdGeomXformOp::Precision const precision,TfToken const & opSuffix,bool isInverseOp) const271 UsdGeomXformable::AddRotateXYZOp(UsdGeomXformOp::Precision const precision,
272     TfToken const &opSuffix, bool isInverseOp) const
273 {
274     return AddXformOp(UsdGeomXformOp::TypeRotateXYZ, precision, opSuffix,
275                       isInverseOp);
276 }
277 
278 UsdGeomXformOp
AddRotateXZYOp(UsdGeomXformOp::Precision const precision,TfToken const & opSuffix,bool isInverseOp) const279 UsdGeomXformable::AddRotateXZYOp(UsdGeomXformOp::Precision const precision,
280     TfToken const &opSuffix, bool isInverseOp) const
281 {
282     return AddXformOp(UsdGeomXformOp::TypeRotateXZY, precision, opSuffix,
283                       isInverseOp);
284 }
285 
286 UsdGeomXformOp
AddRotateYXZOp(UsdGeomXformOp::Precision const precision,TfToken const & opSuffix,bool isInverseOp) const287 UsdGeomXformable::AddRotateYXZOp(UsdGeomXformOp::Precision const precision,
288     TfToken const &opSuffix, bool isInverseOp) const
289 {
290     return AddXformOp(UsdGeomXformOp::TypeRotateYXZ, precision, opSuffix,
291                       isInverseOp);
292 }
293 
294 UsdGeomXformOp
AddRotateYZXOp(UsdGeomXformOp::Precision const precision,TfToken const & opSuffix,bool isInverseOp) const295 UsdGeomXformable::AddRotateYZXOp(UsdGeomXformOp::Precision const precision,
296     TfToken const &opSuffix, bool isInverseOp) const
297 {
298     return AddXformOp(UsdGeomXformOp::TypeRotateYZX, precision, opSuffix,
299                       isInverseOp);
300 }
301 
302 UsdGeomXformOp
AddRotateZXYOp(UsdGeomXformOp::Precision const precision,TfToken const & opSuffix,bool isInverseOp) const303 UsdGeomXformable::AddRotateZXYOp(UsdGeomXformOp::Precision const precision,
304     TfToken const &opSuffix, bool isInverseOp) const
305 {
306     return AddXformOp(UsdGeomXformOp::TypeRotateZXY, precision, opSuffix,
307                       isInverseOp);
308 }
309 
310 UsdGeomXformOp
AddRotateZYXOp(UsdGeomXformOp::Precision const precision,TfToken const & opSuffix,bool isInverseOp) const311 UsdGeomXformable::AddRotateZYXOp(UsdGeomXformOp::Precision const precision,
312     TfToken const &opSuffix, bool isInverseOp) const
313 {
314     return AddXformOp(UsdGeomXformOp::TypeRotateZYX, precision, opSuffix,
315                       isInverseOp);
316 }
317 
318 UsdGeomXformOp
AddOrientOp(UsdGeomXformOp::Precision const precision,TfToken const & opSuffix,bool isInverseOp) const319 UsdGeomXformable::AddOrientOp(UsdGeomXformOp::Precision const precision,
320     TfToken const &opSuffix, bool isInverseOp) const
321 {
322     return AddXformOp(UsdGeomXformOp::TypeOrient, precision, opSuffix, isInverseOp);
323 }
324 
325 UsdGeomXformOp
AddTransformOp(UsdGeomXformOp::Precision const precision,TfToken const & opSuffix,bool isInverseOp) const326 UsdGeomXformable::AddTransformOp(UsdGeomXformOp::Precision const precision,
327     TfToken const &opSuffix, bool isInverseOp) const
328 {
329     return AddXformOp(UsdGeomXformOp::TypeTransform, precision, opSuffix,
330                       isInverseOp);
331 }
332 
333 // Returns whether "!resetXformStack! exists in opOrderVec.
334 static
335 bool
_XformOpOrderHasResetXformStack(const VtTokenArray & opOrderVec)336 _XformOpOrderHasResetXformStack(const VtTokenArray &opOrderVec)
337 {
338     return std::find(opOrderVec.begin(), opOrderVec.end(),
339                      UsdGeomXformOpTypes->resetXformStack) != opOrderVec.end();
340 }
341 
342 bool
SetResetXformStack(bool resetXformStack) const343 UsdGeomXformable::SetResetXformStack(bool resetXformStack) const
344 {
345     VtTokenArray opOrderVec;
346     _GetXformOpOrderValue(&opOrderVec);
347 
348     bool result = true;
349     if (resetXformStack) {
350         // Nothing to do if resetXformStack already exists in xformOpOrder
351         if (_XformOpOrderHasResetXformStack(opOrderVec))
352             return true;
353 
354         VtTokenArray newOpOrderVec(opOrderVec.size() + 1);
355 
356         newOpOrderVec[0] = UsdGeomXformOpTypes->resetXformStack;
357         for (size_t i = 0; i < opOrderVec.size(); i++)
358             newOpOrderVec[i+1] = opOrderVec[i];
359 
360         result = CreateXformOpOrderAttr().Set(newOpOrderVec);
361     } else {
362         VtTokenArray newOpOrderVec;
363         bool foundResetXformStack = false;
364         for (size_t i = 0; i < opOrderVec.size(); i++) {
365             if (opOrderVec[i] == UsdGeomXformOpTypes->resetXformStack) {
366                 foundResetXformStack = true;
367                 newOpOrderVec.clear();
368             } else if (foundResetXformStack) {
369                 newOpOrderVec.push_back(opOrderVec[i]);
370             }
371         }
372 
373         if (foundResetXformStack) {
374             result = CreateXformOpOrderAttr().Set(newOpOrderVec);
375         } else {
376             // This is a no-op if "!resetXformStack!" isn't present in
377             // xformOpOrder.
378         }
379     }
380 
381     return result;
382 }
383 
384 bool
GetResetXformStack() const385 UsdGeomXformable::GetResetXformStack() const
386 {
387     VtTokenArray opOrderVec;
388     if (!_GetXformOpOrderValue(&opOrderVec))
389         return false;
390 
391     return _XformOpOrderHasResetXformStack(opOrderVec);
392 }
393 
394 bool
SetXformOpOrder(vector<UsdGeomXformOp> const & orderedXformOps,bool resetXformStack) const395 UsdGeomXformable::SetXformOpOrder(
396     vector<UsdGeomXformOp> const &orderedXformOps,
397     bool resetXformStack) const
398 {
399     VtTokenArray ops;
400     ops.reserve(orderedXformOps.size() + (resetXformStack ? 1: 0));
401 
402     if (resetXformStack)
403         ops.push_back(UsdGeomXformOpTypes->resetXformStack);
404 
405     TF_FOR_ALL(it, orderedXformOps) {
406         // Check to make sure that the xformOp being added to xformOpOrder
407         // belongs to this prim.
408         if (it->GetAttr().GetPrim() == GetPrim()) {
409             ops.push_back(it->GetOpName());
410         } else {
411             TF_CODING_ERROR("XformOp attribute <%s> does not belong to schema "
412                             "prim <%s>.",  it->GetAttr().GetPath().GetText(),
413                             GetPath().GetText());
414             return false;
415         }
416     }
417 
418     return CreateXformOpOrderAttr().Set(ops);
419 }
420 
421 bool
ClearXformOpOrder() const422 UsdGeomXformable::ClearXformOpOrder() const
423 {
424     return SetXformOpOrder(vector<UsdGeomXformOp>());
425 }
426 
427 UsdGeomXformOp
MakeMatrixXform() const428 UsdGeomXformable::MakeMatrixXform() const
429 {
430     ClearXformOpOrder();
431     return AddTransformOp();
432 }
433 
434 vector<UsdGeomXformOp>
GetOrderedXformOps(bool * resetXformStack) const435 UsdGeomXformable::GetOrderedXformOps(bool *resetXformStack) const
436 {
437     return _GetOrderedXformOps(
438         resetXformStack, /*withAttributeQueries=*/false);
439 }
440 
441 vector<UsdGeomXformOp>
_GetOrderedXformOps(bool * resetsXformStack,bool withAttributeQueries) const442 UsdGeomXformable::_GetOrderedXformOps(bool *resetsXformStack,
443                                       bool withAttributeQueries) const
444 {
445     vector<UsdGeomXformOp> result;
446 
447     if (resetsXformStack) {
448         *resetsXformStack = false;
449     } else {
450         TF_CODING_ERROR("resetsXformStack is NULL.");
451     }
452 
453     VtTokenArray opOrderVec;
454     if (!_GetXformOpOrderValue(&opOrderVec)) {
455         return result;
456     }
457 
458     if (opOrderVec.size() == 0) {
459         return result;
460     }
461 
462     // Reserve space for the xform ops.
463     result.reserve(opOrderVec.size());
464 
465     UsdPrim thisPrim = GetPrim();
466     for (VtTokenArray::iterator it = opOrderVec.begin() ;
467          it != opOrderVec.end(); ++it) {
468 
469         const TfToken &opName = *it;
470 
471         // If this is the special resetXformStack op, then clear the currently
472         // accreted xformOps and continue.
473         if (opName == UsdGeomXformOpTypes->resetXformStack) {
474             if (resetsXformStack) {
475                 *resetsXformStack = true;
476             }
477             result.clear();
478         } else {
479             bool isInverseOp = false;
480             UsdAttribute attr = UsdGeomXformOp::_GetXformOpAttr(
481                 thisPrim, opName, &isInverseOp);
482             if (withAttributeQueries) {
483                 TfErrorMark m;
484                 UsdAttributeQuery query(attr);
485                 if (m.IsClean()) {
486                     result.emplace_back(
487                         std::move(query), isInverseOp,
488                         UsdGeomXformOp::_ValidAttributeTagType {});
489                 }
490                 else {
491                     // Skip invalid xform ops that appear in xformOpOrder, but
492                     // issue a warning.
493                     TF_WARN("Unable to get attribute associated with the "
494                             "xformOp '%s', on the prim at path <%s>. Skipping "
495                             "xformOp in the computation of the local "
496                             "transformation at prim.",
497                             opName.GetText(), GetPrim().GetPath().GetText());
498                 }
499             }
500             else {
501                 if (attr) {
502                     // Only add valid xform ops.  We pass _ValidAttributeTag here
503                     // since we've pre-checked the validity of attr above.
504                     result.emplace_back(
505                         attr, isInverseOp,
506                         UsdGeomXformOp::_ValidAttributeTagType {});
507                 }
508                 else {
509                     // Skip invalid xform ops that appear in xformOpOrder, but
510                     // issue a warning.
511                     TF_WARN("Unable to get attribute associated with the "
512                             "xformOp '%s', on the prim at path <%s>. Skipping "
513                             "xformOp in the computation of the local "
514                             "transformation at prim.",
515                             opName.GetText(), GetPrim().GetPath().GetText());
516                 }
517             }
518         }
519     }
520 
521     return result;
522 }
523 
XformQuery(const UsdGeomXformable & xformable)524 UsdGeomXformable::XformQuery::XformQuery(const UsdGeomXformable &xformable):
525     _resetsXformStack(false)
526 {
527     _xformOps = xformable._GetOrderedXformOps(
528         &_resetsXformStack, /*withAttributeQueries=*/true);
529 }
530 
531 bool
GetLocalTransformation(GfMatrix4d * transform,const UsdTimeCode time) const532 UsdGeomXformable::XformQuery::GetLocalTransformation(
533     GfMatrix4d *transform,
534     const UsdTimeCode time) const
535 {
536     return UsdGeomXformable::GetLocalTransformation(transform, _xformOps, time);
537 }
538 
539 static
540 bool
_TransformMightBeTimeVarying(vector<UsdGeomXformOp> const & xformOps)541 _TransformMightBeTimeVarying(vector<UsdGeomXformOp> const &xformOps)
542 {
543     // If any of the xform ops may vary, then the cumulative transform may vary.
544     TF_FOR_ALL(it, xformOps) {
545         if (it->MightBeTimeVarying())
546             return true;
547     }
548 
549     return false;
550 }
551 
552 bool
TransformMightBeTimeVarying() const553 UsdGeomXformable::XformQuery::TransformMightBeTimeVarying() const
554 {
555     return _TransformMightBeTimeVarying(_xformOps);
556 }
557 
558 bool
TransformMightBeTimeVarying() const559 UsdGeomXformable::TransformMightBeTimeVarying() const
560 {
561     VtTokenArray opOrderVec;
562     if (!_GetXformOpOrderValue(&opOrderVec))
563         return false;
564 
565     if (opOrderVec.size() == 0) {
566         return false;
567     }
568 
569     for (VtTokenArray::reverse_iterator it = opOrderVec.rbegin() ;
570          it != opOrderVec.rend(); ++it) {
571 
572         const TfToken &opName = *it;
573 
574         // If this is the special resetXformStack op, return false to indicate
575         // that none of the xformOps that affect the local transformation are
576         // time-varying (since none of the (valid) xformOps after the last
577         // occurrence of !resetXformStack! are time-varying).
578         if (opName == UsdGeomXformOpTypes->resetXformStack) {
579             return false;
580         } else {
581             bool isInverseOp = false;
582             if (UsdAttribute attr = UsdGeomXformOp::_GetXformOpAttr(
583                     GetPrim(), opName, &isInverseOp)) {
584                 // Only check valid xform ops for time-varyingness.
585                 UsdGeomXformOp op(attr, isInverseOp);
586                 if (op && op.MightBeTimeVarying()) {
587                     return true;
588                 }
589             }
590         }
591     }
592     return false;
593 }
594 
595 bool
TransformMightBeTimeVarying(const vector<UsdGeomXformOp> & ops) const596 UsdGeomXformable::TransformMightBeTimeVarying(
597     const vector<UsdGeomXformOp> &ops) const
598 {
599     if (!ops.empty())
600         return _TransformMightBeTimeVarying(ops);
601 
602     // Assume unvarying if neither orderedXformOps nor transform attribute is
603     // authored.
604     return false;
605 }
606 
607 /* static */
608 bool
GetTimeSamples(vector<UsdGeomXformOp> const & orderedXformOps,vector<double> * times)609 UsdGeomXformable::GetTimeSamples(
610     vector<UsdGeomXformOp> const &orderedXformOps,
611     vector<double> *times)
612 {
613     return GetTimeSamplesInInterval(orderedXformOps,
614             GfInterval::GetFullInterval(), times);
615 }
616 
617 /* static */
618 bool
GetTimeSamplesInInterval(std::vector<UsdGeomXformOp> const & orderedXformOps,const GfInterval & interval,std::vector<double> * times)619 UsdGeomXformable::GetTimeSamplesInInterval(
620     std::vector<UsdGeomXformOp> const &orderedXformOps,
621     const GfInterval &interval,
622     std::vector<double> *times)
623 {
624     // Optimize for the case where there's a single xformOp (typically a 4x4
625     // matrix op).
626     if (orderedXformOps.size() == 1) {
627         return orderedXformOps.front().GetTimeSamplesInInterval(interval,
628             times);
629     }
630 
631     vector<UsdAttribute> xformOpAttrs;
632     xformOpAttrs.reserve(orderedXformOps.size());
633     for (auto &xformOp : orderedXformOps) {
634         xformOpAttrs.push_back(xformOp.GetAttr());
635     }
636 
637     return UsdAttribute::GetUnionedTimeSamplesInInterval(xformOpAttrs,
638             interval, times);
639 }
640 
641 bool
GetTimeSamplesInInterval(const GfInterval & interval,std::vector<double> * times) const642 UsdGeomXformable::GetTimeSamplesInInterval(
643     const GfInterval &interval,
644     std::vector<double> *times) const
645 {
646     bool resetsXformStack=false;
647     const vector<UsdGeomXformOp> &orderedXformOps= GetOrderedXformOps(
648         &resetsXformStack);
649 
650     return UsdGeomXformable::GetTimeSamplesInInterval(orderedXformOps, interval,
651             times);
652 }
653 
654 bool
GetTimeSamples(vector<double> * times) const655 UsdGeomXformable::XformQuery::GetTimeSamples(vector<double> *times) const
656 {
657     return GetTimeSamplesInInterval(GfInterval::GetFullInterval(), times);
658 }
659 
660 bool
GetTimeSamplesInInterval(const GfInterval & interval,vector<double> * times) const661 UsdGeomXformable::XformQuery::GetTimeSamplesInInterval(
662     const GfInterval &interval,
663     vector<double> *times) const
664 {
665     if (_xformOps.size() == 1) {
666         _xformOps.front().GetTimeSamplesInInterval(interval, times);
667     }
668 
669     vector<UsdAttributeQuery> xformOpAttrQueries;
670     xformOpAttrQueries.reserve(_xformOps.size());
671     for (auto &xformOp : _xformOps) {
672         // This should never throw and exception because XformQuery's constructor
673         // initializes an attribute query for all its xformOps.
674         const UsdAttributeQuery &attrQuery =
675             boost::get<UsdAttributeQuery>(xformOp._attr);
676         xformOpAttrQueries.push_back(attrQuery);
677     }
678 
679     return UsdAttributeQuery::GetUnionedTimeSamplesInInterval(
680             xformOpAttrQueries, interval, times);
681 }
682 
683 bool
GetTimeSamples(vector<double> * times) const684 UsdGeomXformable::GetTimeSamples(vector<double> *times) const
685 {
686     bool resetsXformStack=false;
687     const vector<UsdGeomXformOp> &orderedXformOps= GetOrderedXformOps(
688         &resetsXformStack);
689 
690     return GetTimeSamples(orderedXformOps, times);
691 }
692 
693 bool
IsAttributeIncludedInLocalTransform(const TfToken & attrName) const694 UsdGeomXformable::XformQuery::IsAttributeIncludedInLocalTransform(
695     const TfToken &attrName) const
696 {
697     TF_FOR_ALL(it, _xformOps) {
698         if (it->GetName() == attrName)
699             return true;
700     }
701 
702     return false;
703 }
704 
705 // Given two UsdGeomXformOps, returns true if they are inverses of each other.
706 static bool
_AreInverseXformOps(const UsdGeomXformOp & a,const UsdGeomXformOp & b)707 _AreInverseXformOps(const UsdGeomXformOp &a, const UsdGeomXformOp &b)
708 {
709     // The two given ops are inverses of each other if they have the same
710     // underlying attribute and only if one of them is an inverseOp.
711     return a.GetAttr() == b.GetAttr() &&
712            a.IsInverseOp() != b.IsInverseOp();
713 }
714 
715 // Given two xformOp names, returns true if they are inverses of each other.
716 static bool
_AreInverseXformOps(const TfToken & a,const TfToken & b)717 _AreInverseXformOps(const TfToken &a, const TfToken &b)
718 {
719     return _tokens->invertPrefix.GetString() + a.GetString() == b.GetString()
720         || _tokens->invertPrefix.GetString() + b.GetString() == a.GetString();
721 }
722 
723 bool
GetLocalTransformation(GfMatrix4d * transform,bool * resetsXformStack,const UsdTimeCode time) const724 UsdGeomXformable::GetLocalTransformation(
725     GfMatrix4d *transform,
726     bool *resetsXformStack,
727     const UsdTimeCode time) const
728 {
729     TRACE_FUNCTION();
730 
731     if (transform) {
732         *transform = GfMatrix4d(1.);
733     }
734     else {
735         TF_CODING_ERROR("transform is NULL.");
736         return false;
737     }
738 
739     if (resetsXformStack) {
740         *resetsXformStack = false;
741     } else {
742         TF_CODING_ERROR("resetsXformStack is NULL.");
743         return false;
744     }
745 
746     VtTokenArray opOrderVec;
747     if (!_GetXformOpOrderValue(&opOrderVec))
748         return false;
749 
750     if (opOrderVec.size() == 0) {
751         return true;
752     }
753 
754     for (VtTokenArray::reverse_iterator it = opOrderVec.rbegin() ;
755          it != opOrderVec.rend(); ++it) {
756 
757         const TfToken &opName = *it;
758 
759         // Skip the current xformOp and the next one if they're inverses of
760         // each other.
761         if ((it+1) != opOrderVec.rend()) {
762             const TfToken &nextOpName = *(it+1);
763             if (_AreInverseXformOps(opName, nextOpName)) {
764                 ++it;
765                 continue;
766             }
767         }
768 
769         // If this is the special resetXformStack op, then the currently
770         // accreted localXform is the local transformation of the prim.
771         if (opName == UsdGeomXformOpTypes->resetXformStack) {
772             *resetsXformStack = true;
773             break;
774         } else {
775             bool isInverseOp = false;
776             if (UsdAttribute attr = UsdGeomXformOp::_GetXformOpAttr(
777                     GetPrim(), opName, &isInverseOp)) {
778                 // Only add valid xform ops.
779                 UsdGeomXformOp op(attr, isInverseOp);
780                 if (op) {
781                     GfMatrix4d opTransform = op.GetOpTransform(time);
782                     // Avoid multiplying by the identity matrix when possible.
783                     if (opTransform != *_IDENTITY) {
784                         (*transform) *= opTransform;
785                     }
786                 }
787             } else {
788                 // Skip invalid xform ops that appear in xformOpOrder, but issue
789                 // a warning.
790                 TF_WARN("Unable to get attribute associated with the xformOp "
791                     "'%s', on the prim at path <%s>. Skipping xformOp in the "
792                     "computation of the local transformation at prim.",
793                     opName.GetText(), GetPrim().GetPath().GetText());
794             }
795         }
796     }
797 
798     return true;
799 }
800 
801 bool
GetLocalTransformation(GfMatrix4d * transform,bool * resetsXformStack,const vector<UsdGeomXformOp> & ops,const UsdTimeCode time) const802 UsdGeomXformable::GetLocalTransformation(
803     GfMatrix4d *transform,
804     bool *resetsXformStack,
805     const vector<UsdGeomXformOp> &ops,
806     const UsdTimeCode time) const
807 {
808     TRACE_FUNCTION();
809 
810     if (resetsXformStack) {
811         *resetsXformStack = GetResetXformStack();
812     } else {
813         TF_CODING_ERROR("resetsXformStack is NULL.");
814     }
815     return GetLocalTransformation(transform, ops, time);
816 }
817 
818 /* static */
819 bool
GetLocalTransformation(GfMatrix4d * transform,const vector<UsdGeomXformOp> & orderedXformOps,const UsdTimeCode time)820 UsdGeomXformable::GetLocalTransformation(
821     GfMatrix4d *transform,
822     const vector<UsdGeomXformOp> &orderedXformOps,
823     const UsdTimeCode time)
824 {
825     GfMatrix4d xform(1.);
826 
827     for (vector<UsdGeomXformOp>::const_reverse_iterator
828             it = orderedXformOps.rbegin();
829          it != orderedXformOps.rend(); ++it) {
830 
831         const UsdGeomXformOp &xformOp = *it;
832 
833         // Skip the current xformOp and the next one if they're inverses of
834         // each other.
835         if ((it+1) != orderedXformOps.rend()) {
836             const UsdGeomXformOp &nextXformOp = *(it+1);
837             if (_AreInverseXformOps(xformOp, nextXformOp)) {
838                 ++it;
839                 continue;
840             }
841         }
842 
843         GfMatrix4d opTransform = xformOp.GetOpTransform(time);
844         // Avoid multiplying by the identity matrix when possible.
845         if (opTransform != *_IDENTITY) {
846             xform *= opTransform;
847         }
848     }
849 
850     if (transform) {
851         *transform = xform;
852         return true;
853     } else {
854         TF_CODING_ERROR("'transform' pointer is NULL.");
855     }
856 
857     return false;
858 }
859 
860 /* static */
861 bool
IsTransformationAffectedByAttrNamed(const TfToken & attrName)862 UsdGeomXformable::IsTransformationAffectedByAttrNamed(const TfToken &attrName)
863 {
864     return attrName == UsdGeomTokens->xformOpOrder ||
865            UsdGeomXformOp::IsXformOp(attrName);
866 }
867 
868 PXR_NAMESPACE_CLOSE_SCOPE
869