Overte C++ Documentation
Transform.h
1 //
2 // Transform.h
3 // shared/src/gpu
4 //
5 // Created by Sam Gateau on 11/4/2014.
6 // Copyright 2014 High Fidelity, Inc.
7 // Copyright 2023 Overte e.V.
8 //
9 // Distributed under the Apache License, Version 2.0.
10 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
11 // SPDX-License-Identifier: Apache-2.0
12 //
13 
14 #ifndef hifi_gpu_Transform_h
15 #define hifi_gpu_Transform_h
16 
17 #include <assert.h>
18 
19 #include "GLMHelpers.h"
20 #include <glm/gtc/matrix_transform.hpp>
21 #include <glm/gtx/quaternion.hpp>
22 
23 #include <bitset>
24 
25 #include <memory>
26 
27 class QJsonObject;
28 class QJsonValue;
29 
30 inline bool isValidScale(glm::vec3 scale) {
31  bool result = scale.x != 0.0f && scale.y != 0.0f && scale.z != 0.0f;
32  if(!result){
33  qWarning() << "Scale is equal to 0";
34  }
35  // V8TODO: commented out for now
36  // assert(result);
37  return result;
38 }
39 
40 inline bool isValidScale(float scale) {
41  bool result = scale != 0.0f && !glm::isnan(scale) && !glm::isinf(scale);
42  assert(result);
43  return result;
44 }
45 
46 class Transform {
47 public:
48  friend QDebug& operator<<(QDebug& debug, const Transform& transform);
49  using Pointer = std::shared_ptr<Transform>;
50  typedef glm::mat4 Mat4;
51  typedef glm::mat3 Mat3;
52  typedef glm::vec4 Vec4;
53  typedef glm::vec3 Vec3;
54  typedef glm::vec2 Vec2;
55  typedef glm::quat Quat;
56 
57  Transform() :
58  _rotation(1.0f, 0.0f, 0.0f, 0.0f),
59  _scale(1.0f),
60  _translation(0.0f),
61  _flags(FLAG_CACHE_INVALID_BITSET) // invalid cache
62  {
63  }
64  Transform(Quat rotation, Vec3 scale, Vec3 translation) :
65  _rotation(rotation),
66  _scale(scale),
67  _translation(translation),
68  _flags(0xf) // FLAG_TRANSLATION | FLAG_ROTATION | FLAG_SCALING | FLAG_NON_UNIFORM
69  {
70  if (!isValidScale(_scale)) {
71  _scale = Vec3(1.0f);
72  }
73  }
74  Transform(const Transform& transform) :
75  _rotation(transform._rotation),
76  _scale(transform._scale),
77  _translation(transform._translation),
78  _flags(transform._flags)
79  {
80  invalidCache();
81  }
82  Transform(const Mat4& raw) {
83  evalFromRawMatrix(raw);
84  }
85  ~Transform() {}
86 
87  Transform& operator=(const Transform& transform) {
88  _rotation = transform._rotation;
89  _scale = transform._scale;
90  _translation = transform._translation;
91  _flags = transform._flags;
92  invalidCache();
93  return (*this);
94  }
95 
96  bool operator==(const Transform& other) const {
97  return _rotation == other._rotation && _scale == other._scale && _translation == other._translation;
98  }
99 
100  bool operator!=(const Transform& other) const {
101  return _rotation != other._rotation || _scale != other._scale || _translation != other._translation;
102  }
103 
104  Transform& setIdentity();
105 
106  const Vec3& getTranslation() const;
107  Transform& setTranslation(const Vec3& translation); // [new this] = [translation] * [this.rotation] * [this.scale]
108  Transform& preTranslate(const Vec3& translation); // [new this] = [translation] * [this]
109  Transform& postTranslate(const Vec3& translation); // [new this] = [this] * [translation] equivalent to:glTranslate
110 
111  const Quat& getRotation() const;
112  Transform& setRotation(const Quat& rotation); // [new this] = [this.translation] * [rotation] * [this.scale]
113  Transform& preRotate(const Quat& rotation); // [new this] = [rotation] * [this]
114  Transform& postRotate(const Quat& rotation); // [new this] = [this] * [rotation] equivalent to:glRotate
115 
116  const Vec3& getScale() const;
117  Transform& setScale(float scale);
118  Transform& setScale(const Vec3& scale); // [new this] = [this.translation] * [this.rotation] * [scale]
119  Transform& postScale(float scale); // [new this] = [this] * [scale] equivalent to:glScale
120  Transform& postScale(const Vec3& scale); // [new this] = [this] * [scale] equivalent to:glScale
121 
122  bool isIdentity() const { return (_flags & ~Flags(FLAG_CACHE_INVALID_BITSET)).none(); }
123  bool isTranslating() const { return _flags[FLAG_TRANSLATION]; }
124  bool isRotating() const { return _flags[FLAG_ROTATION]; }
125  bool isScaling() const { return _flags[FLAG_SCALING]; }
126  bool isUniform() const { return !isNonUniform(); }
127  bool isNonUniform() const { return _flags[FLAG_NON_UNIFORM]; }
128 
129  Transform& evalFromRawMatrix(const Mat4& matrix);
130  Transform& evalFromRawMatrix(const Mat3& rotationScalematrix);
131 
132  Mat4 getMatrix() const;
133  Mat4 getInverseMatrix() const;
134  Mat4& getMatrix(Mat4& result) const;
135  Mat4& getInverseMatrix(Mat4& result) const;
136  Mat4& getInverseTransposeMatrix(Mat4& result) const;
137 
138  Mat4& getRotationScaleMatrix(Mat4& result) const;
139  Mat4& getRotationScaleMatrixInverse(Mat4& result) const;
140 
141  Transform& evalInverse(Transform& result) const;
142 
143  Transform relativeTransform(const Transform& world) const;
144  Transform worldTransform(const Transform& relative) const;
145 
146  static void evalRotationScale(Quat& rotation, Vec3& scale, const Mat3& rotationScaleMatrix);
147 
148  static Transform& mult(Transform& result, const Transform& left, const Transform& right);
149 
150  // Left will be inversed before the multiplication
151  static Transform& inverseMult(Transform& result, const Transform& left, const Transform& right);
152 
153 
154  static Transform fromJson(const QJsonValue& json);
155  static QJsonObject toJson(const Transform& transform);
156 
157  Vec4 transform(const Vec4& pos) const;
158  Vec3 transform(const Vec3& pos) const;
159  Vec3 transformDirection(const Vec3& dir) const;
160 
161  bool containsNaN() const { return isNaN(_rotation) || isNaN(glm::dot(_scale, _translation)); }
162 
163 protected:
164 
165  enum Flag {
166  FLAG_CACHE_INVALID = 0,
167 
168  FLAG_TRANSLATION,
169  FLAG_ROTATION,
170  FLAG_SCALING,
171  FLAG_NON_UNIFORM,
172  FLAG_ZERO_SCALE,
173 
174  FLAG_PROJECTION,
175 
176  NUM_FLAGS,
177 
178  FLAG_CACHE_INVALID_BITSET = 1,
179  };
180  typedef std::bitset<NUM_FLAGS> Flags;
181 
182  // TRS
183  Quat _rotation;
184  Vec3 _scale;
185  Vec3 _translation;
186 
187  mutable Flags _flags;
188 
189  // Cached transform
190  mutable std::unique_ptr<Mat4> _matrix;
191 
192  bool isCacheInvalid() const { return _flags[FLAG_CACHE_INVALID]; }
193  void validCache() const { _flags.set(FLAG_CACHE_INVALID, false); }
194  void invalidCache() const { _flags.set(FLAG_CACHE_INVALID, true); }
195 
196  void flagTranslation() { _flags.set(FLAG_TRANSLATION, true); }
197  void unflagTranslation() { _flags.set(FLAG_TRANSLATION, false); }
198 
199  void flagRotation() { _flags.set(FLAG_ROTATION, true); }
200  void unflagRotation() { _flags.set(FLAG_ROTATION, false); }
201 
202  void flagScaling() { _flags.set(FLAG_SCALING, true); }
203  void unflagScaling() { _flags.set(FLAG_SCALING, false); }
204 
205 
206  void flagUniform() { _flags.set(FLAG_NON_UNIFORM, false); }
207  void flagNonUniform() { _flags.set(FLAG_NON_UNIFORM, true); }
208 
209  void updateCache() const;
210  Mat4& getCachedMatrix(Mat4& result) const;
211 };
212 
213 QDebug& operator<<(QDebug& debug, const Transform& transform);
214 
215 inline Transform& Transform::setIdentity() {
216  _translation = Vec3(0.0f);
217  _rotation = Quat(1.0f, 0.0f, 0.0f, 0.0f);
218  _scale = Vec3(1.0f);
219  _flags = Flags(FLAG_CACHE_INVALID_BITSET);
220  return *this;
221 }
222 
223 inline const Transform::Vec3& Transform::getTranslation() const {
224  return _translation;
225 }
226 
227 inline Transform& Transform::setTranslation(const Vec3& translation) {
228  invalidCache();
229  if (translation == Vec3()) {
230  unflagTranslation();
231  } else {
232  flagTranslation();
233  }
234  _translation = translation;
235  return *this;
236 }
237 
238 inline Transform& Transform::preTranslate(const Vec3& translation) {
239  if (translation == Vec3()) {
240  return *this;
241  }
242  invalidCache();
243  flagTranslation();
244  _translation += translation;
245  return *this;
246 }
247 
248 inline Transform& Transform::postTranslate(const Vec3& translation) {
249  if (translation == Vec3()) {
250  return *this;
251  }
252  invalidCache();
253  flagTranslation();
254 
255  Vec3 scaledT = translation;
256  if (isScaling()) {
257  scaledT *= _scale;
258  }
259 
260  if (isRotating()) {
261  _translation += glm::rotate(_rotation, scaledT);
262  } else {
263  _translation += scaledT;
264  }
265  return *this;
266 }
267 
268 inline const Transform::Quat& Transform::getRotation() const {
269  return _rotation;
270 }
271 
272 inline Transform& Transform::setRotation(const Quat& rotation) {
273  invalidCache();
274  if (rotation == Quat()) {
275  unflagRotation();
276  } else {
277  flagRotation();
278  }
279  _rotation = rotation;
280  return *this;
281 }
282 
283 inline Transform& Transform::preRotate(const Quat& rotation) {
284  if (rotation == Quat()) {
285  return *this;
286  }
287  invalidCache();
288  if (isRotating()) {
289  _rotation = rotation * _rotation;
290  } else {
291  _rotation = rotation;
292  }
293  flagRotation();
294 
295  _translation = glm::rotate(rotation, _translation);
296  return *this;
297 }
298 
299 inline Transform& Transform::postRotate(const Quat& rotation) {
300  if (rotation == Quat()) {
301  return *this;
302  }
303  invalidCache();
304 
305  if (isNonUniform()) {
306  Quat newRot;
307  Vec3 newScale;
308  Mat3 scaleRot(glm::mat3_cast(rotation));
309  scaleRot[0] *= _scale;
310  scaleRot[1] *= _scale;
311  scaleRot[2] *= _scale;
312  evalRotationScale(newRot, newScale, scaleRot);
313 
314  if (isRotating()) {
315  _rotation *= newRot;
316  } else {
317  _rotation = newRot;
318  }
319  setScale(newScale);
320  } else {
321  if (isRotating()) {
322  _rotation *= rotation;
323  } else {
324  _rotation = rotation;
325  }
326  }
327  flagRotation();
328  return *this;
329 }
330 
331 inline const Transform::Vec3& Transform::getScale() const {
332  return _scale;
333 }
334 
335 inline Transform& Transform::setScale(float scale) {
336  if (!isValidScale(scale)) {
337  return *this;
338  }
339  invalidCache();
340  flagUniform();
341 
342  if (scale == 1.0f) {
343  unflagScaling();
344  } else {
345  flagScaling();
346  }
347  _scale = Vec3(scale);
348  return *this;
349 }
350 
351 inline Transform& Transform::setScale(const Vec3& scale) {
352  if (!isValidScale(scale)) {
353  return *this;
354  }
355 
356  if ((scale.x == scale.y) && (scale.x == scale.z)) {
357  return setScale(scale.x);
358  }
359 
360  invalidCache();
361  flagScaling();
362  flagNonUniform();
363  _scale = scale;
364  return *this;
365 }
366 
367 inline Transform& Transform::postScale(float scale) {
368  if (!isValidScale(scale) || scale == 1.0f) {
369  return *this;
370  }
371  if (!isScaling()) {
372  return setScale(scale);
373  }
374  // if already scaling, just invalid cache and apply uniform scale
375  invalidCache();
376  _scale *= scale;
377  return *this;
378 }
379 
380 inline Transform& Transform::postScale(const Vec3& scale) {
381  if (!isValidScale(scale)) {
382  return *this;
383  }
384  invalidCache();
385  if ((scale.x != scale.y) || (scale.x != scale.z)) {
386  flagNonUniform();
387  }
388  if (isScaling()) {
389  _scale *= scale;
390  } else {
391  _scale = scale;
392  }
393  flagScaling();
394  return *this;
395 }
396 
397 inline Transform::Mat4 Transform::getMatrix() const {
398  Transform::Mat4 result;
399  getMatrix(result);
400  return result;
401 }
402 
403 inline Transform::Mat4 Transform::getInverseMatrix() const {
404  Transform::Mat4 result;
405  getInverseMatrix(result);
406  return result;
407 }
408 
409 inline Transform::Mat4& Transform::getMatrix(Transform::Mat4& result) const {
410  if (isRotating()) {
411  Mat3 rot = glm::mat3_cast(_rotation);
412 
413  if (isScaling()) {
414  rot[0] *= _scale.x;
415  rot[1] *= _scale.y;
416  rot[2] *= _scale.z;
417  }
418 
419  result[0] = Vec4(rot[0], 0.0f);
420  result[1] = Vec4(rot[1], 0.0f);
421  result[2] = Vec4(rot[2], 0.0f);
422  } else {
423  result[0] = Vec4(_scale.x, 0.0f, 0.0f, 0.0f);
424  result[1] = Vec4(0.0f, _scale.y, 0.0f, 0.0f);
425  result[2] = Vec4(0.0f, 0.0f, _scale.z, 0.0f);
426  }
427 
428  result[3] = Vec4(_translation, 1.0f);
429  return result;
430 }
431 
432 inline Transform::Mat4& Transform::getInverseMatrix(Transform::Mat4& result) const {
433  Transform inverse;
434  evalInverse(inverse);
435  return inverse.getMatrix(result);
436 }
437 
438 inline Transform::Mat4& Transform::getInverseTransposeMatrix(Transform::Mat4& result) const {
439  getInverseMatrix(result);
440  result = glm::transpose(result);
441  return result;
442 }
443 
444 inline Transform::Mat4& Transform::getRotationScaleMatrix(Mat4& result) const {
445  getMatrix(result);
446  result[3] = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
447  return result;
448 }
449 
450 inline Transform::Mat4& Transform::getRotationScaleMatrixInverse(Mat4& result) const {
451  getInverseMatrix(result);
452  result[3] = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
453  return result;
454 }
455 
456 inline Transform& Transform::evalFromRawMatrix(const Mat4& matrix) {
457  // for now works only in the case of TRS transformation
458  if ((matrix[0][3] == 0.0f) && (matrix[1][3] == 0.0f) && (matrix[2][3] == 0.0f) && (matrix[3][3] == 1.0f)) {
459  setTranslation(extractTranslation(matrix));
460  evalFromRawMatrix(Mat3(matrix));
461  }
462  return *this;
463 }
464 
465 inline Transform& Transform::evalFromRawMatrix(const Mat3& rotationScaleMatrix) {
466  Quat rotation;
467  Vec3 scale;
468  evalRotationScale(rotation, scale, rotationScaleMatrix);
469  setRotation(rotation);
470  setScale(scale);
471  return *this;
472 }
473 
474 inline Transform& Transform::evalInverse(Transform& inverse) const {
475  inverse.setIdentity();
476  if (isScaling()) {
477  if (isNonUniform()) {
478  inverse.setScale(Vec3(1.0f) / _scale);
479  } else {
480  inverse.setScale(1.0f / _scale.x);
481  }
482  }
483  if (isRotating()) {
484  inverse.postRotate(glm::conjugate(_rotation));
485  }
486  if (isTranslating()) {
487  inverse.postTranslate(-_translation);
488  }
489  return inverse;
490 }
491 
492 inline Transform& Transform::mult( Transform& result, const Transform& left, const Transform& right) {
493  result = left;
494  if (right.isTranslating()) {
495  result.postTranslate(right.getTranslation());
496  }
497  if (right.isRotating()) {
498  result.postRotate(right.getRotation());
499  }
500  if (right.isScaling()) {
501  result.postScale(right.getScale());
502  }
503 
504  // HACK: In case of an issue in the Transform multiplication results, to make sure this code is
505  // working properly uncomment the next 2 lines and compare the results, they should be the same...
506  // Transform::Mat4 mv = left.getMatrix() * right.getMatrix();
507  // Transform::Mat4 mv2 = result.getMatrix();
508 
509  return result;
510 }
511 
512 inline Transform& Transform::inverseMult( Transform& result, const Transform& left, const Transform& right) {
513  result.setIdentity();
514 
515  if (left.isScaling()) {
516  result.setScale(Vec3(1.0f) / left.getScale());
517  }
518  if (left.isRotating()) {
519  result.postRotate(glm::conjugate(left.getRotation()));
520  }
521  if (left.isTranslating() || right.isTranslating()) {
522  result.postTranslate(right.getTranslation() - left.getTranslation());
523  }
524  if (right.isRotating()) {
525  result.postRotate(right.getRotation());
526  }
527  if (right.isScaling()) {
528  result.postScale(right.getScale());
529  }
530 
531  // HACK: In case of an issue in the Transform multiplication results, to make sure this code is
532  // working properly uncomment the next 2 lines and compare the results, they should be the same...
533  // Transform::Mat4 mv = left.getMatrix() * right.getMatrix();
534  // Transform::Mat4 mv2 = result.getMatrix();
535 
536  return result;
537 }
538 
539 inline Transform::Vec4 Transform::transform(const Vec4& pos) const {
540  Mat4 m;
541  getMatrix(m);
542  return m * pos;
543 }
544 
545 inline Transform::Vec3 Transform::transform(const Vec3& pos) const {
546  Mat4 m;
547  getMatrix(m);
548  Vec4 result = m * Vec4(pos, 1.0f);
549  return Vec3(result.x / result.w, result.y / result.w, result.z / result.w);
550 }
551 
552 inline Transform::Vec3 Transform::transformDirection(const Vec3& dir) const {
553  Mat4 m;
554  getMatrix(m);
555  Vec4 result = m * Vec4(dir, 0.0f);
556  return Vec3(result.x, result.y, result.z);
557 }
558 
559 inline Transform::Mat4& Transform::getCachedMatrix(Transform::Mat4& result) const {
560  updateCache();
561  result = (*_matrix);
562  return result;
563 }
564 
565 inline void Transform::updateCache() const {
566  if (isCacheInvalid()) {
567  if (!_matrix.get()) {
568  _matrix.reset(new Mat4());
569  }
570  getMatrix((*_matrix));
571  validCache();
572  }
573 }
574 
575 #endif
Provides the Mat4 scripting interface.
Definition: Mat4.h:44
Provides the Quat scripting interface.
Definition: Quat.h:61
Provides the Vec3 scripting interface.
Definition: Vec3.h:80