HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_TransformUtil.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: UT_TransformUtil.h (UT Library, C++)
7  *
8  * COMMENTS:
9  */
10 
11 #ifndef __UT_TRANSFORMUTIL_H_INCLUDED__
12 #define __UT_TRANSFORMUTIL_H_INCLUDED__
13 
14 /// @file
15 /// Additional utilities for dealing with transforms
16 
17 #include "UT_API.h"
18 #include "UT_Array.h"
19 #include "UT_Assert.h"
20 #include "UT_Matrix3.h"
21 #include "UT_Matrix4.h"
22 #include "UT_Quaternion.h"
23 #include "UT_SmallArray.h"
24 #include "UT_Vector3.h"
25 #include "UT_Vector4.h"
26 #include <SYS/SYS_Math.h>
27 
28 /// Scale inheritance modes
30 {
31  /// simple inheritance: W = L * pW
32  DEFAULT = 0,
33 
34  /// child doesn't scale with the parent local scales, but local translation
35  /// is scaled: W = SR * pLS^-1 * T * pW
36  /// (Maya segment scale compensation)
38 
39  /// local translation is scaled as before but parent local scaling is also
40  /// reapplied by the child in local space: W = pLS * SR * pLS^-1 * T * pW
41  /// (Softimage hierarchical scaling)
43 
44  /// local translation is not scaled, but parent local scaling is reapplied
45  /// by the child in local space: W = pLS * SRT * pLS^-1 * pW
46  SCALE_ONLY,
47 
48  /// child completely ignores any parent local scaling:
49  /// W = SRT * pLS^-1 * pW
51 };
52 
54 {
55  MODE_PRE = 0,
56  MODE_POST,
58 };
59 
60 /// Combine a local transform on top of another with scale inheritance options
61 /// L is modified to contain the effective local transform on return for t.
62 template<typename PREC>
65  const UT_Matrix4T<PREC>& L, /// local transform
66  const UT_Matrix4T<PREC>& pW, /// parent world transform
67  const UT_Matrix4T<PREC>& pL, /// parent local transform
69  UT_Matrix4T<PREC>* effective_local=nullptr,
70  UT_Matrix3T<PREC>* pLS_ptr=nullptr, /// parent local transform stretch
71  UT_Matrix3T<PREC>* pLS_inv_ptr=nullptr, /// parent local transform stretch_inv
72  bool* pLS_init_ptr=nullptr
73  );
74 
75 /// Combine a local transform on top of another with scale inheritance options
76 /// L is modified to contain the effective local transform on return for t.
77 /// Same as UTtransformCombineLocal, but with mode passed as a template argument
78 /// and an optimization with HAS_EFFECTIVE_LOCAL.
79 template <
80  typename PREC,
82  bool HAS_PLS
83 >
86  const UT_Matrix4T<PREC>& L, /// local transform
87  const UT_Matrix4T<PREC>& pW, /// parent world transform
88  const UT_Matrix4T<PREC>& pL, /// parent local transform
89  UT_Matrix4T<PREC>* effective_local = nullptr,
90  UT_Matrix3T<PREC>* pLS_ptr = nullptr, /// parent local transform stretch
91  UT_Matrix3T<PREC>* pLS_inv_ptr = nullptr, /// parent local transform stretch_inv
92  bool* pLS_init_ptr = nullptr);
93 
94 /// Extract relative transform with scale inheritance
95 template<typename PREC>
98  const UT_Matrix4T<PREC>& W, /// source world transform
99  const UT_Matrix4T<PREC>& pW, /// target parent world
100  const UT_Matrix4T<PREC>& pL, /// target parent local
102  UT_Matrix4T<PREC>* effective_local = nullptr);
103 
104 template<typename PREC>
105 void
108  const UT_Matrix4T<PREC>& W, /// source world transform
109  const UT_Matrix4T<PREC>& pW, /// target parent world
110  const UT_Matrix4T<PREC>& pW_inv, /// target parent world
111  const UT_Matrix4T<PREC>& pL, /// target parent local
113  UT_Matrix4T<PREC>* effective_local = nullptr);
114 
115 template <
116  typename PREC,
118  bool HAS_EFFECTIVE_LOCAL = false
119 >
120 SYS_FORCE_INLINE void
123  const UT_Matrix4T<PREC>& W, /// source world transform
124  const UT_Matrix4T<PREC>& pW, /// target parent world
125  const UT_Matrix4T<PREC>& pW_inv, /// target parent world
126  const UT_Matrix4T<PREC>& pL, /// target parent local
127  UT_Matrix4T<PREC>* effective_local = nullptr);
128 
129 /// Combines the local space transform components with a local space matrix.
130 ///
131 /// The matrix composition takes the follow forms:
132 /// UT_ParameterTransformMode::MODE_PRE:
133 /// M = S_parm * S_local * (Sh_parm + Sh_local)
134 /// * R_parm * T_parm * R_local * T_local
135 ///
136 /// UT_ParameterTransformMode::MODE_POST:
137 /// M = S_local * S_parm * (Sh_local + Sh_parm)
138 /// * R_local * R_parm * T_local * T_parm
139 ///
140 /// UT_ParameterTransformMode::MODE_OVERRIDE:
141 /// M = S_parm * Sh_parm * R_parm * T_parm
142 ///
143 /// MODE_PRE behaves similarly to a traditional parent-child transformation
144 /// (with the component parameters representing the child and the local matrix
145 /// representing the parent); however, the parent's scaling and shearing are
146 /// applied in the child's local space. This behavior is desirable when the
147 /// parent matrix represents a local space rest transform. For example, a
148 /// prop's rest scale can be manipulated without affecting its animated
149 /// position. Simultaneously, this behavior avoids unintentional shearing when
150 /// the prop rotates while non-uniformly scaled.
151 ///
152 /// MODE_POST first applies the \p local transformation (eg. a local space rest
153 /// transform). Then, the rotation and translation parameters are applied using
154 /// the \p local transform's position as its pivot point (essentially layering
155 /// the rotation translation on top of \p local transformation). The scale and
156 /// shear parameters are combined with the local transform's scale and shear
157 /// in the same manner as MODE_PRE.
158 /// Mathematically, this can be understood as adding the following pivot:
159 /// R_local * T_local * pivot^1 * R_parm * pivot * T_parm
160 /// = R_local * T_local * (T_local)^1 * R_parm * (T_local) * T_parm
161 /// = R_local * R_parm * T_local * T_parm
162 ///
163 /// MODE_OVERRIDE forms a transformation from the parameter components and
164 /// ignores the local matrix.
165 ///
166 /// \tparam PREC Precision of the matrix and vector types
167 /// \param local A local space matrix
168 /// \param parm_ord Transform and rotation order of transform components
169 /// \param parm_t Translation
170 /// \param parm_r Rotation (Euler angles in degrees). Respects \p parm_ord.
171 /// \param parm_s Scale
172 /// \param parm_sh Shear
173 /// \param pivot_space Pivot space for the components
174 /// \param mode Controls how the components are combined with the \p local
175 /// matrix
176 /// \return The combined local space transformation matrix
177 template <typename PREC>
179  const UT_Matrix4T<PREC> &local,
180  const UT_XformOrder &parm_ord,
181  UT_Vector3T<PREC> parm_t,
182  UT_Vector3T<PREC> parm_r,
183  UT_Vector3T<PREC> parm_s,
184  UT_Vector3T<PREC> parm_sh,
185  const typename UT_Matrix4T<PREC>::PivotSpace &pivot_space
186  = typename UT_Matrix4T<PREC>::PivotSpace{},
189 
190 /// \brief Extracts the local space transformation components from the \p local
191 /// matrix with respect to the \p relativelocal matrix.
192 ///
193 /// This function behaves as the inverse of
194 /// UTcombineParametersWithLocalTransform(). In particular, if the returned
195 /// parameters are re-combined with the \p relative_local matrix using
196 /// UTcombineParametersWithLocalTransform(), then the \p local matrix will be
197 /// reformed. As such, this function is useful for rig inversion workflows
198 /// in which the end goal is to produce the animated components which would
199 /// re-construct the local matrix. The \p relative_local matrix could
200 /// represent a rest matrix or some other input pose upon which to transfer
201 /// animation.
202 ///
203 /// \tparam PREC Precision of the matrix and vector types
204 /// \param local A local space matrix from which to extract the transform
205 /// components
206 /// \param relative_local The extraction of the transform components is
207 /// performed relative to this matrix
208 /// \param parm_ord Transform and rotation order
209 /// \param pivot_space Pivot space for the extracted components
210 /// \param mode Controls how the extraction of the parameter components happens
211 /// in relation to the \p relative_local matrix
212 /// \param reflection_hint A vector which provides a hint as to which axes
213 /// should have negative scales. The sign of each vector component is used.
214 /// \return Tuple representing the extracted translation, rotation (Euler
215 /// angles in degrees), scale, and shear components (in that order)
216 template <typename PREC>
217 SYS_FORCE_INLINE std::tuple<
219  UT_Vector3T<PREC>,
220  UT_Vector3T<PREC>,
221  UT_Vector3T<PREC>>
223  const UT_Matrix4T<PREC> &local,
224  const UT_Matrix4T<PREC> &relative_local,
225  const UT_XformOrder &parm_ord,
226  const typename UT_Matrix4T<PREC>::PivotSpace &pivot_space
227  = typename UT_Matrix4T<PREC>::PivotSpace{},
228  const UT_ParameterTransformMode mode
230  const UT_Vector3T<PREC> &reflection_hint
231  = UT_Vector3T<PREC>(1., 1., 1.));
232 
233 /// Method of slerp for blending
234 enum class UT_SLERP_METHOD
235 {
236  NLERP, /// normalized lerp of quaternion
237  ACCURATE, /// use iterative method
238 };
239 
240 /// Flip method for slerp to ensure consistency during blending
242 {
243  FIRST, /// use hemisphere of first quaternion
244  SUCCESSIVE, /// compare each adjacent quaternions when using NLERP
245 };
246 
247 /// Spherically blend transforms by decomposing into separate quaternions
248 template<
249  typename PREC,
250  bool NORMALIZE_WEIGHTS = true,
253 >
255 UTslerp(const UT_Array<UT_Matrix4T<PREC>> &xforms,
256  const UT_Array<PREC> &input_weights);
257 
258 /// Spherically blend 2 transforms by decomposing into separate quaternions
259 template<
260  typename PREC,
263 >
265 UTslerp( const UT_Matrix4T<PREC> &xform0, const UT_Matrix4T<PREC> &xform1,
266  const PREC input_weight);
267 
268 /// Spherically blend multiple quaternions, provided for interface consistency
269 template<
270  typename PREC,
271  bool NORMALIZE_WEIGHTS = true
272 >
275  const UT_Array<PREC> &weights);
276 
277 template<typename PREC>
279 {
280 public:
284 };
285 
286 /// Spherically blend 2 transforms by decomposing into separate quaternions
287 /// Also makes use of a cache to avoid calling makeRotationMatrix if the
288 /// input matrices didn't change. 2 cache entries are used per invocation.
289 template<
290  typename PREC,
293 >
295 UTslerp( const UT_Matrix4T<PREC> &xform0,const UT_Matrix4T<PREC> &xform1,
296  const PREC input_weight, UT_SlerpCache<PREC> *makerotcache);
297 
298 
299 ///////////////////////////////////////////////////////////////////////////////
300 //
301 // Implementations
302 //
303 
304 template<
305  typename PREC,
307  bool HAS_PLS
308 >
311  const UT_Matrix4T<PREC>& L, /// local transform
312  const UT_Matrix4T<PREC>& pW, /// parent world transform
313  const UT_Matrix4T<PREC>& pL, /// parent local transform
314  UT_Matrix4T<PREC>* effective_local,
315  UT_Matrix3T<PREC>* pLS_ptr, /// parent local transform stretch
316  UT_Matrix3T<PREC>* pLS_inv_ptr, /// parent local transform stretch_inv
317  bool* pLS_init_ptr)
318 {
319  // W: child world transform
320  // T,R,S: child local translation, rotation and scale
321  // pW: parent world transform
322  // pT, pR, pS: parent translation, rotation and scale
323  // pL: parent local transform
324  // pLT, pLR, pLS: parent local translate, rotation and scale
325 
326 
327  UT_Matrix3T<PREC> tmp_pLS, tmp_pLS_inv;
328  bool tmp_pLS_init = true;
329 
330  UT_Matrix3T<PREC> &pLS = HAS_PLS ? *pLS_ptr : tmp_pLS;
331  UT_Matrix3T<PREC> &pLS_inv = HAS_PLS ? *pLS_inv_ptr : tmp_pLS_inv;
332  bool &pLS_init = HAS_PLS ? *pLS_init_ptr : tmp_pLS_init;
333 
334  if (effective_local && effective_local != &L)
335  *effective_local = L;
336 
337  UT_Matrix4T<PREC> my_xform;
338 
339  switch (MODE)
340  {
345  if (pLS_init)
346  {
347  UT_Matrix3T<PREC> parent_local(pL);
348  parent_local.polarDecompose(&pLS);
349  pLS.invert(pLS_inv);
350  pLS_init = false;
351  }
352  break;
353 
354  default:
355  break;
356  };
357 
358  switch (MODE)
359  {
361  {
362  my_xform = L * pW;
363  break;
364  }
366  {
367  UT_Matrix4T<PREC> RS = L;
368  constexpr UT_Vector3T<PREC> _t0{ 0.0, 0.0, 0.0 };
369  RS.setTranslates(_t0);
370 
371  UT_Matrix4T<PREC> T{ 1.0 };
372  UT_Vector3T<PREC> _t;
373  L.getTranslates(_t);
374  T.setTranslates(_t);
375 
376  my_xform = RS * UT_Matrix4T<PREC>(pLS_inv) * T * pW;
377  break;
378  }
380  {
381 
382  UT_Matrix4T<PREC> RS = L;
383  constexpr UT_Vector3T<PREC> _t0{ 0.0, 0.0, 0.0 };
384  RS.setTranslates(_t0);
385 
386  UT_Matrix4T<PREC> T{ 1.0 };
387  UT_Vector3T<PREC> _t;
388  L.getTranslates(_t);
389  T.setTranslates(_t);
390 
391  my_xform = UT_Matrix4T<PREC>(pLS) * RS * UT_Matrix4T<PREC>(pLS_inv)
392  * T * pW;
393  // NB: effective_local here is same as SCALE_ONLY case because
394  // it's for the child transforms
395  if (effective_local)
396  effective_local->leftMult(UT_Matrix4T<PREC>(pLS));
397  break;
398  }
400  {
401  my_xform = UT_Matrix4T<PREC>(pLS) * L * UT_Matrix4T<PREC>(pLS_inv)
402  * pW;
403  if (effective_local)
404  effective_local->leftMult(UT_Matrix4T<PREC>(pLS));
405  break;
406  }
408  {
409  my_xform = L * UT_Matrix4T<PREC>(pLS_inv) * pW;
410  break;
411  }
412  default:
413  {
414  my_xform = L * pW;
415  break;
416  }
417  }
418  return my_xform;
419 }
420 
421 template<typename PREC>
422 inline UT_Matrix4T<PREC>
424  const UT_Matrix4T<PREC>& L, /// local transform
425  const UT_Matrix4T<PREC>& pW, /// parent world transform
426  const UT_Matrix4T<PREC>& pL, /// parent local transform
428  UT_Matrix4T<PREC>* effective_local,
429  UT_Matrix3T<PREC>* pLS_ptr,
430  UT_Matrix3T<PREC>* pLS_inv_ptr,
431  bool* pLS_init_ptr)
432 {
433 #define UT_TRANSFORM_COMBINE(M, A) \
434  return UTtransformCombineLocalT<PREC, M, A>( \
435  L, pW, pL, effective_local, pLS_ptr, pLS_inv_ptr, pLS_init_ptr); \
436  /**/
437 #define IF_UT_TRANSFORM_COMBINE(M, A) \
438  if (mode == M) \
439  UT_TRANSFORM_COMBINE(M, A) \
440  /**/
441 
442  if (pLS_init_ptr && pLS_ptr && pLS_inv_ptr)
443  {
450  }
451  else
452  {
453  pLS_ptr = nullptr;
454  pLS_inv_ptr = nullptr;
455  pLS_init_ptr = nullptr;
456 
463  }
464 }
465 
466 /// Extract relative transform with scale inheritance
467 template<typename PREC>
468 inline void
471  const UT_Matrix4T<PREC>& W,
472  const UT_Matrix4T<PREC>& pW,
473  const UT_Matrix4T<PREC>& pW_inv,
474  const UT_Matrix4T<PREC>& pL,
476  UT_Matrix4T<PREC>* effective_local)
477 {
478  // W: child world transform
479  // T,R,S: child local translation, rotation and scale
480  // pW: parent world transform
481  // pT, pR, pS: parent translation, rotation and scale
482  // pL: parent local transform
483  // pLT, pLR, pLS: parent local translate, rotation and scale
484 
485  // the result
486 
487  UT_Matrix3T<PREC> pLS{ 1 };
488  UT_Matrix3T<PREC> pLS_inv{ 1 };
489 
490  switch (mode)
491  {
493  {
494  L = W * pW_inv;
495  if (effective_local)
496  *effective_local = L;
497  break;
498  }
500  {
501  UT_Vector3T<PREC> T_vec;
502  W.getTranslates(T_vec);
503  T_vec *= pW_inv;
504 
505  UT_Matrix4T<PREC>(pL).polarDecompose(&pLS);
506 
507  pLS.invert(pLS_inv);
508 
509  UT_Matrix4T<PREC> _pW{pW};
510  _pW.preMultiply(UT_Matrix4T<PREC>(pLS_inv));
511 
512  UT_Matrix4T<PREC> _pW_inv;
513  _pW.invert(_pW_inv);
514 
515  L = W * _pW_inv;
516 
517  L.setTranslates(T_vec);
518 
519  if (effective_local)
520  *effective_local = L;
521  break;
522  }
524  {
525  UT_Vector3T<PREC> T_vec;
526  W.getTranslates(T_vec);
527  T_vec *= pW_inv;
528 
529  UT_Matrix4T<PREC>(pL).polarDecompose(&pLS);
530 
531  pLS.invert(pLS_inv);
532 
533  UT_Matrix4T<PREC> _pW{ pW };
534  _pW.preMultiply(UT_Matrix4T<PREC>(pLS_inv));
535  UT_Matrix4T<PREC> _pW_inv;
536  _pW.invert(_pW_inv);
537 
538  L = W * _pW_inv;
539 
540  L.setTranslates(T_vec);
541 
542  if (effective_local)
543  *effective_local = L;
544 
545  L.preMultiply(UT_Matrix4T<PREC>(pLS_inv));
546 
547 
548  break;
549  }
551  {
552  // Inverse of:
553  // W = pLS * SRT * pLS^-1 * pW
554 
555  UT_Matrix4T<PREC>(pL).polarDecompose(&pLS);
556 
557  pLS.invert(pLS_inv);
558 
559  UT_Matrix4T<PREC> _pW{ pW };
560  _pW.preMultiply(UT_Matrix4T<PREC>(pLS_inv));
561  UT_Matrix4T<PREC> _pW_inv;
562  _pW.invert(_pW_inv);
563 
564  L = W * _pW_inv;
565 
566  if (effective_local)
567  *effective_local = L;
568 
569  L.preMultiply(UT_Matrix4T<PREC>(pLS_inv));
570 
571  break;
572  }
574  {
575  UT_Matrix4T<PREC>(pL).polarDecompose(&pLS);
576  pLS.invert(pLS_inv);
577  UT_Matrix4T<PREC> _pW{ pW };
578  _pW.preMultiply(UT_Matrix4T<PREC>(pLS_inv));
579  UT_Matrix4T<PREC> _pW_inv;
580  _pW.invert(_pW_inv);
581  L = W * _pW_inv;
582  if (effective_local)
583  *effective_local = L;
584  break;
585  }
586  default:
587  {
588  L = W * pW_inv;
589  if (effective_local)
590  *effective_local = L;
591  break;
592  }
593  }
594 }
595 
596 /// Extract relative transform with scale inheritance
597 template<typename PREC, UT_ScaleInheritanceMode MODE, bool HAS_EFFECTIVE_LOCAL>
598 SYS_FORCE_INLINE void
601  const UT_Matrix4T<PREC>& W,
602  const UT_Matrix4T<PREC>& pW,
603  const UT_Matrix4T<PREC>& pW_inv,
604  const UT_Matrix4T<PREC>& pL,
605  UT_Matrix4T<PREC>* effective_local)
606 {
607  // W: child world transform
608  // T,R,S: child local translation, rotation and scale
609  // pW: parent world transform
610  // pT, pR, pS: parent translation, rotation and scale
611  // pL: parent local transform
612  // pLT, pLR, pLS: parent local translate, rotation and scale
613 
614  // the result
615 
616  UT_Matrix3T<PREC> pLS;
617  UT_Matrix3T<PREC> pLS_inv;
618 
619  switch (MODE)
620  {
622  {
623  L = W * pW_inv;
624  if (HAS_EFFECTIVE_LOCAL && effective_local)
625  *effective_local = L;
626  break;
627  }
629  {
630  UT_Vector3T<PREC> T_vec;
631  W.getTranslates(T_vec);
632  T_vec *= pW_inv;
633 
634  UT_Matrix4T<PREC>(pL).polarDecompose(&pLS);
635 
636  pLS.invert(pLS_inv);
637 
638  UT_Matrix4T<PREC> _pW{pW};
639  _pW.preMultiply(UT_Matrix4T<PREC>(pLS_inv));
640 
641  UT_Matrix4T<PREC> _pW_inv;
642  _pW.invert(_pW_inv);
643 
644  L = W * _pW_inv;
645 
646  L.setTranslates(T_vec);
647 
648  if (HAS_EFFECTIVE_LOCAL && effective_local)
649  *effective_local = L;
650  break;
651  }
653  {
654  UT_Vector3T<PREC> T_vec;
655  W.getTranslates(T_vec);
656  T_vec *= pW_inv;
657 
658  UT_Matrix4T<PREC>(pL).polarDecompose(&pLS);
659 
660  pLS.invert(pLS_inv);
661 
662  UT_Matrix4T<PREC> _pW{ pW };
663  _pW.preMultiply(UT_Matrix4T<PREC>(pLS_inv));
664  UT_Matrix4T<PREC> _pW_inv;
665  _pW.invert(_pW_inv);
666 
667  L = W * _pW_inv;
668 
669  L.setTranslates(T_vec);
670 
671  if (HAS_EFFECTIVE_LOCAL && effective_local)
672  *effective_local = L;
673 
674  L.preMultiply(UT_Matrix4T<PREC>(pLS_inv));
675 
676 
677  break;
678  }
680  {
681  // Inverse of:
682  // W = pLS * SRT * pLS^-1 * pW
683 
684  UT_Matrix4T<PREC>(pL).polarDecompose(&pLS);
685 
686  pLS.invert(pLS_inv);
687 
688  UT_Matrix4T<PREC> _pW{ pW };
689  _pW.preMultiply(UT_Matrix4T<PREC>(pLS_inv));
690  UT_Matrix4T<PREC> _pW_inv;
691  _pW.invert(_pW_inv);
692 
693  L = W * _pW_inv;
694 
695  if (HAS_EFFECTIVE_LOCAL && effective_local)
696  *effective_local = L;
697 
698  L.preMultiply(UT_Matrix4T<PREC>(pLS_inv));
699 
700  break;
701  }
703  {
704  UT_Matrix4T<PREC>(pL).polarDecompose(&pLS);
705  pLS.invert(pLS_inv);
706  UT_Matrix4T<PREC> _pW{ pW };
707  _pW.preMultiply(UT_Matrix4T<PREC>(pLS_inv));
708  UT_Matrix4T<PREC> _pW_inv;
709  _pW.invert(_pW_inv);
710  L = W * _pW_inv;
711  if (HAS_EFFECTIVE_LOCAL && effective_local)
712  *effective_local = L;
713  break;
714  }
715  default:
716  {
717  L = W * pW_inv;
718  if (HAS_EFFECTIVE_LOCAL && effective_local)
719  *effective_local = L;
720  break;
721  }
722  }
723 }
724 
725 /// Extract relative transform with scale inheritance
726 template<typename PREC>
727 inline UT_Matrix4T<PREC>
729  const UT_Matrix4T<PREC>& W,
730  const UT_Matrix4T<PREC>& pW,
731  const UT_Matrix4T<PREC>& pL,
733  UT_Matrix4T<PREC>* effective_local)
734 {
735 
736  UT_Matrix4T<PREC> ret;
737  UT_Matrix4T<PREC>pW_inv;
738  pW.invert(pW_inv);
739  UTtransformExtractLocal(ret, W, pW, pW_inv, pL, mode, effective_local);
740  return ret;
741 }
742 
743 template<
744  typename PREC,
745  bool NORMALIZE_WEIGHTS,
746  UT_SLERP_METHOD METHOD,
747  UT_SLERP_FLIP_METHOD FLIP_METHOD
748 >
749 inline UT_Matrix4T<PREC>
751  const UT_Array<PREC> &input_weights)
752 {
753  const int n = xforms.size();
754  if (n < 1)
755  {
756  UT_ASSERT(!"Not enough inputs");
758  }
759  UT_ASSERT(n == input_weights.size());
760 
761  const PREC *weights = input_weights.data();
762 
763  const int kSmallArrayElements = 16;
765  if (NORMALIZE_WEIGHTS)
766  {
767  PREC total_weights = 0;
768  for (PREC w : input_weights)
769  total_weights += w;
770 
771  if (total_weights > 0)
772  {
773  new_weights.setSizeNoInit(input_weights.size());
774  for (int i = 0; i < n; ++i)
775  new_weights[i] = input_weights[i] / total_weights;
776  weights = new_weights.data();
777  }
778  }
779 
780  UT_Vector3T<PREC> translate;
782  UT_Matrix3T<PREC> stretch;
783 
784  switch (METHOD)
785  {
787  {
788  xforms[0].getTranslates(translate);
789  translate *= weights[0];
790 
791  rotate = xforms[0];
792  rotate.makeRotationMatrix(&stretch);
793  stretch *= weights[0];
794 
796  quat.updateFromRotationMatrix(rotate);
797  UT_QuaternionT<PREC> ref = quat;
798  quat *= weights[0];
799 
800  for (int i = 1; i < n; ++i)
801  {
802  const PREC w = weights[i];
803 
804  UT_Vector3T<PREC> t;
805  xforms[i].getTranslates(t);
806  translate += w * t;
807 
808  UT_Matrix3T<PREC> stretch_tmp;
809  UT_Matrix3T<PREC> rotate_tmp{xforms[i]};
810  rotate_tmp.makeRotationMatrix(&stretch_tmp);
811  stretch += w * stretch_tmp;
812 
814  q.updateFromRotationMatrix(rotate_tmp);
815  if (dot(ref, q) < 0)
816  q.negate();
817  quat += w * q;
818 
819  if (FLIP_METHOD == UT_SLERP_FLIP_METHOD::SUCCESSIVE)
820  ref = q;
821  }
822 
823  quat.getRotationMatrix(rotate); // uses normalized quat
824  break;
825  }
827  {
829  sizeof(UT_QuaternionT<PREC>)*kSmallArrayElements> rots;
830 
831  translate = 0;
832  stretch = 0;
833  rots.setSizeNoInit(n);
834  for (int i = 0; i < n; ++i)
835  {
836  const PREC w = weights[i];
837 
838  UT_Vector3T<PREC> t;
839  xforms[i].getTranslates(t);
840  translate += w * t;
841 
842  UT_Matrix3T<PREC> stretch_tmp;
843  UT_Matrix3T<PREC> rotate_tmp{xforms[i]};
844  rotate_tmp.makeRotationMatrix(&stretch_tmp);
845  stretch += w * stretch_tmp;
846 
847  rots[i].updateFromRotationMatrix(rotate_tmp);
848  }
849 
851  quat.interpolate(rots.data(), weights, rots.size());
852  quat.getRotationMatrix(rotate);
853  break;
854  }
855  }
856 
857  UT_Matrix4T<PREC> result{stretch};
858  result *= rotate;
859  result.setTranslates(translate);
860  return result;
861 }
862 
863 template<
864  typename PREC,
865  UT_SLERP_METHOD METHOD,
866  UT_SLERP_FLIP_METHOD FLIP_METHOD
867 >
869 UTslerp( const UT_Matrix4T<PREC> &xform0, const UT_Matrix4T<PREC> &xform1,
870  const PREC input_weight)
871 {
872  const PREC weight0 = ((PREC)1.0) - input_weight;
873  const PREC weight1 = input_weight;
874 
875  UT_Vector3T<PREC> translate;
877  UT_Matrix3T<PREC> stretch;
878 
879  switch (METHOD)
880  {
882  {
883  xform0.getTranslates(translate);
884  translate *= weight0;
885 
886  rotate = xform0;
887  rotate.makeRotationMatrix(&stretch);
888  stretch *= weight0;
889 
891  quat.updateFromRotationMatrix(rotate);
892  UT_QuaternionT<PREC> ref = quat;
893  quat *= weight0;
894 
895  {
896  const PREC w = weight1;
897 
898  UT_Vector3T<PREC> t;
899  xform1.getTranslates(t);
900  translate += w * t;
901 
902  UT_Matrix3T<PREC> stretch_tmp;
903  UT_Matrix3T<PREC> rotate_tmp{xform1};
904  rotate_tmp.makeRotationMatrix(&stretch_tmp);
905  stretch += w * stretch_tmp;
906 
908  q.updateFromRotationMatrix(rotate_tmp);
909  if (dot(ref, q) < 0)
910  q.negate();
911  quat += w * q;
912 
913  if (FLIP_METHOD == UT_SLERP_FLIP_METHOD::SUCCESSIVE)
914  ref = q;
915  }
916 
917  quat.getRotationMatrix(rotate); // uses normalized quat
918  break;
919  }
921  {
922  translate = 0;
923  stretch = 0;
924 
927  {
928  const PREC w = weight0;
929 
930  UT_Vector3T<PREC> t;
931  xform0.getTranslates(t);
932  translate += w * t;
933 
934  UT_Matrix3T<PREC> stretch_tmp;
935  UT_Matrix3T<PREC> rotate_tmp{xform0};
936  rotate_tmp.makeRotationMatrix(&stretch_tmp);
937  stretch += w * stretch_tmp;
938 
939  rot0.updateFromRotationMatrix(rotate_tmp);
940  }
941 
942  {
943  const PREC w = weight1;
944 
945  UT_Vector3T<PREC> t;
946  xform1.getTranslates(t);
947  translate += w * t;
948 
949  UT_Matrix3T<PREC> stretch_tmp;
950  UT_Matrix3T<PREC> rotate_tmp{xform1};
951  rotate_tmp.makeRotationMatrix(&stretch_tmp);
952  stretch += w * stretch_tmp;
953 
954  rot1.updateFromRotationMatrix(rotate_tmp);
955  }
956 
958  quat = rot0.interpolate(rot1,weight1);
959  quat.getRotationMatrix(rotate);
960  break;
961  }
962  }
963 
964  UT_Matrix4T<PREC> result{stretch};
965  result *= rotate;
966  result.setTranslates(translate);
967  return result;
968 }
969 
970 template<
971  typename PREC,
972  UT_SLERP_METHOD METHOD,
973  UT_SLERP_FLIP_METHOD FLIP_METHOD
974 >
975 inline UT_Matrix4T<PREC>
976 UTslerp( const UT_Matrix4T<PREC> &xform0, const UT_Matrix4T<PREC> &xform1,
977  const PREC input_weight, UT_SlerpCache<PREC>* makerotcache)
978 {
979  const PREC weight0 = ((PREC)1.0) - input_weight;
980  const PREC weight1 = input_weight;
981 
982  UT_Vector3T<PREC> translate;
984  UT_Matrix3T<PREC> stretch;
985 
986 
987  auto cachedMakeRot = [&makerotcache](
988  const UT_Matrix4T<PREC> &in_xfo,
989  UT_Matrix3T<PREC> &io_stretch,
990  UT_QuaternionT<PREC> &io_quat)
991  {
992  // Check if we have a cache hit
993  if (makerotcache->rot==in_xfo)
994  {
995  io_quat = makerotcache->quat;
996  io_stretch = makerotcache->stretch;
997  }
998  else
999  {
1000  // do the computation of stretch and quat
1001  // The intermediate makeRotation matrix isn't cached
1002  UT_Matrix3T<PREC> in_rot{in_xfo};
1003  in_rot.makeRotationMatrix(&io_stretch);
1004  io_quat.updateFromRotationMatrix(in_rot);
1005 
1006  // Fill the cache
1007  makerotcache->rot = in_xfo;
1008  makerotcache->quat = io_quat;
1009  makerotcache->stretch = io_stretch;
1010  }
1011 
1012  makerotcache++;
1013  };
1014 
1015  switch (METHOD)
1016  {
1018  {
1019  xform0.getTranslates(translate);
1020  translate *= weight0;
1021 
1022  UT_QuaternionT<PREC> quat;
1023  cachedMakeRot(xform0,stretch,quat);
1024  stretch *= weight0;
1025 
1026  UT_QuaternionT<PREC> ref = quat;
1027  quat *= weight0;
1028 
1029  {
1030  const PREC w = weight1;
1031 
1032  UT_Vector3T<PREC> t;
1033  xform1.getTranslates(t);
1034  translate += w * t;
1035 
1037  UT_Matrix3T<PREC> stretch_tmp;
1038  cachedMakeRot(xform1,stretch_tmp,q);
1039  stretch += w * stretch_tmp;
1040 
1041  if (dot(ref, q) < 0)
1042  q.negate();
1043  quat += w * q;
1044 
1045  if (FLIP_METHOD == UT_SLERP_FLIP_METHOD::SUCCESSIVE)
1046  ref = q;
1047  }
1048 
1049  quat.getRotationMatrix(rotate); // uses normalized quat
1050  break;
1051  }
1053  {
1054  translate = 0;
1055  stretch = 0;
1056 
1057  UT_QuaternionT<PREC> rot0;
1058  {
1059  const PREC w = weight0;
1060 
1061  UT_Vector3T<PREC> t;
1062  xform0.getTranslates(t);
1063  translate += w * t;
1064 
1065  UT_Matrix3T<PREC> stretch_tmp;
1066  cachedMakeRot(xform0,stretch_tmp,rot0);
1067  stretch += w * stretch_tmp;
1068  }
1069 
1070  UT_QuaternionT<PREC> rot1;
1071  {
1072  const PREC w = weight1;
1073 
1074  UT_Vector3T<PREC> t;
1075  xform1.getTranslates(t);
1076  translate += w * t;
1077 
1078  UT_Matrix3T<PREC> stretch_tmp;
1079  cachedMakeRot(xform1,stretch_tmp,rot1);
1080  stretch += w * stretch_tmp;
1081  }
1082 
1083  UT_QuaternionT<PREC> quat;
1084  quat = rot0.interpolate(rot1,weight1);
1085  quat.getRotationMatrix(rotate);
1086  break;
1087  }
1088  }
1089 
1090  UT_Matrix4T<PREC> result{stretch};
1091  result *= rotate;
1092  result.setTranslates(translate);
1093  return result;
1094 }
1095 
1096 namespace UT_Detail
1097 {
1098  template <typename T, bool NORMALIZE_WEIGHTS>
1099  struct QuatSlerp;
1100 
1101  template <typename T>
1102  struct QuatSlerp<T, false>
1103  {
1105  const UT_Array<UT_QuaternionT<T>> &quats,
1106  const UT_Array<T> &weights)
1107  {
1109  q.interpolate(quats.data(), weights.data(), weights.size());
1110  return q;
1111  }
1112  };
1113 
1114  template <typename T>
1115  struct QuatSlerp<T, true>
1116  {
1118  const UT_Array<UT_QuaternionT<T>> &quats,
1119  const UT_Array<T> &weights)
1120  {
1121  const T *w = weights.data();
1122 
1123  UT_SmallArray<T,sizeof(T)*32> tmp_weights;
1124  T total = 0;
1125  const int n = weights.size();
1126  for (int i = 0; i < n; ++i)
1127  total += w[i];
1128  if (total > 0)
1129  {
1130  tmp_weights.setSizeNoInit(n);
1131  for (int i = 0; i < n; ++i)
1132  tmp_weights[i] = w[i] / total;
1133  w = tmp_weights.data();
1134  }
1135 
1137  q.interpolate(quats.data(), w, n);
1138  return q;
1139  }
1140  };
1141 }
1142 
1143 template <typename PREC, bool NORMALIZE_WEIGHTS>
1146  const UT_Array<PREC> &weights)
1147 {
1148  UT_ASSERT(quats.size() == weights.size());
1149  return UT_Detail::QuatSlerp<PREC, NORMALIZE_WEIGHTS>()(quats, weights);
1150 }
1151 
1152 template<typename PREC>
1153 inline void
1155  UT_Matrix4T<PREC> &local,
1156  const UT_Vector3T<PREC> &parent_local_s,
1157  const UT_XformOrder &adjust_ord,
1158  const UT_Vector3T<PREC> &parm_t,
1159  const UT_Vector3T<PREC> &parm_r,
1160  const UT_Vector3T<PREC> &parm_s,
1161  const UT_Vector3T<PREC> &parm_p,
1162  const UT_Vector3T<PREC> &parm_pr,
1163  const UT_ScaleInheritanceMode scalecomp,
1164  const UT_ParameterTransformMode mode
1165 )
1166 {
1168 
1169  typename UT_Matrix4T<PREC>::PivotSpace pivotspace;
1170 
1171  pivotspace.myTranslate = parm_p;
1172  pivotspace.myRotate = parm_pr;
1173 
1174  UT_Matrix4T<PREC> adjust {1};
1175  adjust.xform(adjust_ord,
1176  parm_t[0], parm_t[1], parm_t[2],
1177  parm_r[0], parm_r[1], parm_r[2],
1178  parm_s[0], parm_s[1], parm_s[2],
1179  pivotspace);
1180 
1182  {
1183  local = adjust;
1184  return;
1185  }
1186 
1187  bool scales_local_t = scalecomp < UT_ScaleInheritanceMode::SCALE_ONLY;
1188 
1189  UT_Vector3T<PREC> local_t;
1190  UT_Vector3T<PREC> local_r;
1191  UT_Vector3T<PREC> local_s;
1192  local.explode(order, local_r, local_s, local_t, nullptr);
1193 
1194  UT_Vector3T<PREC> t;
1195  UT_Vector3T<PREC> r;
1196  UT_Vector3T<PREC> s;
1197  adjust.explode(
1198  order, r, s, t, nullptr );
1199 
1201  {
1202  if( scales_local_t )
1203  {
1204  UT_Matrix3T<PREC> t_space(local);
1205  // we'll just take the orientation of our 3x3 transform
1206  // and apply the correct scales below
1207  t_space.polarDecompose();
1208 
1209  // We can now apply the scales in our own local space
1210  t_space.prescale(parent_local_s);
1211 
1212  // put the translate delta into the input space by multiplying
1213  // by our input 3x3 transform
1214  t *= t_space;
1215  // inversely scale the delta by the parent local scales,
1216  // ready to be combined with the existing local translates
1217  t *= 1.0 / (parent_local_s);
1218  }
1219  else // our scale compensate mode is >=UT_ScaleInheritanceMode::SCALE_ONLY
1220  {
1221  UT_Matrix4T<PREC> local_polardecomp{ local };
1222  local_polardecomp.polarDecompose();
1223  t *= UT_Matrix3T<PREC>(local_polardecomp);
1224  }
1225  }
1226 
1227  // Apply the local_r by multiplying the quaternion
1228  {
1229  UT_QuaternionT<PREC> parm_q( r, order);
1230  UT_QuaternionT<PREC> local_q( local_r, order);
1231 
1234  q = parm_q * local_q;
1235  else
1236  q = local_q * parm_q;
1237 
1238  // Get back euler angles from the quaternion
1239  UT_Matrix3T<PREC> mq;
1240  q.getRotationMatrix(mq);
1241  mq.crack(r,order);
1242  r.radToDeg();
1243  }
1244 
1245  // Apply local t and s
1246  t = local_t + t;
1247  s = local_s * s;
1248 
1249  // rebuild the transform
1250  local.identity();
1251  local.xform( order,
1252  t.x(), t.y(), t.z(),
1253  r.x(), r.y(), r.z(),
1254  s.x(), s.y(), s.z() );
1255 }
1256 
1257 template <typename PREC>
1258 inline void
1260  const UT_Matrix4T<PREC> &local,
1261  const UT_Matrix4T<PREC> &inputlocal,
1262  const UT_XformOrder &parmorder,
1263  const UT_ScaleInheritanceMode scalecomp,
1264  const UT_ParameterTransformMode mode,
1265  const UT_Vector3T<PREC> &parent_local_s,
1266  UT_Vector3T<PREC> &parm_t,
1267  UT_Vector3T<PREC> &parm_r,
1268  UT_Vector3T<PREC> &parm_s
1269 )
1270 {
1271  UT_Matrix4T<PREC> adjust {1};
1273 
1275  {
1276  local.explode(parmorder, parm_r, parm_s, parm_t, nullptr);
1277  parm_r.radToDeg();
1278  return;
1279  }
1280 
1281  bool scales_local_t = scalecomp < UT_ScaleInheritanceMode::SCALE_ONLY;
1282 
1283  UT_Vector3T<PREC> local_t;
1284  UT_Vector3T<PREC> local_r;
1285  UT_Vector3T<PREC> local_s;
1286  local.explode(order, local_r, local_s, local_t, nullptr);
1287 
1288  UT_Vector3T<PREC> input_t;
1289  UT_Vector3T<PREC> input_r;
1290  UT_Vector3T<PREC> input_s;
1291  inputlocal.explode(order, input_r, input_s, input_t, nullptr);
1292 
1293  adjust.explode(order, parm_r, parm_s, parm_t, nullptr);
1294 
1295  // Apply local t and s
1296  parm_t = local_t - input_t;
1297  parm_s = local_s / input_s;
1298 
1300  {
1301  if (scales_local_t)
1302  {
1303 
1304  parm_t *= (parent_local_s);
1305 
1306  UT_Matrix3T<PREC> t_space(inputlocal);
1307  t_space.polarDecompose();
1308 
1309  t_space.prescale(parent_local_s);
1310  t_space.invert();
1311 
1312  parm_t *= t_space;
1313  }
1314  else // our scale compensate mode is >=UT_ScaleInheritanceMode::SCALE_ONLY
1315  {
1316  UT_Matrix3T<PREC> t_space(inputlocal);
1317  t_space.polarDecompose();
1318  t_space.invert();
1319  parm_t *= t_space;
1320  }
1321  }
1322 
1323  {
1324  UT_QuaternionT<PREC> input_q( input_r, order);
1325  UT_QuaternionT<PREC> local_q( local_r, order);
1326 
1329  {
1330  input_q.invert();
1331  q = local_q * input_q;
1332  }
1333  else
1334  {
1335  input_q.invert();
1336  q = input_q * local_q;
1337  }
1338 
1339  // Get back euler angles from the quaternion
1340  UT_Matrix3T<PREC> mq;
1341  q.getRotationMatrix(mq);
1342  mq.crack(parm_r,order);
1343  parm_r.radToDeg();
1344  }
1345 
1346  UT_Matrix4D result(1.0);
1347  result.xform(order, parm_t[0], parm_t[1], parm_t[2],
1348  parm_r[0], parm_r[1], parm_r[2],
1349  parm_s[0], parm_s[1], parm_s[2]);
1350  result.explode(parmorder, parm_r, parm_s, parm_t, nullptr);
1351  parm_r.radToDeg();
1352 }
1353 
1354 namespace detail
1355 {
1356 
1357 template <typename PREC>
1358 SYS_FORCE_INLINE void
1360  const UT_Matrix4T<PREC> &xform,
1361  const UT_Vector3T<PREC> &reflect_hint,
1362  UT_Vector3T<PREC> &xform_r,
1363  UT_Vector3T<PREC> &xform_s,
1364  UT_Vector3T<PREC> &xform_t,
1365  const typename UT_Matrix4T<PREC>::PivotSpace &pivot_space,
1366  UT_Vector3T<PREC> *xform_sh)
1367 {
1368  // Explode matrices using SRT XYZ order.
1369  const UT_XformOrder order(
1370  UT_XformOrder::rstOrder::SRT, UT_XformOrder::xyzOrder::XYZ);
1371 
1372  xform.explode(order, xform_r, xform_s, xform_t, pivot_space, xform_sh);
1373 
1374  // If a reflection hint hasn't been provided, then explode does everything
1375  // we need.
1376  if(SYSsignum(reflect_hint[0]) >= 0
1377  && SYSsignum(reflect_hint[1]) >= 0
1378  && SYSsignum(reflect_hint[2]) >= 0)
1379  return;
1380 
1381  UT_Matrix3T<PREC> xform3;
1382  xform3.identity();
1383  xform3.rotate(xform_r, order);
1384 
1385  // Restore the reflections introduced by explode() and then reintroduce
1386  // the reflections from the hint.
1387  if ((xform_s[0] < 0) != (reflect_hint[0] < 0))
1388  {
1389  xform3[0] *= -1;
1390  xform_s[0] *= -1;
1391  }
1392 
1393  if ((xform_s[1] < 0) != (reflect_hint[1] < 0))
1394  {
1395  xform3[1] *= -1;
1396  xform_s[1] *= -1;
1397  }
1398 
1399  if ((xform_s[2] < 0) != (reflect_hint[2] < 0))
1400  {
1401  xform3[2] *= -1;
1402  xform_s[2] *= -1;
1403  }
1404 
1405  // Re-crack the matrix
1406  xform3.crack(xform_r, order);
1407 }
1408 
1409 } // namespace detail
1410 
1411 template <typename PREC>
1414  const UT_Matrix4T<PREC> &local,
1415  const UT_XformOrder &parm_ord,
1416  UT_Vector3T<PREC> parm_t,
1417  UT_Vector3T<PREC> parm_r,
1418  UT_Vector3T<PREC> parm_s,
1419  UT_Vector3T<PREC> parm_sh,
1420  const typename UT_Matrix4T<PREC>::PivotSpace &pivot_space,
1421  const UT_ParameterTransformMode mode)
1422 {
1423  using Quat = UT_QuaternionT<PREC>;
1424  using Vector3 = UT_Vector3T<PREC>;
1425  using Matrix4 = UT_Matrix4T<PREC>;
1426 
1427  // Skip extra work if we don't need to combine the local matrix
1429  {
1430  Matrix4 parm{1};
1431  parm.xform(parm_ord,
1432  parm_t(0), parm_t(1), parm_t(2),
1433  parm_r(0), parm_r(1), parm_r(2),
1434  parm_s(0), parm_s(1), parm_s(2),
1435  parm_sh(0), parm_sh(1), parm_sh(2),
1436  pivot_space);
1437 
1438  return parm;
1439  }
1440 
1441  // Explode matrices into components (using SRT XYZ order)
1442  const UT_XformOrder order{
1443  UT_XformOrder::rstOrder::SRT, UT_XformOrder::xyzOrder::XYZ};
1444 
1445  // Only compose and decompose the parm matrix if we need to change its
1446  // transform or rotation order
1447  if (parm_ord != order)
1448  {
1449  Matrix4 parm{1};
1450  parm.xform(parm_ord,
1451  parm_t(0), parm_t(1), parm_t(2),
1452  parm_r(0), parm_r(1), parm_r(2),
1453  parm_s(0), parm_s(1), parm_s(2),
1454  parm_sh(0), parm_sh(1), parm_sh(2),
1455  pivot_space);
1456 
1457  parm.explode(order, parm_r, parm_s, parm_t, pivot_space, &parm_sh);
1458  }
1459  else
1460  {
1461  parm_r.degToRad();
1462  }
1463 
1464  Vector3 local_t, local_r, local_s, local_sh;
1465  local.explode(order, local_r, local_s, local_t, pivot_space, &local_sh);
1466 
1467  // Combine scales and shears
1468  Vector3 combined_s = parm_s * local_s;
1469  Vector3 combined_sh = parm_sh + local_sh;
1470 
1471  // Combine rotations and translations
1472  Vector3 combined_t, combined_r;
1473  Quat combined_q;
1474  Quat parm_q{parm_r, order};
1475  Quat local_q{local_r, order};
1476 
1478  {
1479  // Check for a left-handed transform
1480  if (SYSsignum(local_s(0) * local_s(1) * local_s(2) < 0))
1481  {
1482  // I don't love this, but it's here to provide minimal backwards
1483  // compatibility for an odd behavior that exists in
1484  // UTcombineParameterTransform().
1485  parm_t.negate();
1486  }
1487 
1488  combined_q = local_q * parm_q;
1489  combined_t = local_q.rotate(parm_t) + local_t;
1490  }
1491  else if (mode == UT_ParameterTransformMode::MODE_POST)
1492  {
1493  combined_q = parm_q * local_q;
1494  combined_t = parm_t + local_t;
1495  }
1496  else
1497  {
1498  UT_ASSERT(!"Unknown UT_ParameterTransformMode.");
1499  return local;
1500  }
1501 
1502  combined_r = combined_q.computeRotations(order);
1503  combined_r.radToDeg();
1504 
1505  Matrix4 combined_local{1};
1506  combined_local.xform(order,
1507  combined_t(0), combined_t(1), combined_t(2),
1508  combined_r(0), combined_r(1), combined_r(2),
1509  combined_s(0), combined_s(1), combined_s(2),
1510  combined_sh(0), combined_sh(1), combined_sh(2),
1511  pivot_space);
1512 
1513  return combined_local;
1514 }
1515 
1516 template <typename PREC>
1517 SYS_FORCE_INLINE std::tuple<
1518  UT_Vector3T<PREC>,
1519  UT_Vector3T<PREC>,
1520  UT_Vector3T<PREC>,
1521  UT_Vector3T<PREC>>
1523  const UT_Matrix4T<PREC> &local,
1524  const UT_Matrix4T<PREC> &relative_local,
1525  const UT_XformOrder &parm_ord,
1526  const typename UT_Matrix4T<PREC>::PivotSpace &pivot_space,
1527  const UT_ParameterTransformMode mode,
1528  const UT_Vector3T<PREC> &reflect_hint)
1529 {
1530  using Quat = UT_QuaternionT<PREC>;
1531  using Vector3 = UT_Vector3T<PREC>;
1532  using Matrix4 = UT_Matrix4T<PREC>;
1533 
1534  Vector3 parm_t, parm_r, parm_s, parm_sh;
1536  {
1537  local.explode(parm_ord, parm_r, parm_s, parm_t, pivot_space, &parm_sh);
1538  parm_r.radToDeg();
1539  return {parm_t, parm_r, parm_s, parm_sh};
1540  }
1541 
1542  // Explode matrices into components (using SRT XYZ order)
1543  const UT_XformOrder order{
1544  UT_XformOrder::rstOrder::SRT, UT_XformOrder::xyzOrder::XYZ};
1545 
1546  // Explode local matrix
1547  // NB: Extracting the scales with the reflection hint only works when the
1548  // relative matrix is right-handed. Its intended use case is to be able to
1549  // add negative scales to a simple control. It will not work when the
1550  // control has been mirrored (via a reflection into its rest transform).
1551  // Negative scales are NOT supported in the apex transform model. The
1552  // little support we do have is mostly for backwards compatibility :).
1553  Vector3 local_t, local_r, local_s, local_sh;
1555  local, reflect_hint, local_r, local_s, local_t, pivot_space,
1556  &local_sh);
1557 
1558  // Explode relative local matrix
1559  Vector3 rel_t, rel_r, rel_s, rel_sh;
1560  relative_local.explode(order, rel_r, rel_s, rel_t, pivot_space, &rel_sh);
1561 
1562  // Extract scale and shear
1563  parm_s = local_s / rel_s;
1564  parm_sh = local_sh - rel_sh;
1565 
1566  // Extract rotation and translation
1567  Quat parm_q;
1568  Quat local_q{local_r, order};
1569  Quat rel_inv_q{rel_r, order};
1570  rel_inv_q.invert();
1571 
1573  {
1574  parm_q = rel_inv_q * local_q;
1575  parm_t = rel_inv_q.rotate(local_t - rel_t);
1576 
1577  // Check for a left-handed transform
1578  if (SYSsignum(rel_s[0] * rel_s[1] * rel_s[2]) < 0)
1579  {
1580  // I don't love this, but it's here to provide minimal backwards
1581  // compatibility for an odd behavior that exists in
1582  // UTextractParameterTransform().
1583  parm_t.negate();
1584  }
1585  }
1586  else if (mode == UT_ParameterTransformMode::MODE_POST)
1587  {
1588  parm_q = local_q * rel_inv_q;
1589  parm_t = local_t - rel_t;
1590  }
1591  else
1592  {
1593  UT_ASSERT(!"Unknown UT_ParameterTransformMode.");
1594  return {Vector3{0.0}, Vector3{0.0}, Vector3{1}, Vector3{0.0}};
1595  }
1596 
1597  parm_r = parm_q.computeRotations(order);
1598  parm_r.radToDeg();
1599 
1600  // If the xform order differs, then we must compose and decompose
1601  // to get the correct order
1602  if (parm_ord != order)
1603  {
1604  Matrix4 parm{1};
1605  parm.xform(order,
1606  parm_t(0), parm_t(1), parm_t(2),
1607  parm_r(0), parm_r(1), parm_r(2),
1608  parm_s(0), parm_s(1), parm_s(2),
1609  parm_sh(0), parm_sh(1), parm_sh(2),
1610  pivot_space);
1611 
1612  parm.explode(parm_ord, parm_r, parm_s, parm_t, pivot_space, &parm_sh);
1613  parm_r.radToDeg();
1614  }
1615 
1616  return {parm_t, parm_r, parm_s, parm_sh};
1617 }
1618 
1619 #endif // __UT_TRANSFORMUTIL_H_INCLUDED__
UT_Matrix3T< PREC > stretch
Definition: ImathQuat.h:42
#define UT_TRANSFORM_COMBINE(M, A)
Transformation order of scales, rotates, and translates.
Definition: UT_XformOrder.h:23
void setSizeNoInit(exint newsize)
Definition: UT_Array.h:702
void radToDeg()
conversion between degrees and radians
constexpr SYS_FORCE_INLINE T & z() noexcept
Definition: UT_Vector3.h:667
GLdouble s
Definition: glad.h:3009
void prescale(T sx, T sy, T sz)
Definition: UT_Matrix3.h:867
**But if you need a result
Definition: thread.h:622
UT_Matrix4T< PREC > UTslerp(const UT_Array< UT_Matrix4T< PREC >> &xforms, const UT_Array< PREC > &input_weights)
Spherically blend transforms by decomposing into separate quaternions.
SYS_FORCE_INLINE UT_Matrix4T< PREC > UTcombineParametersWithLocalTransform(const UT_Matrix4T< PREC > &local, const UT_XformOrder &parm_ord, UT_Vector3T< PREC > parm_t, UT_Vector3T< PREC > parm_r, UT_Vector3T< PREC > parm_s, UT_Vector3T< PREC > parm_sh, const typename UT_Matrix4T< PREC >::PivotSpace &pivot_space=typename UT_Matrix4T< PREC >::PivotSpace{}, const UT_ParameterTransformMode mode=UT_ParameterTransformMode::MODE_PRE)
void getRotationMatrix(UT_Matrix3 &mat) const
GLdouble GLdouble GLdouble q
Definition: glad.h:2445
UT_ParameterTransformMode
exint size() const
Definition: UT_Array.h:653
UT_QuaternionT< PREC > quat
void degToRad()
conversion between degrees and radians
UT_SLERP_METHOD
Method of slerp for blending.
void UTcombineParameterTransform(UT_Matrix4T< PREC > &local, const UT_Vector3T< PREC > &parent_local_s, const UT_XformOrder &adjust_ord, const UT_Vector3T< PREC > &parm_t, const UT_Vector3T< PREC > &parm_r, const UT_Vector3T< PREC > &parm_s, const UT_Vector3T< PREC > &parm_p, const UT_Vector3T< PREC > &parm_pr, const UT_ScaleInheritanceMode scalecomp, const UT_ParameterTransformMode mode)
SYS_FORCE_INLINE void UTtransformExtractLocalT(UT_Matrix4T< PREC > &L, const UT_Matrix4T< PREC > &W, const UT_Matrix4T< PREC > &pW, const UT_Matrix4T< PREC > &pW_inv, const UT_Matrix4T< PREC > &pL, UT_Matrix4T< PREC > *effective_local=nullptr)
Extract relative transform with scale inheritance.
GLdouble n
Definition: glcorearb.h:2008
void identity()
Set the matrix to identity.
Definition: UT_Matrix3.h:1128
GLint ref
Definition: glcorearb.h:124
simple inheritance: W = L * pW
constexpr SYS_FORCE_INLINE void negate() noexcept
Definition: UT_Vector3.h:351
static const UT_Matrix4T< T > & getIdentityMatrix()
XYZ
Definition: ImathEuler.h:133
fpreal64 dot(const CE_VectorT< T > &a, const CE_VectorT< T > &b)
Definition: CE_Vector.h:137
#define SYS_FORCE_INLINE
Definition: SYS_Inline.h:45
int explode(const UT_XformOrder &order, UT_Vector3F &r, UT_Vector3F &s, UT_Vector3F &t, UT_Vector3F *shears=0) const
Definition: UT_Matrix4.h:954
#define IF_UT_TRANSFORM_COMBINE(M, A)
SYS_FORCE_INLINE void UTexplodeWithReflectionHint(const UT_Matrix4T< PREC > &xform, const UT_Vector3T< PREC > &reflect_hint, UT_Vector3T< PREC > &xform_r, UT_Vector3T< PREC > &xform_s, UT_Vector3T< PREC > &xform_t, const typename UT_Matrix4T< PREC >::PivotSpace &pivot_space, UT_Vector3T< PREC > *xform_sh)
constexpr int SYSsignum(const F a) noexcept
Definition: SYS_Math.h:176
UT_Matrix4T< Float > Matrix4
Definition: APEX_Include.h:68
GLdouble GLdouble GLint GLint order
Definition: glad.h:2676
void identity()
Set the matrix to identity.
Definition: UT_Matrix4.h:1124
UT_Matrix4T< PREC > rot
SYS_FORCE_INLINE UT_Matrix4T< PREC > UTtransformCombineLocal(const UT_Matrix4T< PREC > &L, const UT_Matrix4T< PREC > &pW, const UT_Matrix4T< PREC > &pL, UT_ScaleInheritanceMode mode=UT_ScaleInheritanceMode::DEFAULT, UT_Matrix4T< PREC > *effective_local=nullptr, UT_Matrix3T< PREC > *pLS_ptr=nullptr, UT_Matrix3T< PREC > *pLS_inv_ptr=nullptr, bool *pLS_init_ptr=nullptr)
ImageBuf OIIO_API rotate(const ImageBuf &src, float angle, string_view filtername=string_view(), float filterwidth=0.0f, bool recompute_roi=false, ROI roi={}, int nthreads=0)
UT_Matrix4T< PREC > UTtransformExtractLocal(const UT_Matrix4T< PREC > &W, const UT_Matrix4T< PREC > &pW, const UT_Matrix4T< PREC > &pL, UT_ScaleInheritanceMode mode=UT_ScaleInheritanceMode::DEFAULT, UT_Matrix4T< PREC > *effective_local=nullptr)
Extract relative transform with scale inheritance.
UT_SLERP_FLIP_METHOD
Flip method for slerp to ensure consistency during blending.
void xform(const UT_XformOrder &order, T tx=0, T ty=0, T tz=0, T rx=0, T ry=0, T rz=0, T sx=1, T sy=1, T sz=1, T px=0, T py=0, T pz=0, int reverse=0)
SYS_FORCE_INLINE std::tuple< UT_Vector3T< PREC >, UT_Vector3T< PREC >, UT_Vector3T< PREC >, UT_Vector3T< PREC > > UTextractParametersFromLocalTransform(const UT_Matrix4T< PREC > &local, const UT_Matrix4T< PREC > &relative_local, const UT_XformOrder &parm_ord, const typename UT_Matrix4T< PREC >::PivotSpace &pivot_space=typename UT_Matrix4T< PREC >::PivotSpace{}, const UT_ParameterTransformMode mode=UT_ParameterTransformMode::MODE_PRE, const UT_Vector3T< PREC > &reflection_hint=UT_Vector3T< PREC >(1., 1., 1.))
Extracts the local space transformation components from the local matrix with respect to the relative...
GLdouble t
Definition: glad.h:2397
void setTranslates(const UT_Vector3T< S > &translates)
Definition: UT_Matrix4.h:1438
UT_QuaternionT< T > operator()(const UT_Array< UT_QuaternionT< T >> &quats, const UT_Array< T > &weights)
GLenum mode
Definition: glcorearb.h:99
void UTextractParameterTransform(const UT_Matrix4T< PREC > &local, const UT_Matrix4T< PREC > &inputlocal, const UT_XformOrder &parmorder, const UT_ScaleInheritanceMode scalecomp, const UT_ParameterTransformMode mode, const UT_Vector3T< PREC > &parent_local_s, UT_Vector3T< PREC > &parm_t, UT_Vector3T< PREC > &parm_r, UT_Vector3T< PREC > &parm_s)
Definition: Types.h:305
SYS_NO_DISCARD_RESULT UT_QuaternionT< T > interpolate(const UT_QuaternionT< T > &target, T t, T b=0.0f) const
Interpolates between this quat (t==0) and the target (t==1)
T * data()
Definition: UT_Array.h:849
bool polarDecompose(UT_Matrix3T< T > *stretch=nullptr, bool reverse=true, const int max_iter=64, const T rel_tol=FLT_EPSILON)
int invert(T tol=0.0F)
GLubyte GLubyte GLubyte GLubyte w
Definition: glcorearb.h:857
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:156
void leftMult(const UT_Matrix4T< T > &m)
Definition: UT_Matrix4.h:1589
GLboolean r
Definition: glcorearb.h:1222
void rotate(UT_Vector3T< S > &axis, T theta, int norm=1)
PUGI__FN char_t * translate(char_t *buffer, const char_t *from, const char_t *to, size_t to_length)
Definition: pugixml.cpp:8574
normalized lerp of quaternion
use hemisphere of first quaternion
constexpr SYS_FORCE_INLINE T & y() noexcept
Definition: UT_Vector3.h:665
UT_ScaleInheritanceMode
Scale inheritance modes.
UT_QuaternionT< T > operator()(const UT_Array< UT_QuaternionT< T >> &quats, const UT_Array< T > &weights)
SYS_FORCE_INLINE UT_Matrix4T< PREC > UTtransformCombineLocalT(const UT_Matrix4T< PREC > &L, const UT_Matrix4T< PREC > &pW, const UT_Matrix4T< PREC > &pL, UT_Matrix4T< PREC > *effective_local=nullptr, UT_Matrix3T< PREC > *pLS_ptr=nullptr, UT_Matrix3T< PREC > *pLS_inv_ptr=nullptr, bool *pLS_init_ptr=nullptr)
bool polarDecompose(UT_Matrix3T< T > *stretch=nullptr, bool reverse=true, const int max_iter=64, const T rel_tol=FLT_EPSILON)
int crack(UT_Vector3T< S > &rvec, const UT_XformOrder &order) const
bool makeRotationMatrix(UT_Matrix3T< T > *stretch=nullptr, bool reverse=true, const int max_iter=64, const T rel_tol=FLT_EPSILON)
void getTranslates(UT_Vector3T< S > &translates) const
Definition: UT_Matrix4.h:1428
void updateFromRotationMatrix(const UT_Matrix3 &)
void preMultiply(const UT_Matrix4T< T > &m)
Definition: UT_Matrix4.h:348
constexpr SYS_FORCE_INLINE T & x() noexcept
Definition: UT_Vector3.h:663