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