Ocean
Loading...
Searching...
No Matches
SquareMatrix4.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8#ifndef META_OCEAN_MATH_SQUARE_MATRIX_4_H
9#define META_OCEAN_MATH_SQUARE_MATRIX_4_H
10
11#include "ocean/math/Math.h"
12#include "ocean/math/Equation.h"
13#include "ocean/math/Vector2.h"
14#include "ocean/math/Vector3.h"
15#include "ocean/math/Vector4.h"
16
17#include <vector>
18
19namespace Ocean
20{
21
22// Forward declaration.
23template <typename T> class AnyCameraT;
24
25// Forward declaration.
26template <typename T> class HomogenousMatrixT4;
27
28// Forward declaration.
29template <typename T> class SquareMatrixT3;
30
31// Forward declaration.
32template <typename T> class SquareMatrixT4;
33
34/**
35 * Definition of the SquareMatrix4 object, depending on the OCEAN_MATH_USE_SINGLE_PRECISION either with single or double precision float data type.
36 * @see SquareMatrixT4
37 * @ingroup math
38 */
40
41/**
42 * Instantiation of the SquareMatrixT4 template class using a double precision float data type.
43 * @see SquareMatrixT4
44 * @ingroup math
45 */
47
48/**
49 * Instantiation of the SquareMatrixT4 template class using a double precision float data type.
50 * @see SquareMatrixT4
51 * @ingroup math
52 */
54
55/**
56 * Definition of a typename alias for vectors with SquareMatrixT4 objects.
57 * @see SquareMatrixT4
58 * @ingroup math
59 */
60template <typename T>
61using SquareMatricesT4 = std::vector<SquareMatrixT4<T>>;
62
63/**
64 * Definition of a vector holding SquareMatrix4 objects.
65 * @see SquareMatrix4
66 * @ingroup math
67 */
68using SquareMatrices4 = std::vector<SquareMatrix4>;
69
70/**
71 * This class implements a 4x4 square matrix.
72 * The values are stored in a column aligned order with indices:
73 * <pre>
74 * | 0 4 8 12 |
75 * | 1 5 9 13 |
76 * | 2 6 10 14 |
77 * | 3 7 11 15 |
78 * </pre>
79 * @tparam T Data type of matrix elements
80 * @see SquareMatrix4, SquareMatrixF4, SquareMatrixD4.
81 * @ingroup math
82 */
83template <typename T>
85{
86 template <typename U> friend class SquareMatrixT4;
87
88 public:
89
90 /**
91 * Definition of the used data type.
92 */
93 using Type = T;
94
95 public:
96
97 /**
98 * Creates a new SquareMatrixT4 object with undefined elements.
99 * Beware: This matrix is neither a zero nor an entity matrix!
100 */
102
103 /**
104 * Copy constructor.
105 * @param matrix The matrix to copy
106 */
107 SquareMatrixT4(const SquareMatrixT4<T>& matrix) = default;
108
109 /**
110 * Copy constructor for a matrix with difference element data type than T.
111 * @param matrix The matrix to copy
112 * @tparam U The element data type of the second matrix
113 */
114 template <typename U>
115 inline explicit SquareMatrixT4(const SquareMatrixT4<U>& matrix);
116
117 /**
118 * Creates a new SquareMatrixT4 object.
119 * @param setToIdentity Determines whether a entity matrix will be created
120 */
121 explicit SquareMatrixT4(const bool setToIdentity);
122
123 /**
124 * Creates a new SquareMatrixT4 object by an array of at least sixteen elements of float type U.
125 * @param arrayValues The sixteen matrix elements defining the new matrix, must be valid
126 * @tparam U The floating point type of the given elements
127 */
128 template <typename U>
129 explicit SquareMatrixT4(const U* arrayValues);
130
131 /**
132 * Creates a new SquareMatrixT4 object by an array of at least sixteen elements.
133 * @param arrayValues The sixteen matrix elements defining the new matrix, must be valid
134 */
135 explicit SquareMatrixT4(const T* arrayValues);
136
137 /**
138 * Creates a new SquareMatrixT4 object by an array of at least sixteen elements of float type U.
139 * @param arrayValues The sixteen matrix elements defining the new matrix, must be valid
140 * @param valuesRowAligned True, if the given values are stored in a row aligned order; False, if the values are stored in a column aligned order (which is the default case for this matrix)
141 * @tparam U The floating point type of the given elements
142 */
143 template <typename U>
144 SquareMatrixT4(const U* arrayValues, const bool valuesRowAligned);
145
146 /**
147 * Creates a new SquareMatrixT4 object by an array of at least sixteen elements.
148 * @param valuesRowAligned True, if the given values are stored in a row aligned order; False, if the values are stored in a column aligned order (which is the default case for this matrix)
149 * @param arrayValues The sixteen matrix elements defining the new matrix, must be valid
150 */
151 SquareMatrixT4(const T* arrayValues, const bool valuesRowAligned);
152
153 /**
154 * Creates a new SquareMatrixT4 object by given transformation matrix.
155 * @param transformation The transformation matrix to copy
156 */
157 explicit SquareMatrixT4(const HomogenousMatrixT4<T>& transformation);
158
159 /**
160 * Creates a new SquareMatrixT4 object by given 3x3 sub matrix.
161 * The lower and right elements of the 4x4 square matrix are set to zero.
162 * @param subMatrix 3x3 subMatrix defining the upper left elements of the new matrix
163 */
164 explicit SquareMatrixT4(const SquareMatrixT3<T>& subMatrix);
165
166 /**
167 * Creates a new SquareMatrixT4 object by a given diagonal vector.
168 * @param diagonal The diagonal vector for the new matrix
169 */
170 explicit SquareMatrixT4(const VectorT4<T>& diagonal);
171
172 /**
173 * Returns the transposed of this matrix.
174 * @return Transposed matrix
175 */
176 [[nodiscard]] SquareMatrixT4<T> transposed() const;
177
178 /**
179 * Transposes the matrix.
180 */
181 void transpose();
182
183 /**
184 * Returns the inverted matrix of this matrix.
185 * This matrix must not be singular.<br>
186 * Beware: This function does not throw an exception if the matrix cannot be inverted.<br>
187 * Thus, ensure that the matrix is invertible before calling this function.<br>
188 * Even better: avoid the usage of this function and call invert() instead.<br>
189 * In case, this matrix is not invertible, this matrix will be returned instead.
190 * @return The inverted matrix
191 * @see invert(), isSingular().
192 */
193 [[nodiscard]] SquareMatrixT4<T> inverted() const;
194
195 /**
196 * Inverts this matrix in place.
197 * @return True, if the matrix is not singular and could be inverted
198 * @see inverted().
199 */
200 bool invert();
201
202 /**
203 * Inverts the matrix and returns the result.
204 * @param invertedMatrix The resulting inverted matrix
205 * @return True, if the matrix is not singular and could be inverted
206 * @see inverted().
207 */
208 bool invert(SquareMatrixT4<T>& invertedMatrix) const;
209
210 /**
211 * Returns the determinant of the matrix.
212 * @return Matrix determinant
213 */
214 T determinant() const;
215
216 /**
217 * Returns the trace of the matrix which is the sum of the diagonal elements.
218 * @return Trace of the matrix
219 */
220 T trace() const;
221
222 /**
223 * Sets the matrix to the identity matrix.
224 */
225 inline void toIdentity();
226
227 /**
228 * Sets the matrix to a zero matrix.
229 * @see isNull();
230 */
231 inline void toNull();
232
233 /**
234 * Returns whether this matrix is a null matrix.
235 * @return True, if so
236 */
237 bool isNull() const;
238
239 /**
240 * Returns whether this matrix is the identity matrix.
241 * @return True, if so
242 */
243 bool isIdentity() const;
244
245 /**
246 * Returns whether this matrix is singular (and thus cannot be inverted).
247 * A matrix is singular if the determinant of a matrix is 0.<br>
248 * @return True, if so
249 */
250 inline bool isSingular() const;
251
252 /**
253 * Returns whether this matrix is symmetric.
254 * @param epsilon The epsilon threshold to be used, with range [0, infinity)
255 * @return True, if so
256 */
257 bool isSymmetric(const T epsilon = NumericT<T>::eps()) const;
258
259 /**
260 * Returns whether two matrices are almost identical up to a specified epsilon.
261 * @param matrix Second matrix that will be checked
262 * @param eps The epsilon threshold to be used, with range [0, infinity)
263 * @return True, if so
264 */
265 inline bool isEqual(const SquareMatrixT4<T>& matrix, const T eps = NumericT<T>::eps()) const;
266
267 /**
268 * Performs an eigen value analysis.
269 * @param eigenValues The resulting four eigen values, must be valid
270 * @param eigenVectors The resulting four eigen vectors, must be valid
271 * @return True, if succeeded
272 */
273 bool eigenSystem(T* eigenValues, VectorT4<T>* eigenVectors) const;
274
275 /**
276 * Returns a pointer to the internal values.
277 * @return Pointer to the internal values
278 */
279 inline const T* data() const;
280
281 /**
282 * Returns a pointer to the internal values.
283 * @return Pointer to the internal values
284 */
285 inline T* data();
286
287 /**
288 * Copies the elements of this matrix to an array with floating point values of type U.
289 * @param arrayValues Array with 16 floating point values of type U receiving the elements of this matrix
290 * @tparam U Floating point type
291 */
292 template <typename U>
293 void copyElements(U* arrayValues) const;
294
295 /**
296 * Copies the elements of this matrix to an array with floating point values.
297 * @param arrayValues Array with floating point values receiving the elements of this matrix
298 */
299 void copyElements(T* arrayValues) const;
300
301 /**
302 * Default assign operator.
303 * @return Reference to this object
304 */
306
307 /**
308 * Returns whether two matrices are identical up to a small epsilon.
309 * @param matrix Right operand
310 * @return True, if so
311 */
312 bool operator==(const SquareMatrixT4<T>& matrix) const;
313
314 /**
315 * Returns whether two matrices are not identical up to a small epsilon.
316 * @param matrix Right operand
317 * @return True, if so
318 */
319 inline bool operator!=(const SquareMatrixT4<T>& matrix) const;
320
321 /**
322 * Adds two matrices.
323 * @param matrix Right operand
324 * @return Sum matrix
325 */
327
328 /**
329 * Adds and assigns two matrices.
330 * @param matrix Right operand
331 * @return Reference to this object
332 */
334
335 /**
336 * Subtracts two matrices.
337 * @param matrix Right operand
338 * @return Difference matrix
339 */
341
342 /**
343 * Subtracts and assigns two matrices.
344 * @param matrix Right operand
345 * @return Reference to this object
346 */
348
349 /**
350 * Returns the negative matrix of this matrix (all matrix elements are multiplied by -1).
351 * @return Resulting negative matrix
352 */
354
355 /**
356 * Multiplies two matrices.
357 * @param matrix Right operand
358 * @return Product matrix
359 */
360 OCEAN_FORCE_INLINE SquareMatrixT4<T> operator*(const SquareMatrixT4<T>& matrix) const;
361
362 /**
363 * Multiplies two matrices.
364 * @param matrix Right operand
365 * @return Product matrix
366 */
367 OCEAN_FORCE_INLINE SquareMatrixT4<T> operator*(const HomogenousMatrixT4<T>& matrix) const;
368
369 /**
370 * Multiplies and assigns two matrices.
371 * @param matrix Right operand
372 * @return Reference to this object
373 */
374 OCEAN_FORCE_INLINE SquareMatrixT4<T>& operator*=(const SquareMatrixT4<T>& matrix);
375
376 /**
377 * Multiplies and assigns two matrices.
378 * @param matrix Right operand
379 * @return Reference to this object
380 */
381 OCEAN_FORCE_INLINE SquareMatrixT4<T>& operator*=(const HomogenousMatrixT4<T>& matrix);
382
383 /**
384 * Multiply operator for a 3D vector.
385 * The 3D vector is interpreted as a 4D vector with fourth component equal to 1.<br>
386 * The final result will be de-homogenizated to provide a 3D vector result.<br>
387 * Beware the dot product of the last row with the vector must not be zero!
388 * @param vector Right operand
389 * @return Resulting 3D vector
390 */
391 OCEAN_FORCE_INLINE VectorT3<T> operator*(const VectorT3<T>& vector) const;
392
393 /**
394 * Multiply operator for a 4D vector.
395 * @param vector Right operand
396 * @return Resulting 4D vector
397 */
398 OCEAN_FORCE_INLINE VectorT4<T> operator*(const VectorT4<T>& vector) const;
399
400 /**
401 * Multiplies this matrix with a scalar value.
402 * @param value Right operand
403 * @return Resulting matrix
404 */
405 SquareMatrixT4<T> operator*(const T value) const;
406
407 /**
408 * Multiplies and assigns this matrix with a scalar value.
409 * @param value right operand
410 * @return Reference to this object
411 */
413
414 /**
415 * Element operator.
416 * Beware: No range check will be done!
417 * @param index The index of the element to return [0, 15]
418 * @return Specified element
419 */
420 inline T operator[](const unsigned int index) const;
421
422 /**
423 * Element operator.
424 * Beware: No range check will be done!
425 * @param index The index of the element to return [0, 15]
426 * @return Specified element
427 */
428 inline T& operator[](const unsigned int index);
429
430 /**
431 * Element operator.
432 * Beware: No range check will be done!
433 * @param row The row of the element to return [0, 3]
434 * @param column The column of the element to return [0, 3]
435 * @return Specified element
436 */
437 inline T operator()(const unsigned int row, const unsigned int column) const;
438
439 /**
440 * Element operator.
441 * Beware: No range check will be done!
442 * @param row The row of the element to return [0, 3]
443 * @param column The column of the element to return [0, 3]
444 * @return Specified element
445 */
446 inline T& operator()(const unsigned int row, const unsigned int column);
447
448 /**
449 * Element operator.
450 * Beware: No range check will be done!
451 * @param index The index of the element to return [0, 15]
452 * @return Specified element
453 */
454 inline T operator()(const unsigned int index) const;
455
456 /**
457 * Element operator.
458 * Beware: No range check will be done!
459 * @param index The index of the element to return [0, 15]
460 * @return Specified element
461 */
462 inline T& operator()(const unsigned int index);
463
464 /**
465 * Access operator.
466 * @return Pointer to the internal values
467 */
468 inline const T* operator()() const;
469
470 /**
471 * Access operator.
472 * @return Pointer to the internal values
473 */
474 inline T* operator()();
475
476 /**
477 * Hash function.
478 * @param matrix The matrix for which the hash value will be determined
479 * @return The resulting hash value
480 */
481 inline size_t operator()(const SquareMatrixT4<T>& matrix) const;
482
483 /**
484 * Returns the number of elements this matrix has.
485 * @return The number of elements, always 16
486 */
487 static inline size_t elements();
488
489 /**
490 * Multiplies several 4D vectors with a given matrix.
491 * @param matrix The matrix to be used for multiplication, may be nullptr if number is 0
492 * @param vectors The input vectors that will be multiplied, may be nullptr if number is 0
493 * @param results The resulting output (multiplied/transformed) vectors, with same number as the provided input vectors
494 * @param number The number of provided vectors (input and output), with range [0, infinity)
495 */
496 static void multiply(const SquareMatrixT4<T>& matrix, const VectorT4<T>* vectors, VectorT4<T>* results, const size_t number);
497
498 /**
499 * Creates a projection matrix defined by the horizontal field of view, the aspect ratio and the near and far clipping plane.
500 * @param fovX Horizontal field of view, in radian and a range of [0, Pi]
501 * @param aspectRatio View aspect ratio which is width divided by the height of the projection window
502 * @param nearDistance Positive distance to the near clipping plane
503 * @param farDistance Positive distance to the far clipping lane
504 * @return Specified projection matrix
505 */
506 static SquareMatrixT4<T> projectionMatrix(const T fovX, const T aspectRatio, const T nearDistance, const T farDistance);
507
508 /**
509 * Creates a projection matrix defined by a camera profile of a pinhole camera and the near and far clipping plane.
510 * @param anyCamera The camera profile of a pinhole camera, without distortion parameters, must be valid
511 * @param nearDistance Positive distance to the near clipping plane
512 * @param farDistance Positive distance to the far clipping lane
513 * @return Specified projection matrix
514 */
515 static SquareMatrixT4<T> projectionMatrix(const AnyCameraT<T>& anyCamera, const T nearDistance, const T farDistance);
516
517 /**
518 * Creates a projection matrix defined by an asymmetric viewing frustum.
519 * The shape of the frustum is defined by the rectangle on the near plane.<br>
520 * Afterwards, the field of view is defined by the (positive) distance to the near clipping plane.<br>
521 * Followed by the (positive) far clipping plane to determine the entire frustum.
522 * @param left Position of the left border of the rectangle on the near plane
523 * @param right Position of the right border of the rectangle on the near plane
524 * @param top Position of the top border of the rectangle on the near plane
525 * @param bottom Position of the bottom border of the rectangle on the near plane
526 * @param nearDistance Positive distance to the near clipping plane
527 * @param farDistance Positive distance to the far clipping plane
528 * @return Specified frustum projection matrix
529 */
530 static SquareMatrixT4<T> frustumMatrix(const T left, const T right, const T top, const T bottom, const T nearDistance, const T farDistance);
531
532 /**
533 * Creates a project matrix defined by an asymmetric viewing frustum.
534 * The shape of the frustum is defined by the rectangle on the near plane.<br>
535 * The viewing position is defined by the given view matrix while the near plane is expected to lie in the origin of the coordinate system.
536 * @param width The width of the near plane, with range (0, infinity)
537 * @param height The height of the near plane, with range (0, infinity)
538 * @param viewingMatrix Viewing matrix transforming point defined in the camera coordinate system into points defined in the world coordinate system, must be invertible
539 * @param nearDistance Positive distance to the near clipping plane, with range (0, infinity)
540 * @param farDistance Positive distance to the far clipping plane, with range (nearDistance, infinity)
541 * @return Specified frustum projection matrix
542 */
543 static SquareMatrixT4<T> frustumMatrix(const T width, const T height, const HomogenousMatrixT4<T>& viewingMatrix, const T nearDistance, const T farDistance);
544
545 /**
546 * Converts matrices with specific data type to matrices with different data type.
547 * @param matrices The matrices to convert
548 * @return The converted matrices
549 * @tparam U The element data type of the matrices to convert
550 */
551 template <typename U>
552 static inline std::vector< SquareMatrixT4<T> > matrices2matrices(const std::vector< SquareMatrixT4<U> >& matrices);
553
554 /**
555 * Converts matrices with specific data type to matrices with different data type.
556 * @param matrices The matrices to convert
557 * @param size The number of matrices to convert
558 * @return The converted matrices
559 * @tparam U The element data type of the matrices to convert
560 */
561 template <typename U>
562 static inline std::vector< SquareMatrixT4<T> > matrices2matrices(const SquareMatrixT4<U>* matrices, const size_t size);
563
564 protected:
565
566 /**
567 * Swaps two rows of this matrix.
568 * @param row0 The index of the first row ,with range [0, 3]
569 * @param row1 The index of the second row, with range [0, 3]
570 */
571 void swapRows(const unsigned int row0, const unsigned int row1);
572
573 /**
574 * Multiplies a row with a scalar value.
575 * @param row The index of the row to multiply, with range [0, 3]
576 * @param scalar The scalar to multiply
577 */
578 void multiplyRow(const unsigned int row, const T scalar);
579
580 /**
581 * Multiplies elements from a specific row with a scalar and adds them to another row.
582 * @param targetRow The index of the target row, with range [0, 3]
583 * @param sourceRow The index of the source row, with range [0, 3]
584 * @param scalar The scalar to multiply the source elements with
585 */
586 void addRows(const unsigned int targetRow, const unsigned int sourceRow, const T scalar);
587
588 protected:
589
590 /// The sixteen values of the matrix.
591 T values[16];
592};
593
594template <typename T>
596{
597 // nothing to do here
598}
599
600template <typename T>
601template <typename U>
603{
604 for (unsigned int n = 0u; n < 16u; ++n)
605 {
606 values[n] = T(matrix.values[n]);
607 }
608}
609
610template <typename T>
611SquareMatrixT4<T>::SquareMatrixT4(const bool setToIdentity)
612{
613 if (setToIdentity)
614 {
615 toIdentity();
616 }
617 else
618 {
619 toNull();
620 }
621}
622
623template <typename T>
624template <typename U>
626{
627 ocean_assert(arrayValues);
628
629 for (unsigned int n = 0u; n < 16u; ++n)
630 {
631 values[n] = T(arrayValues[n]);
632 }
633}
634
635template <typename T>
637{
638 ocean_assert(arrayValues);
639 memcpy(values, arrayValues, sizeof(T) * 16);
640}
641
642template <typename T>
643template <typename U>
644SquareMatrixT4<T>::SquareMatrixT4(const U* arrayValues, const bool valuesRowAligned)
645{
646 ocean_assert(arrayValues);
647
648 if (valuesRowAligned)
649 {
650 values[ 0] = T(arrayValues[ 0]);
651 values[ 1] = T(arrayValues[ 4]);
652 values[ 2] = T(arrayValues[ 8]);
653 values[ 3] = T(arrayValues[12]);
654 values[ 4] = T(arrayValues[ 1]);
655 values[ 5] = T(arrayValues[ 5]);
656 values[ 6] = T(arrayValues[ 9]);
657 values[ 7] = T(arrayValues[13]);
658 values[ 8] = T(arrayValues[ 2]);
659 values[ 9] = T(arrayValues[ 6]);
660 values[10] = T(arrayValues[10]);
661 values[11] = T(arrayValues[14]);
662 values[12] = T(arrayValues[ 3]);
663 values[13] = T(arrayValues[ 7]);
664 values[14] = T(arrayValues[11]);
665 values[15] = T(arrayValues[15]);
666 }
667 else
668 {
669 for (unsigned int n = 0u; n < 16u; ++n)
670 {
671 values[n] = T(arrayValues[n]);
672 }
673 }
674}
675
676template <typename T>
677SquareMatrixT4<T>::SquareMatrixT4(const T* arrayValues, const bool valuesRowAligned)
678{
679 ocean_assert(arrayValues);
680
681 if (valuesRowAligned)
682 {
683 values[ 0] = arrayValues[ 0];
684 values[ 1] = arrayValues[ 4];
685 values[ 2] = arrayValues[ 8];
686 values[ 3] = arrayValues[12];
687 values[ 4] = arrayValues[ 1];
688 values[ 5] = arrayValues[ 5];
689 values[ 6] = arrayValues[ 9];
690 values[ 7] = arrayValues[13];
691 values[ 8] = arrayValues[ 2];
692 values[ 9] = arrayValues[ 6];
693 values[10] = arrayValues[10];
694 values[11] = arrayValues[14];
695 values[12] = arrayValues[ 3];
696 values[13] = arrayValues[ 7];
697 values[14] = arrayValues[11];
698 values[15] = arrayValues[15];
699 }
700 else
701 {
702 memcpy(values, arrayValues, sizeof(T) * 16);
703 }
704}
705
706template <typename T>
708{
709 memcpy(values, transformation(), sizeof(T) * 16);
710}
711
712template <typename T>
714{
715 values[ 3] = T(0.0);
716 values[ 7] = T(0.0);
717 values[11] = T(0.0);
718 values[12] = T(0.0);
719 values[13] = T(0.0);
720 values[14] = T(0.0);
721 values[15] = T(0.0);
722
723 memcpy(values, subMatrix(), sizeof(T) * 3); // Set values[0] - values[2]
724 memcpy(values + 4, subMatrix() + 3, sizeof(T) * 3); // Set values[4] - values[6]
725 memcpy(values + 8, subMatrix() + 6, sizeof(T) * 3); // Set values[8] - values[10]
726}
727
728template <typename T>
730{
731 values[ 0] = diagonal[0];
732 values[ 1] = T(0.0);
733 values[ 2] = T(0.0);
734 values[ 3] = T(0.0);
735 values[ 4] = T(0.0);
736 values[ 5] = diagonal[1];
737 values[ 6] = T(0.0);
738 values[ 7] = T(0.0);
739 values[ 8] = T(0.0);
740 values[ 9] = T(0.0);
741 values[10] = diagonal[2];
742 values[11] = T(0.0);
743 values[12] = T(0.0);
744 values[13] = T(0.0);
745 values[14] = T(0.0);
746 values[15] = diagonal[3];
747}
748
749template <typename T>
750inline const T* SquareMatrixT4<T>::data() const
751{
752 return values;
753}
754
755template <typename T>
757{
758 return values;
759}
760
761template <typename T>
763{
764 SquareMatrixT4<T> result(*this);
765
766 result.values[1] = values[4];
767 result.values[4] = values[1];
768
769 result.values[2] = values[8];
770 result.values[8] = values[2];
771
772 result.values[3] = values[12];
773 result.values[12] = values[3];
774
775 result.values[6] = values[9];
776 result.values[9] = values[6];
777
778 result.values[7] = values[13];
779 result.values[13] = values[7];
780
781 result.values[11] = values[14];
782 result.values[14] = values[11];
783
784 return result;
785}
786
787template <typename T>
789{
790 SquareMatrixT4<T> tmp(*this);
791
792 values[4] = tmp.values[1];
793 values[1] = tmp.values[4];
794
795 values[8] = tmp.values[2];
796 values[2] = tmp.values[8];
797
798 values[12] = tmp.values[3];
799 values[3] = tmp.values[12];
800
801 values[9] = tmp.values[6];
802 values[6] = tmp.values[9];
803
804 values[13] = tmp.values[7];
805 values[7] = tmp.values[13];
806
807 values[14] = tmp.values[11];
808 values[11] = tmp.values[14];
809}
810
811template <typename T>
813{
814 SquareMatrixT4 invertedMatrix;
815
816 if (!invert(invertedMatrix))
817 {
818 ocean_assert(false && "Could not invert matrix.");
819 return *this;
820 }
821
822 return invertedMatrix;
823}
824
825template <typename T>
827{
828 SquareMatrixT4<T> invertedMatrix;
829
830 if (!invert(invertedMatrix))
831 {
832 return false;
833 }
834
835 *this = invertedMatrix;
836
837 return true;
838}
839
840template <typename T>
842{
843 // implements the Gauss-Jordon Elimination
844
845 SquareMatrixT4<T> source(*this);
846 invertedMatrix.toIdentity();
847
848 for (unsigned int col = 0; col < 4u; ++col)
849 {
850 // find largest absolute value in the col-th column,
851 // to remove zeros from the main diagonal and to provide numerical stability
852 T absolute = 0.0;
853 unsigned int selectedRow = 0u;
854
855 for (unsigned int row = col; row < 4u; ++row)
856 {
857 const T value = NumericT<T>::abs(source(row, col));
858 if (absolute < value)
859 {
860 absolute = value;
861 selectedRow = row;
862 }
863 }
864
865 // if there was no greater absolute value than 0 this matrix is singular
866
867 if (NumericT<T>::isEqualEps(absolute))
868 {
869 return false;
870 }
871
872 // exchange the two rows
873 if (selectedRow != col)
874 {
875 source.swapRows(col, selectedRow);
876 invertedMatrix.swapRows(col, selectedRow);
877 }
878
879 // now the element at (col, col) will be 1.0
880 if (NumericT<T>::isNotEqual(source(col, col), T(1.0)))
881 {
882 const T divisor = T(1.0) / source(col, col);
883 ocean_assert(divisor != T(0.0));
884
885 source.multiplyRow(col, divisor);
886 invertedMatrix.multiplyRow(col, divisor);
887 }
888
889 // clear each entry above and below the selected row and column to zero
890 for (unsigned int row = 0; row < 4u; ++row)
891 {
892 if (row != col)
893 {
894 const T value = -source(row, col);
895
896 source.addRows(row, col, value);
897 invertedMatrix.addRows(row, col, value);
898 }
899 }
900 }
901
902 return true;
903}
904
905template <typename T>
907{
908 const T v6_15 = values[6] * values[15];
909 const T v10_15 = values[10] * values[15];
910 const T v11_14 = values[11] * values[14];
911 const T v7_10 = values[7] * values[10];
912 const T v9_14 = values[9] * values[14];
913 const T v6_13 = values[6] * values[13];
914 const T v2_13 = values[2] * values[13];
915 const T v2_9 = values[2] * values[9];
916 const T v3_10 = values[3] * values[10];
917 const T v2_5 = values[2] * values[5];
918
919 return values[0] * (values[5] * v10_15 - values[13] * v7_10
920 + v9_14 * values[7] - values[5] * v11_14
921 + v6_13 * values[11] - values[9] * v6_15)
922 - values[4] * (v9_14 * values[3] - values[1] * v11_14
923 + v2_13 * values[11] - v2_9 * values[15]
924 + values[1] * v10_15 - values[13] * v3_10)
925 + values[8] * (values[1] * v6_15 - v6_13 * values[3]
926 + values[5] * values[14] * values[3] - values[1] * values[14] * values[7]
927 + v2_13 * values[7] - v2_5 * values[15])
928 - values[12] * (values[1] * values[6] * values[11] - values[9] * values[6] * values[3]
929 + values[5] * v3_10 - values[1] * v7_10
930 + v2_9 * values[7] - v2_5 * values[11]);
931}
932
933template <typename T>
935{
936 return values[0] + values[5] + values[10] + values[15];
937}
938
939template <typename T>
941{
942 values[ 0] = T(1.0);
943 values[ 1] = T(0.0);
944 values[ 2] = T(0.0);
945 values[ 3] = T(0.0);
946 values[ 4] = T(0.0);
947 values[ 5] = T(1.0);
948 values[ 6] = T(0.0);
949 values[ 7] = T(0.0);
950 values[ 8] = T(0.0);
951 values[ 9] = T(0.0);
952 values[10] = T(1.0);
953 values[11] = T(0.0);
954 values[12] = T(0.0);
955 values[13] = T(0.0);
956 values[14] = T(0.0);
957 values[15] = T(1.0);
958}
959
960template <typename T>
962{
963 for (unsigned int n = 0u; n < 16u; ++n)
964 {
965 values[n] = T(0.0);
966 }
967}
968
969template <typename T>
971{
972 return NumericT<T>::isEqual(values[0], 1) && NumericT<T>::isEqualEps(values[1]) && NumericT<T>::isEqualEps(values[2]) && NumericT<T>::isEqualEps(values[3])
973 && NumericT<T>::isEqualEps(values[4]) && NumericT<T>::isEqual(values[5], 1) && NumericT<T>::isEqualEps(values[6]) && NumericT<T>::isEqualEps(values[7])
974 && NumericT<T>::isEqualEps(values[8]) && NumericT<T>::isEqualEps(values[9]) && NumericT<T>::isEqual(values[10], 1) && NumericT<T>::isEqualEps(values[11])
975 && NumericT<T>::isEqualEps(values[12]) && NumericT<T>::isEqualEps(values[13]) && NumericT<T>::isEqualEps(values[14]) && NumericT<T>::isEqual(values[15], 1);
976}
977
978template <typename T>
980{
981 return NumericT<T>::isEqualEps(values[0]) && NumericT<T>::isEqualEps(values[1]) && NumericT<T>::isEqualEps(values[2]) && NumericT<T>::isEqualEps(values[3])
982 && NumericT<T>::isEqualEps(values[4]) && NumericT<T>::isEqualEps(values[5]) && NumericT<T>::isEqualEps(values[6]) && NumericT<T>::isEqualEps(values[7])
983 && NumericT<T>::isEqualEps(values[8]) && NumericT<T>::isEqualEps(values[9]) && NumericT<T>::isEqualEps(values[10]) && NumericT<T>::isEqualEps(values[11])
984 && NumericT<T>::isEqualEps(values[12]) && NumericT<T>::isEqualEps(values[13]) && NumericT<T>::isEqualEps(values[14]) && NumericT<T>::isEqualEps(values[15]);
985}
986
987template <typename T>
989{
990 return NumericT<T>::isEqualEps(determinant());
991}
992
993template <typename T>
994bool SquareMatrixT4<T>::isSymmetric(const T epsilon) const
995{
996 ocean_assert(epsilon >= T(0));
997
998 return NumericT<T>::isEqual(values[1], values[4], epsilon) && NumericT<T>::isEqual(values[2], values[8], epsilon) && NumericT<T>::isEqual(values[3], values[12], epsilon)
999 && NumericT<T>::isEqual(values[6], values[9], epsilon) && NumericT<T>::isEqual(values[7], values[13], epsilon) && NumericT<T>::isEqual(values[11], values[14], epsilon);
1000}
1001
1002template <typename T>
1003inline bool SquareMatrixT4<T>::isEqual(const SquareMatrixT4<T>& matrix, const T eps) const
1004{
1005 return NumericT<T>::isEqual(values[0], matrix.values[0], eps) && NumericT<T>::isEqual(values[1], matrix.values[1], eps)
1006 && NumericT<T>::isEqual(values[2], matrix.values[2], eps) && NumericT<T>::isEqual(values[3], matrix.values[3], eps)
1007 && NumericT<T>::isEqual(values[4], matrix.values[4], eps) && NumericT<T>::isEqual(values[5], matrix.values[5], eps)
1008 && NumericT<T>::isEqual(values[6], matrix.values[6], eps) && NumericT<T>::isEqual(values[7], matrix.values[7], eps)
1009 && NumericT<T>::isEqual(values[8], matrix.values[8], eps) && NumericT<T>::isEqual(values[9], matrix.values[9], eps)
1010 && NumericT<T>::isEqual(values[10], matrix.values[10], eps) && NumericT<T>::isEqual(values[11], matrix.values[11], eps)
1011 && NumericT<T>::isEqual(values[12], matrix.values[12], eps) && NumericT<T>::isEqual(values[13], matrix.values[13], eps)
1012 && NumericT<T>::isEqual(values[14], matrix.values[14], eps) && NumericT<T>::isEqual(values[15], matrix.values[15], eps);
1013}
1014
1015template <typename T>
1016bool SquareMatrixT4<T>::eigenSystem(T* eigenValues, VectorT4<T>* eigenVectors) const
1017{
1018 ocean_assert(eigenValues != nullptr && eigenVectors != nullptr);
1019
1020 /**
1021 * <pre>
1022 * Computation of the characteristic polynomial
1023 *
1024 * [ a b c d ]
1025 * A = [ e f g h ]
1026 * [ i j k l ]
1027 * [ m n o p ]
1028 *
1029 * [ a-x b c d ]
1030 * A - x * E = [ e f-x g h ]
1031 * [ i j k-x l ]
1032 * [ m n o p-x ]
1033 *
1034 * Polynomial = Det|A - x * E| = 0
1035 * = x^4 + (-a - f - k - p)x^3 + (-be + af - ci - gj + ak + fk - dm - hn - lo + ap + fp + kp)x^2
1036 * + (cfi - bgi - cej + agj + bek - afk + dfm - bhm + dkm - clm - den + ahn + hkn - gln - dio - hjo + alo + flo + bep - afp + cip + gjp - akp - fkp)x
1037 * + (dgjm - chjm - dfkm + bhkm + cflm - bglm)
1038 * + (-dgin + chin + dekn - ahkn - celn + agln)
1039 * + (dfio - bhio - dejo + ahjo + belo - aflo)
1040 * + (-cfip + bgip + cejp - agjp - bekp + afkp)
1041 * = a1x^4 + a2x^3 + a3x^2 + a4x + a5 = 0
1042 * </pre>
1043 */
1044
1045 const T a = values[0];
1046 const T e = values[1];
1047 const T i = values[2];
1048 const T m = values[3];
1049
1050 const T b = values[4];
1051 const T f = values[5];
1052 const T j = values[6];
1053 const T n = values[7];
1054
1055 const T c = values[8];
1056 const T g = values[9];
1057 const T k = values[10];
1058 const T o = values[11];
1059
1060 const T d = values[12];
1061 const T h = values[13];
1062 const T l = values[14];
1063 const T p = values[15];
1064
1065 const T a1 = 1.0;
1066 const T a2 = -a - f - k - p;
1067 const T a3 = -b * e + a * f - c * i - g * j + a * k + f * k - d * m - h * n - l * o + a * p + f * p + k * p;
1068 const T a4 = c * f * i - b * g * i - c * e * j + a * g * j + b * e * k - a * f * k + d * f * m - b * h * m
1069 + d * k * m - c * l * m - d * e * n + a * h * n + h * k * n - g * l * n - d * i * o - h * j * o
1070 + a * l * o + f * l * o + b * e * p - a * f * p + c * i * p + g * j * p - a * k * p - f * k * p;
1071 const T a5 = d * g * j * m - c * h * j * m - d * f * k * m + b * h * k * m + c * f * l * m - b * g * l * m
1072 - d * g * i * n + c * h * i * n + d * e * k * n - a * h * k * n - c * e * l * n + a * g * l * n
1073 + d * f * i * o - b * h * i * o - d * e * j * o + a * h * j * o + b * e * l * o - a * f * l * o
1074 - c * f * i * p + b * g * i * p + c * e * j * p - a * g * j * p - b * e * k * p + a * f * k * p;
1075
1076 // Solve the quartic equation to find eigenvalues
1077
1078 double x[4];
1079 const unsigned int solutions = EquationT<double>::solveQuartic(double(a1), double(a2), double(a3), double(a4), double(a5), x);
1080
1081 if (solutions != 4u)
1082 {
1083 return false;
1084 }
1085
1086 for (unsigned int iEigen = 0u; iEigen < solutions && iEigen < 4u; ++iEigen)
1087 {
1088 eigenValues[iEigen] = T(x[iEigen]);
1089 }
1090
1091 for (unsigned int iEigen = solutions; iEigen < 4u; ++iEigen)
1092 {
1093 eigenValues[iEigen] = T(0);
1094 }
1095
1096 Utilities::sortHighestToFront4(eigenValues[0], eigenValues[1], eigenValues[2], eigenValues[3]);
1097
1098 /**
1099 * <pre>
1100 * Determination of the eigen vectors (vx, vy, vz, vw):
1101 * [ a-x b c d ] [ vx ]
1102 * A - x * I = [ e f-x g h ] * [ vy ] = 0
1103 * [ i j k-x l ] [ vz ]
1104 * [ m n o p-x ] [ vw ]
1105 * </pre>
1106 */
1107
1108 for (unsigned int nSolution = 0u; nSolution < solutions; ++nSolution)
1109 {
1110 const T lambda = eigenValues[nSolution];
1111
1112 // create matrix (A - x*I) as rows
1113
1114 VectorT4<T> rows[4];
1115 rows[0] = VectorT4<T>(a - lambda, b, c, d);
1116 rows[1] = VectorT4<T>(e, f - lambda, g, h);
1117 rows[2] = VectorT4<T>(i, j, k - lambda, l);
1118 rows[3] = VectorT4<T>(m, n, o, p - lambda);
1119
1120 // Find eigenvector by solving (A - lambda*I) * v = 0
1121 // We'll try setting each component to 1 and solving for the others
1122
1123 VectorT4<T> eigenvector(T(0), T(0), T(0), T(0));
1124 T bestResidual = NumericT<T>::maxValue();
1125
1126 for (unsigned int freeIndex = 0u; freeIndex < 4u; ++freeIndex)
1127 {
1128 // Try setting component freeIndex to 1
1129 VectorT4<T> candidate(T(0), T(0), T(0), T(0));
1130 candidate[freeIndex] = T(1);
1131
1132 // Find the 3 rows with largest coefficients at freeIndex position
1133 // (these will give us the most stable equations)
1134 unsigned int rowIndices[4] = {0, 1, 2, 3};
1135 T rowScores[4];
1136
1137 for (unsigned int rowNum = 0u; rowNum < 4u; ++rowNum)
1138 {
1139 rowScores[rowNum] = NumericT<T>::abs(rows[rowNum][freeIndex]);
1140 }
1141
1142 // Sort rows by score (descending)
1143 for (unsigned int row = 0u; row < 3u; ++row)
1144 {
1145 for (unsigned int row2 = row + 1u; row2 < 4u; ++row2)
1146 {
1147 if (rowScores[row2] > rowScores[row])
1148 {
1149 std::swap(rowScores[row], rowScores[row2]);
1150 std::swap(rowIndices[row], rowIndices[row2]);
1151 }
1152 }
1153 }
1154
1155 // Build 3x3 system from the 3 best rows
1156 // System: coeffMatrix * unknowns = rhs
1157 T coeffMatrix[3][3];
1158 T rhs[3];
1159 unsigned int unknownIndices[3];
1160 unsigned int idx = 0u;
1161
1162 for (unsigned int col = 0u; col < 4u; ++col)
1163 {
1164 if (col != freeIndex)
1165 {
1166 unknownIndices[idx++] = col;
1167 }
1168 }
1169
1170 for (unsigned int rowIdx = 0u; rowIdx < 3u; ++rowIdx)
1171 {
1172 const VectorT4<T>& row = rows[rowIndices[rowIdx]];
1173 rhs[rowIdx] = -row[freeIndex];
1174
1175 for (unsigned int colIdx = 0u; colIdx < 3u; ++colIdx)
1176 {
1177 coeffMatrix[rowIdx][colIdx] = row[unknownIndices[colIdx]];
1178 }
1179 }
1180
1181 // Solve 3x3 system using Gaussian elimination
1182 T augmented[3][4];
1183 T rowScales[3];
1184
1185 for (unsigned int rowIdx = 0u; rowIdx < 3u; ++rowIdx)
1186 {
1187 // Copy coefficients
1188 for (unsigned int colIdx = 0u; colIdx < 3u; ++colIdx)
1189 {
1190 augmented[rowIdx][colIdx] = coeffMatrix[rowIdx][colIdx];
1191 }
1192 augmented[rowIdx][3] = rhs[rowIdx];
1193
1194 // Compute row scale for better numerical conditioning
1195 T maxRowVal = T(0);
1196 for (unsigned int colIdx = 0u; colIdx < 4u; ++colIdx)
1197 {
1198 maxRowVal = std::max(maxRowVal, NumericT<T>::abs(augmented[rowIdx][colIdx]));
1199 }
1200
1201 rowScales[rowIdx] = maxRowVal;
1202
1203 // Scale the row if possible (improves conditioning)
1204 if (maxRowVal > NumericT<T>::weakEps())
1205 {
1206 const T scale = T(1) / maxRowVal;
1207 for (unsigned int colIdx = 0u; colIdx < 4u; ++colIdx)
1208 {
1209 augmented[rowIdx][colIdx] *= scale;
1210 }
1211 }
1212 }
1213
1214 // Forward elimination with partial pivoting
1215 for (unsigned int pivot = 0u; pivot < 3u; ++pivot)
1216 {
1217 // Find row with largest pivot element
1218 unsigned int maxRow = pivot;
1219 T maxVal = NumericT<T>::abs(augmented[pivot][pivot]);
1220
1221 for (unsigned int rowIdx = pivot + 1u; rowIdx < 3u; ++rowIdx)
1222 {
1223 const T val = NumericT<T>::abs(augmented[rowIdx][pivot]);
1224 if (val > maxVal)
1225 {
1226 maxVal = val;
1227 maxRow = rowIdx;
1228 }
1229 }
1230
1231 // Swap rows if needed
1232 if (maxRow != pivot)
1233 {
1234 for (unsigned int colIdx = 0u; colIdx < 4u; ++colIdx)
1235 {
1236 std::swap(augmented[pivot][colIdx], augmented[maxRow][colIdx]);
1237 }
1238 }
1239
1240 // Use generous threshold (system may be near-singular as we're finding null space)
1241 const T pivotThreshold = std::is_same<T, float>::value ? (NumericT<T>::weakEps() * T(10)) : NumericT<T>::weakEps();
1242
1243 // Skip elimination if pivot is too small, but continue (system is expected to be singular)
1244 if (NumericT<T>::abs(augmented[pivot][pivot]) < pivotThreshold)
1245 {
1246 continue;
1247 }
1248
1249 // Eliminate below pivot
1250 for (unsigned int rowIdx = pivot + 1u; rowIdx < 3u; ++rowIdx)
1251 {
1252 const T factor = augmented[rowIdx][pivot] / augmented[pivot][pivot];
1253 for (unsigned int colIdx = pivot; colIdx < 4u; ++colIdx)
1254 {
1255 augmented[rowIdx][colIdx] -= factor * augmented[pivot][colIdx];
1256 }
1257 }
1258 }
1259
1260 // Back substitution
1261 T solution[3] = {T(0), T(0), T(0)};
1262
1263 for (int rowIdx = 2; rowIdx >= 0; --rowIdx)
1264 {
1265 T sum = augmented[rowIdx][3];
1266
1267 for (unsigned int colIdx = rowIdx + 1u; colIdx < 3u; ++colIdx)
1268 {
1269 sum -= augmented[rowIdx][colIdx] * solution[colIdx];
1270 }
1271
1272 const T pivotThreshold = std::is_same<T, float>::value ? (NumericT<T>::weakEps() * T(10)) : NumericT<T>::weakEps();
1273 if (NumericT<T>::abs(augmented[rowIdx][rowIdx]) > pivotThreshold)
1274 {
1275 solution[rowIdx] = sum / augmented[rowIdx][rowIdx];
1276 }
1277 // else: leave solution[rowIdx] as 0 (free variable in under-determined system)
1278 }
1279
1280 // Build candidate eigenvector
1281 for (unsigned int idx2 = 0u; idx2 < 3u; ++idx2)
1282 {
1283 candidate[unknownIndices[idx2]] = solution[idx2];
1284 }
1285
1286 // Evaluate residual: ||A*v - lambda*v||
1287 const VectorT4<T> Av = (*this) * candidate;
1288 const VectorT4<T> lambdaV = candidate * lambda;
1289 const T residual = (Av - lambdaV).length();
1290
1291 if (residual < bestResidual)
1292 {
1293 bestResidual = residual;
1294 eigenvector = candidate;
1295 }
1296 }
1297
1298 // Normalize the eigenvector
1299 if (!eigenvector.normalize())
1300 {
1301 // Normalization failed - use standard basis vector
1302 eigenvector = VectorT4<T>(T(0), T(0), T(0), T(0));
1303 eigenvector[nSolution % 4u] = T(1);
1304 }
1305
1306 eigenVectors[nSolution] = eigenvector;
1307 }
1308
1309 return true;
1310}
1311
1312template <typename T>
1313template <typename U>
1314void SquareMatrixT4<T>::copyElements(U* arrayValues) const
1315{
1316 ocean_assert(arrayValues != nullptr);
1317
1318 for (unsigned int n = 0u; n < 16u; ++n)
1319 {
1320 arrayValues[n] = U(values[n]);
1321 }
1322}
1323
1324template <typename T>
1325void SquareMatrixT4<T>::copyElements(T* arrayValues) const
1326{
1327 ocean_assert(arrayValues != nullptr);
1328
1329 memcpy(arrayValues, values, sizeof(T) * 16);
1330}
1331
1332template <typename T>
1333inline bool SquareMatrixT4<T>::operator!=(const SquareMatrixT4<T>& matrix) const
1334{
1335 return !(*this == matrix);
1336}
1337
1338template <typename T>
1340{
1341 *this = *this * matrix;
1342 return *this;
1343}
1344
1345template <typename T>
1347{
1348 *this = *this * matrix;
1349 return *this;
1350}
1351
1352template <typename T>
1353inline T SquareMatrixT4<T>::operator[](const unsigned int index) const
1354{
1355 ocean_assert(index < 16u);
1356 return values[index];
1357}
1358
1359template <typename T>
1360inline T& SquareMatrixT4<T>::operator[](const unsigned int index)
1361{
1362 ocean_assert(index < 16u);
1363 return values[index];
1364}
1365
1366template <typename T>
1367inline T SquareMatrixT4<T>::operator()(const unsigned int row, const unsigned int column) const
1368{
1369 ocean_assert(row < 4u && column < 4u);
1370 return values[(column << 2) + row]; // values[(column * 4) + row];
1371}
1372
1373template <typename T>
1374inline T& SquareMatrixT4<T>::operator()(const unsigned int row, const unsigned int column)
1375{
1376 ocean_assert(row < 4u && column < 4u);
1377 return values[(column << 2) + row]; // values[(column * 4) + row];
1378}
1379
1380template <typename T>
1381inline T SquareMatrixT4<T>::operator()(const unsigned int index) const
1382{
1383 ocean_assert(index < 16u);
1384 return values[index];
1385}
1386
1387template <typename T>
1388inline T& SquareMatrixT4<T>::operator()(const unsigned int index)
1389{
1390 ocean_assert(index < 16u);
1391 return values[index];
1392}
1393
1394template <typename T>
1395inline const T* SquareMatrixT4<T>::operator()() const
1396{
1397 return values;
1398}
1399
1400template <typename T>
1402{
1403 return values;
1404}
1405
1406template <typename T>
1407inline size_t SquareMatrixT4<T>::operator()(const SquareMatrixT4<T>& matrix) const
1408{
1409 size_t seed = std::hash<T>{}(matrix.values[0]);
1410
1411 for (unsigned int n = 1u; n < 16u; ++n)
1412 {
1413 seed ^= std::hash<T>{}(matrix.values[n]) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
1414 }
1415
1416 return seed;
1417}
1418
1419template <typename T>
1421{
1422 return 16;
1423}
1424
1425template <typename T>
1427{
1428 return isEqual(matrix);
1429}
1430
1431template <typename T>
1433{
1434 SquareMatrixT4<T> result(*this);
1435
1436 result += matrix;
1437
1438 return result;
1439}
1440
1441template <typename T>
1443{
1444 for (unsigned int n = 0u; n < 16u; ++n)
1445 {
1446 values[n] += matrix.values[n];
1447 }
1448
1449 return *this;
1450}
1451
1452template <typename T>
1454{
1455 SquareMatrixT4<T> result(*this);
1456
1457 result -= matrix;
1458
1459 return result;
1460}
1461
1462template <typename T>
1464{
1465 for (unsigned int n = 0u; n < 16u; ++n)
1466 {
1467 values[n] -= matrix.values[n];
1468 }
1469
1470 return *this;
1471}
1472
1473template <typename T>
1475{
1476 SquareMatrixT4<T> result;
1477
1478 for (unsigned int n = 0u; n < 16u; ++n)
1479 {
1480 result.values[n] = -values[n];
1481 }
1482
1483 return result;
1484}
1485
1486template <typename T>
1488{
1489 SquareMatrixT4<T> result;
1490
1491 result.values[0] = values[0] * matrix.values[0] + values[4] * matrix.values[1] + values[8] * matrix.values[2] + values[12] * matrix.values[3];
1492 result.values[1] = values[1] * matrix.values[0] + values[5] * matrix.values[1] + values[9] * matrix.values[2] + values[13] * matrix.values[3];
1493 result.values[2] = values[2] * matrix.values[0] + values[6] * matrix.values[1] + values[10] * matrix.values[2] + values[14] * matrix.values[3];
1494 result.values[3] = values[3] * matrix.values[0] + values[7] * matrix.values[1] + values[11] * matrix.values[2] + values[15] * matrix.values[3];
1495
1496 result.values[4] = values[0] * matrix.values[4] + values[4] * matrix.values[5] + values[8] * matrix.values[6] + values[12] * matrix.values[7];
1497 result.values[5] = values[1] * matrix.values[4] + values[5] * matrix.values[5] + values[9] * matrix.values[6] + values[13] * matrix.values[7];
1498 result.values[6] = values[2] * matrix.values[4] + values[6] * matrix.values[5] + values[10] * matrix.values[6] + values[14] * matrix.values[7];
1499 result.values[7] = values[3] * matrix.values[4] + values[7] * matrix.values[5] + values[11] * matrix.values[6] + values[15] * matrix.values[7];
1500
1501 result.values[8] = values[0] * matrix.values[8] + values[4] * matrix.values[9] + values[8] * matrix.values[10] + values[12] * matrix.values[11];
1502 result.values[9] = values[1] * matrix.values[8] + values[5] * matrix.values[9] + values[9] * matrix.values[10] + values[13] * matrix.values[11];
1503 result.values[10] = values[2] * matrix.values[8] + values[6] * matrix.values[9] + values[10] * matrix.values[10] + values[14] * matrix.values[11];
1504 result.values[11] = values[3] * matrix.values[8] + values[7] * matrix.values[9] + values[11] * matrix.values[10] + values[15] * matrix.values[11];
1505
1506 result.values[12] = values[0] * matrix.values[12] + values[4] * matrix.values[13] + values[8] * matrix.values[14] + values[12] * matrix.values[15];
1507 result.values[13] = values[1] * matrix.values[12] + values[5] * matrix.values[13] + values[9] * matrix.values[14] + values[13] * matrix.values[15];
1508 result.values[14] = values[2] * matrix.values[12] + values[6] * matrix.values[13] + values[10] * matrix.values[14] + values[14] * matrix.values[15];
1509 result.values[15] = values[3] * matrix.values[12] + values[7] * matrix.values[13] + values[11] * matrix.values[14] + values[15] * matrix.values[15];
1510
1511 return result;
1512}
1513
1514#if defined(OCEAN_HARDWARE_AVX_VERSION) && OCEAN_HARDWARE_AVX_VERSION >= 10
1515
1516template <>
1518{
1519 // the following code uses the following AVX instructions, and needs AVX1 or higher
1520
1521 // AVX1:
1522 // _mm256_broadcast_sd
1523 // _mm256_loadu_pd
1524 // _mm256_mul_pd
1525 // _mm256_add_pd
1526 // _mm256_storeu_pd
1527
1528 // we use the same strategy as we apply for matrix-vector multiplication
1529 // further, here we interpret the right matrix as 4 vectors
1530
1531 // we load the columns of the left matrix
1532 __m256d c0 = _mm256_loadu_pd(values + 0);
1533 __m256d c1 = _mm256_loadu_pd(values + 4);
1534 __m256d c2 = _mm256_loadu_pd(values + 8);
1535 __m256d c3 = _mm256_loadu_pd(values + 12);
1536
1537
1538 // we determine the first vector of the resulting matrix
1539 __m256d v0 = _mm256_broadcast_sd(matrix.data() + 0);
1540 __m256d r0 = _mm256_mul_pd(c0, v0);
1541
1542 __m256d v1 = _mm256_broadcast_sd(matrix.data() + 1);
1543 __m256d r1 = _mm256_mul_pd(c1, v1);
1544
1545 r0 = _mm256_add_pd(r0, r1);
1546
1547 __m256d v2 = _mm256_broadcast_sd(matrix.data() + 2);
1548 __m256d r2 = _mm256_mul_pd(c2, v2);
1549
1550 r0 = _mm256_add_pd(r0, r2);
1551
1552 __m256d v3 = _mm256_broadcast_sd(matrix.data() + 3);
1553 __m256d r3 = _mm256_mul_pd(c3, v3);
1554
1555 r0 = _mm256_add_pd(r0, r3);
1556
1558
1559 _mm256_storeu_pd(result.data(), r0);
1560
1561
1562 // we determine the second vector of the resulting matrix
1563 __m256d v4 = _mm256_broadcast_sd(matrix.data() + 4);
1564 __m256d r4 = _mm256_mul_pd(c0, v4);
1565
1566 __m256d v5 = _mm256_broadcast_sd(matrix.data() + 5);
1567 __m256d r5 = _mm256_mul_pd(c1, v5);
1568
1569 r4 = _mm256_add_pd(r4, r5);
1570
1571 __m256d v6 = _mm256_broadcast_sd(matrix.data() + 6);
1572 __m256d r6 = _mm256_mul_pd(c2, v6);
1573
1574 r4 = _mm256_add_pd(r4, r6);
1575
1576 __m256d v7 = _mm256_broadcast_sd(matrix.data() + 7);
1577 __m256d r7 = _mm256_mul_pd(c3, v7);
1578
1579 r4 = _mm256_add_pd(r4, r7);
1580
1581 _mm256_storeu_pd(result.data() + 4, r4);
1582
1583
1584 // we determine the third vector of the resulting matrix
1585 __m256d v8 = _mm256_broadcast_sd(matrix.data() + 8);
1586 __m256d r8 = _mm256_mul_pd(c0, v8);
1587
1588 __m256d v9 = _mm256_broadcast_sd(matrix.data() + 9);
1589 __m256d r9 = _mm256_mul_pd(c1, v9);
1590
1591 r8 = _mm256_add_pd(r8, r9);
1592
1593 __m256d v10 = _mm256_broadcast_sd(matrix.data() + 10);
1594 __m256d r10 = _mm256_mul_pd(c2, v10);
1595
1596 r8 = _mm256_add_pd(r8, r10);
1597
1598 __m256d v11 = _mm256_broadcast_sd(matrix.data() + 11);
1599 __m256d r11 = _mm256_mul_pd(c3, v11);
1600
1601 r8 = _mm256_add_pd(r8, r11);
1602
1603 _mm256_storeu_pd(result.data() + 8, r8);
1604
1605
1606 // we determine the forth vector of the resulting matrix
1607 __m256d v12 = _mm256_broadcast_sd(matrix.data() + 12);
1608 __m256d r12 = _mm256_mul_pd(c0, v12);
1609
1610 __m256d v13 = _mm256_broadcast_sd(matrix.data() + 13);
1611 __m256d r13 = _mm256_mul_pd(c1, v13);
1612
1613 r12 = _mm256_add_pd(r12, r13);
1614
1615 __m256d v14 = _mm256_broadcast_sd(matrix.data() + 14);
1616 __m256d r14 = _mm256_mul_pd(c2, v14);
1617
1618 r12 = _mm256_add_pd(r12, r14);
1619
1620 __m256d v15 = _mm256_broadcast_sd(matrix.data() + 15);
1621 __m256d r15 = _mm256_mul_pd(c3, v15);
1622
1623 r12 = _mm256_add_pd(r12, r15);
1624
1625 _mm256_storeu_pd(result.data() + 12, r12);
1626
1627 return result;
1628}
1629
1630template <>
1632{
1633 // the following code uses the following AVX instructions, and needs AVX1 or higher
1634
1635 // we use the same strategy as we apply for matrix-vector multiplication
1636 // further, here we interpret the right matrix as 4 vectors, and combine two vectors into one 256 bit register
1637
1638 // we load the four columns of the left matrix
1639 __m256 c0 = _mm256_broadcast_ps((const __m128*)values + 0);
1640 __m256 c1 = _mm256_broadcast_ps((const __m128*)values + 1);
1641 __m256 c2 = _mm256_broadcast_ps((const __m128*)values + 2);
1642 __m256 c3 = _mm256_broadcast_ps((const __m128*)values + 3);
1643
1644 __m256 m01 = _mm256_loadu_ps(matrix.data() + 0);
1645 __m256 m23 = _mm256_loadu_ps(matrix.data() + 8);
1646
1647
1648 // we determine the first two vectors of the resulting matrix
1649 __m256 v0_4 = _mm256_permute_ps(m01, 0x00);
1650 __m256 r0 = _mm256_mul_ps(c0, v0_4);
1651
1652#ifdef OCEAN_COMPILER_MSC
1653 ocean_assert(NumericF::isEqual(r0.m256_f32[0], values[0] * matrix[0]));
1654 ocean_assert(NumericF::isEqual(r0.m256_f32[1], values[1] * matrix[0]));
1655 ocean_assert(NumericF::isEqual(r0.m256_f32[2], values[2] * matrix[0]));
1656 ocean_assert(NumericF::isEqual(r0.m256_f32[3], values[3] * matrix[0]));
1657
1658 ocean_assert(NumericF::isEqual(r0.m256_f32[4], values[0] * matrix[4]));
1659 ocean_assert(NumericF::isEqual(r0.m256_f32[5], values[1] * matrix[4]));
1660 ocean_assert(NumericF::isEqual(r0.m256_f32[6], values[2] * matrix[4]));
1661 ocean_assert(NumericF::isEqual(r0.m256_f32[7], values[3] * matrix[4]));
1662#endif
1663
1664 __m256 v1_5 = _mm256_permute_ps(m01, 0x55);
1665 __m256 r1 = _mm256_mul_ps(c1, v1_5);
1666
1667#ifdef OCEAN_COMPILER_MSC
1668 ocean_assert(NumericF::isEqual(r1.m256_f32[0], values[ 4] * matrix[1]));
1669 ocean_assert(NumericF::isEqual(r1.m256_f32[1], values[ 5] * matrix[1]));
1670 ocean_assert(NumericF::isEqual(r1.m256_f32[2], values[ 6] * matrix[1]));
1671 ocean_assert(NumericF::isEqual(r1.m256_f32[3], values[ 7] * matrix[1]));
1672
1673 ocean_assert(NumericF::isEqual(r1.m256_f32[4], values[4] * matrix[5]));
1674 ocean_assert(NumericF::isEqual(r1.m256_f32[5], values[5] * matrix[5]));
1675 ocean_assert(NumericF::isEqual(r1.m256_f32[6], values[6] * matrix[5]));
1676 ocean_assert(NumericF::isEqual(r1.m256_f32[7], values[7] * matrix[5]));
1677#endif
1678
1679 r0 = _mm256_add_ps(r0, r1);
1680
1681
1682 __m256 v2_6 = _mm256_permute_ps(m01, 0xAA);
1683 __m256 r2 = _mm256_mul_ps(c2, v2_6);
1684
1685 r0 = _mm256_add_ps(r0, r2);
1686
1687
1688 __m256 v3_7 = _mm256_permute_ps(m01, 0xFF);
1689 __m256 r3 = _mm256_mul_ps(c3, v3_7);
1690
1691 r0 = _mm256_add_ps(r0, r3);
1692
1693 SquareMatrixT4<float> result;
1694
1695 _mm256_storeu_ps(result.data(), r0);
1696
1697
1698
1699 // we determine the last two vectors of the resulting matrix
1700 __m256 v8_12 = _mm256_permute_ps(m23, 0x00);
1701 __m256 r8 = _mm256_mul_ps(c0, v8_12);
1702
1703
1704 __m256 v9_13 = _mm256_permute_ps(m23, 0x55);
1705 __m256 r9 = _mm256_mul_ps(c1, v9_13);
1706
1707 r8 = _mm256_add_ps(r8, r9);
1708
1709
1710 __m256 v10_14 = _mm256_permute_ps(m23, 0xAA);
1711 __m256 r10 = _mm256_mul_ps(c2, v10_14);
1712
1713 r8 = _mm256_add_ps(r8, r10);
1714
1715
1716 __m256 v11_15 = _mm256_permute_ps(m23, 0xFF);
1717 __m256 r11 = _mm256_mul_ps(c3, v11_15);
1718
1719 r8 = _mm256_add_ps(r8, r11);
1720
1721 _mm256_storeu_ps(result.data() + 8, r8);
1722
1723 return result;
1724}
1725
1726#else // OCEAN_HARDWARE_AVX_VERSION < 10
1727
1728#if defined(OCEAN_HARDWARE_SSE_VERSION) && OCEAN_HARDWARE_SSE_VERSION >= 10
1729
1730template <>
1732{
1733 // the following code uses the following SSE instructions, and needs SSE1 or higher
1734
1735 // SSE1:
1736 // _mm_load1_ps
1737 // _mm_loadu_ps
1738 // _mm_mul_ps
1739 // _mm_add_ps
1740 // _mm_storeu_ps
1741
1742 // we use the same strategy as we apply for matrix-vector multiplication
1743 // further, here we interpret the right matrix as 4 vectors
1744
1745 // we load the columns of the left matrix
1746 __m128 c0 = _mm_loadu_ps(values + 0);
1747 __m128 c1 = _mm_loadu_ps(values + 4);
1748 __m128 c2 = _mm_loadu_ps(values + 8);
1749 __m128 c3 = _mm_loadu_ps(values + 12);
1750
1751
1752 // we determine the first vector of the resulting matrix
1753 __m128 v0 = _mm_load1_ps(matrix.data() + 0);
1754 __m128 r0 = _mm_mul_ps(c0, v0);
1755
1756 __m128 v1 = _mm_load1_ps(matrix.data() + 1);
1757 __m128 r1 = _mm_mul_ps(c1, v1);
1758
1759 r0 = _mm_add_ps(r0, r1);
1760
1761 __m128 v2 = _mm_load1_ps(matrix.data() + 2);
1762 __m128 r2 = _mm_mul_ps(c2, v2);
1763
1764 r0 = _mm_add_ps(r0, r2);
1765
1766 __m128 v3 = _mm_load1_ps(matrix.data() + 3);
1767 __m128 r3 = _mm_mul_ps(c3, v3);
1768
1769 r0 = _mm_add_ps(r0, r3);
1770
1771 SquareMatrixT4<float> result;
1772
1773 _mm_storeu_ps(result.data(), r0);
1774
1775
1776 // we determine the second vector of the resulting matrix
1777 __m128 v4 = _mm_load1_ps(matrix.data() + 4);
1778 __m128 r4 = _mm_mul_ps(c0, v4);
1779
1780 __m128 v5 = _mm_load1_ps(matrix.data() + 5);
1781 __m128 r5 = _mm_mul_ps(c1, v5);
1782
1783 r4 = _mm_add_ps(r4, r5);
1784
1785 __m128 v6 = _mm_load1_ps(matrix.data() + 6);
1786 __m128 r6 = _mm_mul_ps(c2, v6);
1787
1788 r4 = _mm_add_ps(r4, r6);
1789
1790 __m128 v7 = _mm_load1_ps(matrix.data() + 7);
1791 __m128 r7 = _mm_mul_ps(c3, v7);
1792
1793 r4 = _mm_add_ps(r4, r7);
1794
1795 _mm_storeu_ps(result.data() + 4, r4);
1796
1797
1798 // we determine the third vector of the resulting matrix
1799 __m128 v8 = _mm_load1_ps(matrix.data() + 8);
1800 __m128 r8 = _mm_mul_ps(c0, v8);
1801
1802 __m128 v9 = _mm_load1_ps(matrix.data() + 9);
1803 __m128 r9 = _mm_mul_ps(c1, v9);
1804
1805 r8 = _mm_add_ps(r8, r9);
1806
1807 __m128 v10 = _mm_load1_ps(matrix.data() + 10);
1808 __m128 r10 = _mm_mul_ps(c2, v10);
1809
1810 r8 = _mm_add_ps(r8, r10);
1811
1812 __m128 v11 = _mm_load1_ps(matrix.data() + 11);
1813 __m128 r11 = _mm_mul_ps(c3, v11);
1814
1815 r8 = _mm_add_ps(r8, r11);
1816
1817 _mm_storeu_ps(result.data() + 8, r8);
1818
1819
1820 // we determine the forth vector of the resulting matrix
1821 __m128 v12 = _mm_load1_ps(matrix.data() + 12);
1822 __m128 r12 = _mm_mul_ps(c0, v12);
1823
1824 __m128 v13 = _mm_load1_ps(matrix.data() + 13);
1825 __m128 r13 = _mm_mul_ps(c1, v13);
1826
1827 r12 = _mm_add_ps(r12, r13);
1828
1829 __m128 v14 = _mm_load1_ps(matrix.data() + 14);
1830 __m128 r14 = _mm_mul_ps(c2, v14);
1831
1832 r12 = _mm_add_ps(r12, r14);
1833
1834 __m128 v15 = _mm_load1_ps(matrix.data() + 15);
1835 __m128 r15 = _mm_mul_ps(c3, v15);
1836
1837 r12 = _mm_add_ps(r12, r15);
1838
1839 _mm_storeu_ps(result.data() + 12, r12);
1840
1841 return result;
1842}
1843
1844#endif // OCEAN_HARDWARE_SSE_VERSION >= 10
1845
1846#endif // OCEAN_HARDWARE_AVX_VERSION >= 10
1847
1848template <typename T>
1850{
1851 SquareMatrixT4<T> result;
1852
1853 result.values[0] = values[0] * matrix[0] + values[4] * matrix[1] + values[8] * matrix[2]; // + values[12] * matrix[3];
1854 result.values[1] = values[1] * matrix[0] + values[5] * matrix[1] + values[9] * matrix[2]; // + values[13] * matrix[3];
1855 result.values[2] = values[2] * matrix[0] + values[6] * matrix[1] + values[10] * matrix[2]; // + values[14] * matrix[3];
1856 result.values[3] = values[3] * matrix[0] + values[7] * matrix[1] + values[11] * matrix[2]; // + values[15] * matrix[3];
1857
1858 result.values[4] = values[0] * matrix[4] + values[4] * matrix[5] + values[8] * matrix[6]; // + values[12] * matrix[7];
1859 result.values[5] = values[1] * matrix[4] + values[5] * matrix[5] + values[9] * matrix[6]; // + values[13] * matrix[7];
1860 result.values[6] = values[2] * matrix[4] + values[6] * matrix[5] + values[10] * matrix[6]; // + values[14] * matrix[7];
1861 result.values[7] = values[3] * matrix[4] + values[7] * matrix[5] + values[11] * matrix[6]; // + values[15] * matrix[7];
1862
1863 result.values[8] = values[0] * matrix[8] + values[4] * matrix[9] + values[8] * matrix[10]; // + values[12] * matrix[11];
1864 result.values[9] = values[1] * matrix[8] + values[5] * matrix[9] + values[9] * matrix[10]; // + values[13] * matrix[11];
1865 result.values[10] = values[2] * matrix[8] + values[6] * matrix[9] + values[10] * matrix[10]; // + values[14] * matrix[11];
1866 result.values[11] = values[3] * matrix[8] + values[7] * matrix[9] + values[11] * matrix[10]; // + values[15] * matrix[11];
1867
1868 result.values[12] = values[0] * matrix[12] + values[4] * matrix[13] + values[8] * matrix[14] + values[12]; // * matrix[15];
1869 result.values[13] = values[1] * matrix[12] + values[5] * matrix[13] + values[9] * matrix[14] + values[13]; // * matrix[15];
1870 result.values[14] = values[2] * matrix[12] + values[6] * matrix[13] + values[10] * matrix[14] + values[14]; // * matrix[15];
1871 result.values[15] = values[3] * matrix[12] + values[7] * matrix[13] + values[11] * matrix[14] + values[15]; // * matrix[15];
1872
1873 return result;
1874}
1875
1876template <typename T>
1877OCEAN_FORCE_INLINE VectorT3<T> SquareMatrixT4<T>::operator*(const VectorT3<T>& vector) const
1878{
1879 const T w = values[3] * vector[0] + values[7] * vector[1] + values[11] * vector[2] + values[15];
1880 ocean_assert(NumericT<T>::isNotEqualEps(w) && "Division by zero!");
1881
1882 const T factor = 1 / w;
1883
1884 return VectorT3<T>((values[0] * vector[0] + values[4] * vector[1] + values[8] * vector[2] + values[12]) * factor,
1885 (values[1] * vector[0] + values[5] * vector[1] + values[9] * vector[2] + values[13]) * factor,
1886 (values[2] * vector[0] + values[6] * vector[1] + values[10] * vector[2] + values[14]) * factor);
1887}
1888
1889template <typename T>
1890OCEAN_FORCE_INLINE VectorT4<T> SquareMatrixT4<T>::operator*(const VectorT4<T>& vector) const
1891{
1892 return VectorT4<T>(values[0] * vector[0] + values[4] * vector[1] + values[8] * vector[2] + values[12] * vector[3],
1893 values[1] * vector[0] + values[5] * vector[1] + values[9] * vector[2] + values[13] * vector[3],
1894 values[2] * vector[0] + values[6] * vector[1] + values[10] * vector[2] + values[14] * vector[3],
1895 values[3] * vector[0] + values[7] * vector[1] + values[11] * vector[2] + values[15] * vector[3]);
1896}
1897
1898#if defined(OCEAN_HARDWARE_AVX_VERSION) && OCEAN_HARDWARE_AVX_VERSION >= 10
1899
1900#ifdef OCEAN_USE_SLOWER_IMPLEMENTATION
1901
1902// we keep following implementation inside 'OCEAN_USE_SLOWER_IMPLEMENTATION' showing an alternative (which is slower)
1903
1904template <>
1906{
1907 // the following code uses the following AVX instructions, and needs AVX1 or higher
1908
1909 // AVX1:
1910 // _mm256_loadu_pd
1911 // _mm256_mul_pd
1912 // _mm256_shuffle_pd
1913 // _mm256_hadd_pd
1914 // _mm256_permute2f128_pd
1915 // _mm256_storeu_pd
1916
1917 // first we load the first four rows of the matrix
1918 __m256d row0 = _mm256_loadu_pd(values + 0);
1919 __m256d row1 = _mm256_loadu_pd(values + 4);
1920 __m256d row2 = _mm256_loadu_pd(values + 8);
1921 __m256d row3 = _mm256_loadu_pd(values + 12);
1922
1923 // we load the four values of the vector
1924 __m256d v = _mm256_loadu_pd(vector.data());
1925
1926 // first, we transpose the 4x4 matrix
1927
1928 // A E I M A B C D
1929 // B F J N E F G H
1930 // C G K O I J K L
1931 // D H L P M N O P
1932
1933 // A B C D, E F G H -> A E C G
1934 __m256d temp0 = _mm256_shuffle_pd(row0, row1, 0x00); // 0x00 = 0000 0000
1935 // A B C D, E F G H -> B F D H
1936 __m256d temp2 = _mm256_shuffle_pd(row0, row1, 0x0F); // 0x0F = 0000 1111
1937 // I J K L, M N O P -> I M K O
1938 __m256d temp1 = _mm256_shuffle_pd(row2, row3, 0x00);
1939 // I J K L, M N O P -> J N L P
1940 __m256d temp3 = _mm256_shuffle_pd(row2, row3, 0x0F);
1941
1942 // A E C G I M K O -> A E I M
1943 row0 = _mm256_permute2f128_pd(temp0, temp1, 0x20); // 0x20 = 0010 0000
1944 // B F D H J N L P -> B F J N
1945 row1 = _mm256_permute2f128_pd(temp2, temp3, 0x20);
1946 // A E C G I M K O -> C G K O
1947 row2 = _mm256_permute2f128_pd(temp0, temp1, 0x31); // 0x31 = 0011 0001
1948 // B F D H J N L P -> D H L P
1949 row3 = _mm256_permute2f128_pd(temp2, temp3, 0x31);
1950
1951#ifdef OCEAN_COMPILER_MSC
1952 ocean_assert(row0.m256d_f64[0] == values[0] && row0.m256d_f64[1] == values[4] && row0.m256d_f64[2] == values[ 8] && row0.m256d_f64[3] == values[12]);
1953 ocean_assert(row1.m256d_f64[0] == values[1] && row1.m256d_f64[1] == values[5] && row1.m256d_f64[2] == values[ 9] && row1.m256d_f64[3] == values[13]);
1954 ocean_assert(row2.m256d_f64[0] == values[2] && row2.m256d_f64[1] == values[6] && row2.m256d_f64[2] == values[10] && row2.m256d_f64[3] == values[14]);
1955 ocean_assert(row3.m256d_f64[0] == values[3] && row3.m256d_f64[1] == values[7] && row3.m256d_f64[2] == values[11] && row3.m256d_f64[3] == values[15]);
1956#endif
1957
1958 // unfortunately the AVX does not offer _mm256_dp_pd (the determination of the dot product) so we have to do it on our own
1959
1960 __m256d r0v = _mm256_mul_pd(row0, v);
1961 __m256d r1v = _mm256_mul_pd(row1, v);
1962 __m256d r2v = _mm256_mul_pd(row2, v);
1963 __m256d r3v = _mm256_mul_pd(row3, v);
1964
1965 // we sum both multiplication results horizontally (at least two neighboring products)
1966 __m256d sum_interleaved_r0_r1 = _mm256_hadd_pd(r0v, r1v);
1967 __m256d sum_interleaved_r2_r3 = _mm256_hadd_pd(r2v, r3v);
1968
1969 // now we reorder the interleaved sums
1970 __m256d sum_first = _mm256_permute2f128_pd(sum_interleaved_r0_r1, sum_interleaved_r2_r3, 0x20); // 0x20 = 0010 0000
1971 __m256d sum_second = _mm256_permute2f128_pd(sum_interleaved_r0_r1, sum_interleaved_r2_r3, 0x31); // 0x31 = 0011 0001
1972
1973 // we finally add both reordered sums
1974 __m256d sum = _mm256_add_pd(sum_first, sum_second);
1975
1976 VectorT4<double> result;
1977 _mm256_storeu_pd(result.data(), sum);
1978
1979 ocean_assert(NumericD::isEqual(result[0], values[0] * vector[0] + values[4] * vector[1] + values[ 8] * vector[2] + values[12] * vector[3], NumericD::eps() * 100));
1980 ocean_assert(NumericD::isEqual(result[1], values[1] * vector[0] + values[5] * vector[1] + values[ 9] * vector[2] + values[13] * vector[3], NumericD::eps() * 100));
1981 ocean_assert(NumericD::isEqual(result[2], values[2] * vector[0] + values[6] * vector[1] + values[10] * vector[2] + values[14] * vector[3], NumericD::eps() * 100));
1982 ocean_assert(NumericD::isEqual(result[3], values[3] * vector[0] + values[7] * vector[1] + values[11] * vector[2] + values[15] * vector[3], NumericD::eps() * 100));
1983
1984 return result;
1985}
1986
1987#else // OCEAN_USE_SLOWER_IMPLEMENTATION
1988
1989template <>
1991{
1992 // the following code uses the following AVX instructions, and needs AVX1 or higher
1993
1994 // AVX1:
1995 // _mm256_broadcast_sd
1996 // _mm256_loadu_pd
1997 // _mm256_mul_pd
1998 // _mm256_add_pd
1999 // _mm256_storeu_pd
2000
2001 // we use the same strategy as for the 32 bit float values
2002
2003 // first we load the first vector element in all 64bit elements of the 256 bit register, so that we receive [a, a, a, a]
2004 __m256d v0 = _mm256_broadcast_sd(vector.data() + 0);
2005
2006 // now we load the first column to receive: [A, B, C, D]
2007 __m256d c0 = _mm256_loadu_pd(values + 0);
2008
2009 // now we multiply the 256 bit register [A, B, C, D] * [a, a, a, a] = [Aa, Ba, Ca, Da]
2010 __m256d r0 = _mm256_mul_pd(c0, v0);
2011
2012
2013 // now we proceed with the second column
2014 __m256d v1 = _mm256_broadcast_sd(vector.data() + 1);
2015 __m256d c1 = _mm256_loadu_pd(values + 4);
2016 __m256d r1 = _mm256_mul_pd(c1, v1);
2017
2018 // and we sum the result of the first column with the result of the second column
2019 r0 = _mm256_add_pd(r0, r1);
2020
2021
2022 // now we proceed with the third column
2023 __m256d v2 = _mm256_broadcast_sd(vector.data() + 2);
2024 __m256d c2 = _mm256_loadu_pd(values + 8);
2025 __m256d r2 = _mm256_mul_pd(c2, v2);
2026
2027 // we sum the results
2028 r0 = _mm256_add_pd(r0, r2);
2029
2030
2031 // now we proceed with the fourth column
2032 __m256d v3 = _mm256_broadcast_sd(vector.data() + 3);
2033 __m256d c3 = _mm256_loadu_pd(values + 12);
2034 __m256d r3 = _mm256_mul_pd(c3, v3);
2035
2036 // we sum the results
2037 r0 = _mm256_add_pd(r0, r3);
2038
2039 // and finally we store the results back to the vector
2040 VectorT4<double> result;
2041
2042 _mm256_storeu_pd(result.data(), r0);
2043
2044 return result;
2045}
2046
2047#endif // OCEAN_USE_SLOWER_IMPLEMENTATION
2048
2049#else // OCEAN_HARDWARE_AVX_VERSION
2050
2051#if defined(OCEAN_HARDWARE_SSE_VERSION) && OCEAN_HARDWARE_SSE_VERSION >= 20
2052
2053template <>
2055{
2056 // the following code uses the following SSE instructions, and needs SSE2 or higher
2057
2058 // SSE2:
2059 // _mm_load1_pd
2060 // _mm_loadu_pd
2061 // _mm_add_pd
2062 // _mm_storeu_pd
2063 // _mm_mul_pd
2064
2065 // we use the following strategy:
2066 // the values of the matrix are column aligned so that we normally would need to transpose the matrix before we can apply simple SIMD instructions
2067 // however, we do not transpose the matrix (we avoid the shuffle instructions) and instead multiply the matrix column-wise:
2068 // finally we sum the four columns and have the result, compared to the transpose-based approach this approach is approx. two times faster
2069 //
2070 // A E I M a Aa + Eb + Ic + Md
2071 // B F J N b Ba + Fb + Jc + Nd
2072 // C G K O * c = Ca + Gb + Kc + Od
2073 // D H L P d Da + Hb + Lc + Pd
2074
2075 // first we load the first vector element in both 64 elements of the 128 bit register, so that we receive [a, a]
2076 __m128d v0 = _mm_load1_pd(vector.data() + 0);
2077
2078 // now we load the first column to receive: [A, B] and [C, D]
2079 __m128d c0a = _mm_loadu_pd(values + 0);
2080 __m128d c0b = _mm_loadu_pd(values + 2);
2081
2082 // now we multiply both 128 bit registers by: [A, B] * [a, a] = [Aa, Ba] and [C, D] * [a, a] = [Ca, Da]
2083 __m128d r0a = _mm_mul_pd(c0a, v0);
2084 __m128d r0b = _mm_mul_pd(c0b, v0);
2085
2086
2087 // now we proceed with the second column
2088 __m128d v1 = _mm_load1_pd(vector.data() + 1);
2089
2090 __m128d c1a = _mm_loadu_pd(values + 4);
2091 __m128d c1b = _mm_loadu_pd(values + 6);
2092
2093 __m128d r1a = _mm_mul_pd(c1a, v1);
2094 __m128d r1b = _mm_mul_pd(c1b, v1);
2095
2096 // and we sum the result of the first column with the result of the second column
2097 r0a = _mm_add_pd(r0a, r1a);
2098 r0b = _mm_add_pd(r0b, r1b);
2099
2100
2101 // now we proceed with the third column
2102 __m128d v2 = _mm_load1_pd(vector.data() + 2);
2103
2104 __m128d c2a = _mm_loadu_pd(values + 8);
2105 __m128d c2b = _mm_loadu_pd(values + 10);
2106
2107 __m128d r2a = _mm_mul_pd(c2a, v2);
2108 __m128d r2b = _mm_mul_pd(c2b, v2);
2109
2110 // we sum the results
2111 r0a = _mm_add_pd(r0a, r2a);
2112 r0b = _mm_add_pd(r0b, r2b);
2113
2114
2115 // now we proceed with the fourth column
2116 __m128d v3 = _mm_load1_pd(vector.data() + 3);
2117
2118 __m128d c3a = _mm_loadu_pd(values + 12);
2119 __m128d c3b = _mm_loadu_pd(values + 14);
2120
2121 __m128d r3a = _mm_mul_pd(c3a, v3);
2122 __m128d r3b = _mm_mul_pd(c3b, v3);
2123
2124 // we sum the results
2125 r0a = _mm_add_pd(r0a, r3a);
2126 r0b = _mm_add_pd(r0b, r3b);
2127
2128 // and finally we store the results back to the vector
2129 VectorT4<double> result;
2130
2131 _mm_storeu_pd(result.data() + 0, r0a);
2132 _mm_storeu_pd(result.data() + 2, r0b);
2133
2134 return result;
2135}
2136
2137#endif // OCEAN_HARDWARE_SSE_VERSION >= 20
2138
2139#endif // OCEAN_HARDWARE_AVX_VERSION >= 10
2140
2141#ifdef OCEAN_USE_SLOWER_IMPLEMENTATION
2142
2143// we keep following implementation inside 'OCEAN_USE_SLOWER_IMPLEMENTATION' showing an alternative (which is slower)
2144
2145#if defined(OCEAN_HARDWARE_SSE_VERSION) && OCEAN_HARDWARE_SSE_VERSION >= 41
2146
2147template <>
2149{
2150 // the following code uses the following SSE instructions, and needs SSE 4.1 or higher
2151
2152 // SSE:
2153 // _mm_loadu_ps
2154 // _mm_shuffle_ps
2155 // _mm_or_ps
2156 // _mm_storeu_ps
2157
2158 // SSE4.1:
2159 // _mm_dp_ps
2160
2161 // first we load the first four rows of the matrix
2162 __m128 row0 = _mm_loadu_ps(values + 0);
2163 __m128 row1 = _mm_loadu_ps(values + 4);
2164 __m128 row2 = _mm_loadu_ps(values + 8);
2165 __m128 row3 = _mm_loadu_ps(values + 12);
2166
2167 // we load the four values of the vector
2168 __m128 v = _mm_loadu_ps(vector.data());
2169
2170 // first, we transpose the 4x4 matrix
2171
2172 // A E I M A B C D
2173 // B F J N E F G H
2174 // C G K O I J K L
2175 // D H L P M N O P
2176
2177 // A B C D, E F G H -> A B E F
2178 __m128 temp0 = _mm_shuffle_ps(row0, row1, 0x44); // 0x44 = 0100 0100
2179 // A B C D, E F G H -> C D G H
2180 __m128 temp2 = _mm_shuffle_ps(row0, row1, 0xEE); // 0xEE = 1110 1110
2181 // I J K L, M N O P -> I J M N
2182 __m128 temp1 = _mm_shuffle_ps(row2, row3, 0x44);
2183 // I J K L, M N O P -> K L O P
2184 __m128 temp3 = _mm_shuffle_ps(row2, row3, 0xEE);
2185
2186 // A B E F, I J M N -> A E I M
2187 row0 = _mm_shuffle_ps(temp0, temp1, 0x88); // 0x88 = 1000 1000
2188 // A B E F, I J M N -> B F J N
2189 row1 = _mm_shuffle_ps(temp0, temp1, 0xDD); // 0xDD = 1101 1101
2190 // C D G H, K L O P -> C G K O
2191 row2 = _mm_shuffle_ps(temp2, temp3, 0x88);
2192 // C D G H, K L O P -> D H L P
2193 row3 = _mm_shuffle_ps(temp2, temp3, 0xDD);
2194
2195#ifdef OCEAN_COMPILER_MSC
2196 ocean_assert(row0.m128_f32[0] == values[0] && row0.m128_f32[1] == values[4] && row0.m128_f32[2] == values[ 8] && row0.m128_f32[3] == values[12]);
2197 ocean_assert(row1.m128_f32[0] == values[1] && row1.m128_f32[1] == values[5] && row1.m128_f32[2] == values[ 9] && row1.m128_f32[3] == values[13]);
2198 ocean_assert(row2.m128_f32[0] == values[2] && row2.m128_f32[1] == values[6] && row2.m128_f32[2] == values[10] && row2.m128_f32[3] == values[14]);
2199 ocean_assert(row3.m128_f32[0] == values[3] && row3.m128_f32[1] == values[7] && row3.m128_f32[2] == values[11] && row3.m128_f32[3] == values[15]);
2200#endif
2201
2202 // we determine the dot product between the first row and the vector and store the result in the first float bin
2203 row0 = _mm_dp_ps(row0, v, 0xF1); // 0xF1 = 1111 0001
2204
2205 // we determine the dot product between the second row and the vector and store the result in the second float bin
2206 row1 = _mm_dp_ps(row1, v, 0xF2); // 0xF2 = 1111 0010
2207 row2 = _mm_dp_ps(row2, v, 0xF4); // 0xF4 = 1111 0100
2208 row3 = _mm_dp_ps(row3, v, 0xF8); // 0xF8 = 1111 1000
2209
2210 // now we blend the results by applying the bit-wise or operator
2211 __m128 result01 = _mm_or_ps(row0, row1);
2212 __m128 result23 = _mm_or_ps(row2, row3);
2213 __m128 result03 = _mm_or_ps(result01, result23);
2214
2215 VectorT4<float> result;
2216 _mm_storeu_ps(result.data(), result03);
2217
2218 return result;
2219}
2220
2221#endif // OCEAN_HARDWARE_SSE_VERSION >= 41
2222
2223#else // OCEAN_USE_SLOWER_IMPLEMENTATION
2224
2225#if defined(OCEAN_HARDWARE_SSE_VERSION) && OCEAN_HARDWARE_SSE_VERSION >= 10
2226
2227template <>
2229{
2230 // the following code uses the following SSE instructions, and needs SSE1 or higher
2231
2232 // SSE1:
2233 // _mm_load1_ps
2234 // _mm_loadu_ps
2235 // _mm_mul_ps
2236 // _mm_add_ps
2237 // _mm_storeu_ps
2238
2239
2240 // we use the following strategy:
2241 // the values of the matrix are column aligned so that we normally would need to transpose the matrix before we can apply simple SIMD instructions
2242 // however, we do not transpose the matrix (we avoid the shuffle instructions) and instead multiply the matrix column-wise:
2243 // finally we sum the four columns and have the result, compared to the transpose-based approach this approach is approx. two times faster
2244 //
2245 // A E I M a Aa + Eb + Ic + Md
2246 // B F J N b Ba + Fb + Jc + Nd
2247 // C G K O * c = Ca + Gb + Kc + Od
2248 // D H L P d Da + Hb + Lc + Pd
2249
2250 // first we load the first vector element in all 32bit elements of the 128 bit register, so that we receive [a, a, a, a]
2251 __m128 v0 = _mm_load1_ps(vector.data() + 0);
2252
2253 // now we load the first column to receive: [A, B, C, D]
2254 __m128 c0 = _mm_loadu_ps(values + 0);
2255
2256 // now we multiply the 128 bit register [A, B, C, D] * [a, a, a, a] = [Aa, Ba, Ca, Da]
2257 __m128 r0 = _mm_mul_ps(c0, v0);
2258
2259
2260 // now we proceed with the second column
2261 __m128 v1 = _mm_load1_ps(vector.data() + 1);
2262 __m128 c1 = _mm_loadu_ps(values + 4);
2263 __m128 r1 = _mm_mul_ps(c1, v1);
2264
2265 // and we sum the result of the first column with the result of the second column
2266 r0 = _mm_add_ps(r0, r1);
2267
2268
2269 // now we proceed with the third column
2270 __m128 v2 = _mm_load1_ps(vector.data() + 2);
2271 __m128 c2 = _mm_loadu_ps(values + 8);
2272 __m128 r2 = _mm_mul_ps(c2, v2);
2273
2274 // we sum the results
2275 r0 = _mm_add_ps(r0, r2);
2276
2277
2278 // now we proceed with the fourth column
2279 __m128 v3 = _mm_load1_ps(vector.data() + 3);
2280 __m128 c3 = _mm_loadu_ps(values + 12);
2281 __m128 r3 = _mm_mul_ps(c3, v3);
2282
2283 // we sum the results
2284 r0 = _mm_add_ps(r0, r3);
2285
2286 // and finally we store the results back to the vector
2287 VectorT4<float> result;
2288
2289 _mm_storeu_ps(result.data(), r0);
2290
2291 return result;
2292}
2293
2294#endif // OCEAN_HARDWARE_SSE_VERSION >= 10
2295
2296#endif // OCEAN_USE_SLOWER_IMPLEMENTATION
2297
2298#if defined(OCEAN_HARDWARE_NEON_VERSION) && OCEAN_HARDWARE_NEON_VERSION >= 10
2299
2300#ifdef __aarch64__
2301
2302template <>
2304{
2305 // the following NEON code is almost identical to the SSE implementation
2306
2307 // we use the following strategy:
2308 // the values of the matrix are column aligned so that we normally would need to transpose the matrix before we can apply simple SIMD instructions
2309 // however, we do not transpose the matrix (we avoid the shuffle instructions) and instead multiply the matrix column-wise:
2310 // finally we sum the four columns and have the result, compared to the transpose-based approach this approach is approx. two times faster
2311 //
2312 // A E I M a Aa + Eb + Ic + Md
2313 // B F J N b Ba + Fb + Jc + Nd
2314 // C G K O * c = Ca + Gb + Kc + Od
2315 // D H L P d Da + Hb + Lc + Pd
2316
2317 // first we load the first vector element in both 64 elements of the 128 bit register, so that we receive [a, a]
2318 float64x2_t v0 = vld1q_dup_f64(vector.data() + 0);
2319
2320 // now we load the first column to receive: [A, B] and [C, D]
2321 float64x2_t c0a = vld1q_f64(values + 0);
2322 float64x2_t c0b = vld1q_f64(values + 2);
2323
2324 // now we multiply both 128 bit registers by: [A, B] * [a, a] = [Aa, Ba] and [C, D] * [a, a] = [Ca, Da]
2325 float64x2_t r0a = vmulq_f64(c0a, v0);
2326 float64x2_t r0b = vmulq_f64(c0b, v0);
2327
2328
2329 // now we proceed with the second column
2330 float64x2_t v1 = vld1q_dup_f64(vector.data() + 1);
2331
2332 float64x2_t c1a = vld1q_f64(values + 4);
2333 float64x2_t c1b = vld1q_f64(values + 6);
2334
2335 float64x2_t r1a = vmulq_f64(c1a, v1);
2336 float64x2_t r1b = vmulq_f64(c1b, v1);
2337
2338 // and we sum the result of the first column with the result of the second column
2339 r0a = vaddq_f64(r0a, r1a);
2340 r0b = vaddq_f64(r0b, r1b);
2341
2342
2343 // now we proceed with the third column
2344 float64x2_t v2 = vld1q_dup_f64(vector.data() + 2);
2345
2346 float64x2_t c2a = vld1q_f64(values + 8);
2347 float64x2_t c2b = vld1q_f64(values + 10);
2348
2349 float64x2_t r2a = vmulq_f64(c2a, v2);
2350 float64x2_t r2b = vmulq_f64(c2b, v2);
2351
2352 // we sum the results
2353 r0a = vaddq_f64(r0a, r2a);
2354 r0b = vaddq_f64(r0b, r2b);
2355
2356
2357 // now we proceed with the fourth column
2358 float64x2_t v3 = vld1q_dup_f64(vector.data() + 3);
2359
2360 float64x2_t c3a = vld1q_f64(values + 12);
2361 float64x2_t c3b = vld1q_f64(values + 14);
2362
2363 float64x2_t r3a = vmulq_f64(c3a, v3);
2364 float64x2_t r3b = vmulq_f64(c3b, v3);
2365
2366 // we sum the results
2367 r0a = vaddq_f64(r0a, r3a);
2368 r0b = vaddq_f64(r0b, r3b);
2369
2370 // and finally we store the results back to the vector
2371 VectorT4<double> result;
2372
2373 vst1q_f64(result.data() + 0, r0a);
2374 vst1q_f64(result.data() + 2, r0b);
2375
2376 return result;
2377}
2378
2379#endif // __aarch64__
2380
2381template <>
2383{
2384 // the following NEON code is almost identical to the SSE implementation
2385
2386 // we use the following strategy:
2387 // the values of the matrix are column aligned so that we normally would need to transpose the matrix before we can apply simple SIMD instructions
2388 // however, we do not transpose the matrix (we avoid the shuffle instructions) and instead multiply the matrix column-wise:
2389 // finally we sum the four columns and have the result, compared to the transpose-based approach this approach is approx. two times faster
2390 //
2391 // A E I M a Aa + Eb + Ic + Md
2392 // B F J N b Ba + Fb + Jc + Nd
2393 // C G K O * c = Ca + Gb + Kc + Od
2394 // D H L P d Da + Hb + Lc + Pd
2395
2396 // first we load the first vector element in all 32bit elements of the 128 bit register, so that we receive [a, a, a, a]
2397 float32x4_t v0 = vld1q_dup_f32(vector.data() + 0);
2398
2399 // now we load the first column to receive: [A, B, C, D]
2400 float32x4_t c0 = vld1q_f32(values + 0);
2401
2402 // now we multiply the 128 bit register [A, B, C, D] * [a, a, a, a] = [Aa, Ba, Ca, Da]
2403 float32x4_t r0 = vmulq_f32(c0, v0);
2404
2405
2406 // now we proceed with the second column
2407 float32x4_t v1 = vld1q_dup_f32(vector.data() + 1);
2408 float32x4_t c1 = vld1q_f32(values + 4);
2409 float32x4_t r1 = vmulq_f32(c1, v1);
2410
2411 // and we sum the result of the first column with the result of the second column
2412 r0 = vaddq_f32(r0, r1);
2413
2414
2415 // now we proceed with the third column
2416 float32x4_t v2 = vld1q_dup_f32(vector.data() + 2);
2417 float32x4_t c2 = vld1q_f32(values + 8);
2418 float32x4_t r2 = vmulq_f32(c2, v2);
2419
2420 // we sum the results
2421 r0 = vaddq_f32(r0, r2);
2422
2423
2424 // now we proceed with the fourth column
2425 float32x4_t v3 = vld1q_dup_f32(vector.data() + 3);
2426 float32x4_t c3 = vld1q_f32(values + 12);
2427 float32x4_t r3 = vmulq_f32(c3, v3);
2428
2429 // we sum the results
2430 r0 = vaddq_f32(r0, r3);
2431
2432 // and finally we store the results back to the vector
2433 VectorT4<float> result;
2434
2435 vst1q_f32(result.data(), r0);
2436
2437 return result;
2438}
2439
2440#endif
2441
2442template <typename T>
2444{
2445 SquareMatrixT4<T> result(*this);
2446
2447 result.values[0] *= value;
2448 result.values[1] *= value;
2449 result.values[2] *= value;
2450 result.values[3] *= value;
2451 result.values[4] *= value;
2452 result.values[5] *= value;
2453 result.values[6] *= value;
2454 result.values[7] *= value;
2455 result.values[8] *= value;
2456 result.values[9] *= value;
2457 result.values[10] *= value;
2458 result.values[11] *= value;
2459 result.values[12] *= value;
2460 result.values[13] *= value;
2461 result.values[14] *= value;
2462 result.values[15] *= value;
2463
2464 return result;
2465}
2466
2467template <typename T>
2469{
2470 values[0] *= value;
2471 values[1] *= value;
2472 values[2] *= value;
2473 values[3] *= value;
2474 values[4] *= value;
2475 values[5] *= value;
2476 values[6] *= value;
2477 values[7] *= value;
2478 values[8] *= value;
2479 values[9] *= value;
2480 values[10] *= value;
2481 values[11] *= value;
2482 values[12] *= value;
2483 values[13] *= value;
2484 values[14] *= value;
2485 values[15] *= value;
2486
2487 return *this;
2488}
2489
2490template <typename T>
2491SquareMatrixT4<T> SquareMatrixT4<T>::projectionMatrix(const T fovX, const T aspectRatio, const T nearDistance, const T farDistance)
2492{
2493 /*
2494 * <pre>
2495 * Creates the following frustum projection matrix.
2496 *
2497 * --------------------------------------------------
2498 * | t/a 0 0 0 |
2499 * | 0 t 0 0 |
2500 * | 0 0 (f+n)/(n-f) -2fn/(n-f) |
2501 * | 0 0 -1 0 |
2502 * --------------------------------------------------
2503 *
2504 * With: t = 1 / tan (fovY / 2), a = aspectRatio, n = nearDistance, f = farDistance
2505 * </pre>
2506 */
2507
2508 ocean_assert(fovX > 0 && fovX < NumericT<T>::pi());
2509 ocean_assert(aspectRatio > 0);
2510 ocean_assert(nearDistance > 0);
2511 ocean_assert(nearDistance < farDistance);
2512
2513 const T fovY = T(2.0) * NumericT<T>::atan(NumericT<T>::tan(T(0.5) * fovX) / aspectRatio);
2514
2515 SquareMatrixT4<T> matrix(false);
2516 ocean_assert(matrix(1, 0) == 0 && matrix(2, 0) == 0 && matrix(3, 0) == 0);
2517 ocean_assert(matrix(0, 1) == 0 && matrix(2, 1) == 0 && matrix(3, 1) == 0);
2518 ocean_assert(matrix(0, 2) == 0 && matrix(1, 2) == 0);
2519 ocean_assert(matrix(0, 3) == 0 && matrix(1, 3) == 0 && matrix(3, 3) == 0);
2520
2521 ocean_assert(NumericT<T>::isNotEqual(farDistance, nearDistance));
2522
2523 const T f = T(1.0) / NumericT<T>::tan(fovY * T(0.5));
2524 const T factor = T(1.0) / (nearDistance - farDistance);
2525
2526 matrix(0, 0) = f / aspectRatio;
2527 matrix(1, 1) = f;
2528 matrix(2, 2) = (farDistance + nearDistance) * factor;
2529 matrix(3, 2) = -T(1.0);
2530 matrix(2, 3) = (T(2.0) * farDistance * nearDistance) * factor;
2531
2532 return matrix;
2533}
2534
2535template <typename T>
2536SquareMatrixT4<T> SquareMatrixT4<T>::projectionMatrix(const AnyCameraT<T>& anyCamera, const T nearDistance, const T farDistance)
2537{
2538 /*
2539 * <pre>
2540 * Creates the following frustum projection matrix.
2541 *
2542 * --------------------------------------------------
2543 * | Fx 0 mx 0 |
2544 * | 0 Fy my 0 |
2545 * | 0 0 (f+n)/(n-f) -2fn/(n-f) |
2546 * | 0 0 -1 0 |
2547 * --------------------------------------------------
2548 *
2549 * n = nearDistance, f = farDistance
2550 * </pre>
2551 */
2552
2553 ocean_assert(anyCamera.isValid());
2554
2555 ocean_assert(nearDistance > 0);
2556 ocean_assert(nearDistance < farDistance);
2557
2558 const T fxPixel = anyCamera.focalLengthX();
2559 const T fyPixel = anyCamera.focalLengthY();
2560 ocean_assert(fxPixel > T(1) && fyPixel > T(1));
2561
2562 const T mxPixel = anyCamera.principalPointX();
2563 const T myPixel = anyCamera.principalPointY();
2564
2565 const T width_2 = T(anyCamera.width()) / T(2);
2566 const T height_2 = T(anyCamera.height()) / T(2);
2567
2568 ocean_assert(NumericT<T>::isNotEqualEps(width_2));
2569 ocean_assert(NumericT<T>::isNotEqualEps(height_2));
2570
2571 const T fx = fxPixel / width_2;
2572 const T fy = fyPixel / height_2;
2573
2574 const T mx = (mxPixel - width_2) / width_2; // principal point with range [-1, 1]
2575 const T my = (myPixel - height_2) / height_2;
2576
2577 const T factor = T(1.0) / (nearDistance - farDistance);
2578
2579 SquareMatrixT4<T> matrix(false);
2580
2581 matrix(0, 0) = fx;
2582 matrix(1, 1) = fy;
2583 matrix(0, 2) = -mx;
2584 matrix(1, 2) = my;
2585 matrix(2, 2) = (farDistance + nearDistance) * factor;
2586 matrix(3, 2) = -T(1);
2587 matrix(2, 3) = (T(2) * farDistance * nearDistance) * factor;
2588
2589 return matrix;
2590}
2591
2592template <typename T>
2593SquareMatrixT4<T> SquareMatrixT4<T>::frustumMatrix(const T left, const T right, const T top, const T bottom, const T nearDistance, const T farDistance)
2594{
2595 /**
2596 * <pre>
2597 * Creates the following frustum projection matrix:
2598 *
2599 * --------------------------------------------------
2600 * | 2n/(r-l) 0 (r+l)/(r-l) 0 |
2601 * | 0 2n/(t-b) (t+b)/(t-b) 0 |
2602 * | 0 0 -(f+n)/(f-n) -2fn/(f-n) |
2603 * | 0 0 -1 0 |
2604 * --------------------------------------------------
2605 * </pre>
2606 */
2607
2608 ocean_assert(NumericT<T>::isNotEqual(left, right));
2609 ocean_assert(NumericT<T>::isNotEqual(top, bottom));
2610 ocean_assert(NumericT<T>::isNotEqual(nearDistance, farDistance));
2611
2612 SquareMatrixT4<T> matrix(false);
2613 ocean_assert(matrix(1, 0) == 0 && matrix(2, 0) == 0 && matrix(3, 0) == 0);
2614 ocean_assert(matrix(0, 1) == 0 && matrix(2, 1) == 0 && matrix(3, 1) == 0);
2615 ocean_assert(matrix(0, 3) == 0 && matrix(1, 3) == 0 && matrix(3, 3) == 0);
2616
2617 const T rightLeft = T(1.0) / (right - left);
2618 const T near2 = nearDistance * T(2.0);
2619
2620 matrix(0, 0) = near2 * rightLeft;
2621 matrix(0, 2) = (right + left) * rightLeft;
2622
2623 const T topBottom = T(1.0) / (top - bottom);
2624
2625 matrix(1, 1) = near2 * topBottom;
2626 matrix(1, 2) = (top + bottom) * topBottom;
2627
2628 const T farNear = T(1.0) / (farDistance - nearDistance);
2629
2630 matrix(2, 2) = -(farDistance + nearDistance) * farNear;
2631 matrix(2, 3) = -T(2.0) * farDistance * nearDistance * farNear;
2632
2633 matrix(3, 2) = -T(1.0);
2634
2635 return matrix;
2636}
2637
2638template <typename T>
2639SquareMatrixT4<T> SquareMatrixT4<T>::frustumMatrix(const T width, const T height, const HomogenousMatrixT4<T>& viewingMatrix, const T nearDistance, const T farDistance)
2640{
2641 ocean_assert(width > NumericT<T>::eps() && height > NumericT<T>::eps());
2642 ocean_assert(nearDistance >= NumericT<T>::eps() && farDistance > nearDistance);
2643
2644 const T planeDistance = NumericT<T>::abs(viewingMatrix.translation().z());
2645 ocean_assert(viewingMatrix.isValid() && NumericT<T>::isNotEqualEps(planeDistance));
2646
2647 const HomogenousMatrixT4<T> inversedViewingMatrix(viewingMatrix.inverted());
2648
2649 const VectorT3<T> leftTop(width * -T(0.5), height * T(0.5), 0);
2650 const VectorT3<T> rightBottom(width * T(0.5), height * -T(0.5), 0);
2651
2652 const VectorT3<T> leftTopInCamera(inversedViewingMatrix * leftTop);
2653 const VectorT3<T> rightBottomInCamera(inversedViewingMatrix * rightBottom);
2654
2655 const T factor = nearDistance / planeDistance;
2656
2657 return frustumMatrix(factor * leftTopInCamera.x(), factor * rightBottomInCamera.x(), factor * leftTopInCamera.y(), factor * rightBottomInCamera.y(), nearDistance, farDistance);
2658}
2659
2660template <typename T>
2661void SquareMatrixT4<T>::multiply(const SquareMatrixT4<T>& matrix, const VectorT4<T>* vectors, VectorT4<T>* results, const size_t number)
2662{
2663 ocean_assert((vectors && results) || number == 0);
2664
2665 for (size_t n = 0; n < number; ++n)
2666 {
2667 results[n] = matrix * vectors[n];
2668 }
2669}
2670
2671#if defined(OCEAN_HARDWARE_AVX_VERSION) && OCEAN_HARDWARE_AVX_VERSION >= 10
2672
2673template <>
2674inline void SquareMatrixT4<double>::multiply(const SquareMatrixT4<double>& matrix, const VectorT4<double>* vectors, VectorT4<double>* results, const size_t number)
2675{
2676 // the following code uses the following AVX instructions, and needs AVX1 or higher
2677
2678 // AVX1:
2679 // _mm256_broadcast_sd
2680 // _mm256_loadu_pd
2681 // _mm256_mul_pd
2682 // _mm256_add_pd
2683 // _mm256_storeu_pd
2684
2685 // we use the same strategy as for the 32 bit float values
2686
2687 __m256d c0 = _mm256_loadu_pd(matrix.values + 0);
2688 __m256d c1 = _mm256_loadu_pd(matrix.values + 4);
2689 __m256d c2 = _mm256_loadu_pd(matrix.values + 8);
2690 __m256d c3 = _mm256_loadu_pd(matrix.values + 12);
2691
2692 for (size_t n = 0; n < number; ++n)
2693 {
2694 __m256d v0 = _mm256_broadcast_sd(vectors[n].data() + 0);
2695 __m256d r0 = _mm256_mul_pd(c0, v0);
2696
2697 __m256d v1 = _mm256_broadcast_sd(vectors[n].data() + 1);
2698 __m256d r1 = _mm256_mul_pd(c1, v1);
2699
2700 r0 = _mm256_add_pd(r0, r1);
2701
2702 __m256d v2 = _mm256_broadcast_sd(vectors[n].data() + 2);
2703 __m256d r2 = _mm256_mul_pd(c2, v2);
2704
2705 r0 = _mm256_add_pd(r0, r2);
2706
2707 __m256d v3 = _mm256_broadcast_sd(vectors[n].data() + 3);
2708 __m256d r3 = _mm256_mul_pd(c3, v3);
2709
2710 r0 = _mm256_add_pd(r0, r3);
2711
2712 _mm256_storeu_pd(results[n].data(), r0);
2713 }
2714}
2715
2716#else // OCEAN_HARDWARE_AVX_VERSION
2717
2718#ifdef OCEAN_USE_SLOWER_IMPLEMENTATION
2719
2720// we keep following implementation inside 'OCEAN_USE_SLOWER_IMPLEMENTATION' showing an alternative (which is slower)
2721
2722#if defined(OCEAN_HARDWARE_SSE_VERSION) && OCEAN_HARDWARE_SSE_VERSION >= 41
2723
2724template <>
2725inline void SquareMatrixT4<float>::multiply(const SquareMatrixT4<float>& matrix, const VectorT4<float>* vectors, VectorT4<float>* results, const size_t number)
2726{
2727 // the following code uses the following SSE instructions, and needs SSE 4.1 or higher
2728
2729 // SSE:
2730 // _mm_loadu_ps
2731 // _mm_shuffle_ps
2732 // _mm_or_ps
2733 // _mm_storeu_ps
2734
2735 // SSE4.1:
2736 // _mm_dp_ps
2737
2738 // first we load the four rows of the matrix
2739 __m128 row0 = _mm_loadu_ps(matrix.values + 0);
2740 __m128 row1 = _mm_loadu_ps(matrix.values + 4);
2741 __m128 row2 = _mm_loadu_ps(matrix.values + 8);
2742 __m128 row3 = _mm_loadu_ps(matrix.values + 12);
2743
2744 // first, we transpose the 4x4 matrix
2745
2746 // A E I M A B C D
2747 // B F J N E F G H
2748 // C G K O I J K L
2749 // D H L P M N O P
2750
2751 // A B C D, E F G H -> A B E F
2752 __m128 temp0 = _mm_shuffle_ps(row0, row1, 0x44); // 0x44 = 0100 0100
2753 // A B C D, E F G H -> C D G H
2754 __m128 temp2 = _mm_shuffle_ps(row0, row1, 0xEE); // 0xEE = 1110 1110
2755 // I J K L, M N O P -> I J M N
2756 __m128 temp1 = _mm_shuffle_ps(row2, row3, 0x44);
2757 // I J K L, M N O P -> K L O P
2758 __m128 temp3 = _mm_shuffle_ps(row2, row3, 0xEE);
2759
2760 // A B E F, I J M N -> A E I M
2761 row0 = _mm_shuffle_ps(temp0, temp1, 0x88); // 0x88 = 1000 1000
2762 // A B E F, I J M N -> B F J N
2763 row1 = _mm_shuffle_ps(temp0, temp1, 0xDD); // 0xDD = 1101 1101
2764 // C D G H, K L O P -> C G K O
2765 row2 = _mm_shuffle_ps(temp2, temp3, 0x88);
2766 // C D G H, K L O P -> D H L P
2767 row3 = _mm_shuffle_ps(temp2, temp3, 0xDD);
2768
2769#ifdef OCEAN_COMPILER_MSC
2770 ocean_assert(row0.m128_f32[0] == matrix.values[0] && row0.m128_f32[1] == matrix.values[4] && row0.m128_f32[2] == matrix.values[ 8] && row0.m128_f32[3] == matrix.values[12]);
2771 ocean_assert(row1.m128_f32[0] == matrix.values[1] && row1.m128_f32[1] == matrix.values[5] && row1.m128_f32[2] == matrix.values[ 9] && row1.m128_f32[3] == matrix.values[13]);
2772 ocean_assert(row2.m128_f32[0] == matrix.values[2] && row2.m128_f32[1] == matrix.values[6] && row2.m128_f32[2] == matrix.values[10] && row2.m128_f32[3] == matrix.values[14]);
2773 ocean_assert(row3.m128_f32[0] == matrix.values[3] && row3.m128_f32[1] == matrix.values[7] && row3.m128_f32[2] == matrix.values[11] && row3.m128_f32[3] == matrix.values[15]);
2774#endif
2775
2776 for (size_t n = 0u; n < number; ++n)
2777 {
2778 // we load the four values of the vector
2779 __m128 v = _mm_loadu_ps(vectors[n].data());
2780
2781 // we determine the dot product between the first row and the vector and store the result in the first float bin
2782 __m128 dot0 = _mm_dp_ps(row0, v, 0xF1); // 0xF1 = 1111 0001
2783
2784 // we determine the dot product between the second row and the vector and store the result in the second float bin
2785 __m128 dot1 = _mm_dp_ps(row1, v, 0xF2); // 0xF2 = 1111 0010
2786 __m128 dot2 = _mm_dp_ps(row2, v, 0xF4); // 0xF4 = 1111 0100
2787 __m128 dot3 = _mm_dp_ps(row3, v, 0xF8); // 0xF8 = 1111 1000
2788
2789 // now we blend the results by applying the bit-wise or operator
2790 __m128 result01 = _mm_or_ps(dot0, dot1);
2791 __m128 result23 = _mm_or_ps(dot2, dot3);
2792 __m128 result03 = _mm_or_ps(result01, result23);
2793
2794 _mm_storeu_ps(results[n].data(), result03);
2795 }
2796}
2797
2798#endif // OCEAN_HARDWARE_SSE_VERSION >= 41
2799
2800#else // OCEAN_USE_SLOWER_IMPLEMENTATION
2801
2802#if defined(OCEAN_HARDWARE_SSE_VERSION) && OCEAN_HARDWARE_SSE_VERSION >= 20
2803
2804template <>
2805inline void SquareMatrixT4<double>::multiply(const SquareMatrixT4<double>& matrix, const VectorT4<double>* vectors, VectorT4<double>* results, const size_t number)
2806{
2807 // the following code uses the following SSE instructions, and needs SSE2 or higher
2808
2809 // SSE2:
2810 // _mm_load1_pd
2811 // _mm_loadu_pd
2812 // _mm_add_pd
2813 // _mm_storeu_pd
2814 // _mm_mul_pd
2815
2816 // now we load the four columns
2817 __m128d c0a = _mm_loadu_pd(matrix.values + 0);
2818 __m128d c0b = _mm_loadu_pd(matrix.values + 2);
2819 __m128d c1a = _mm_loadu_pd(matrix.values + 4);
2820 __m128d c1b = _mm_loadu_pd(matrix.values + 6);
2821 __m128d c2a = _mm_loadu_pd(matrix.values + 8);
2822 __m128d c2b = _mm_loadu_pd(matrix.values + 10);
2823 __m128d c3a = _mm_loadu_pd(matrix.values + 12);
2824 __m128d c3b = _mm_loadu_pd(matrix.values + 14);
2825
2826 for (size_t n = 0u; n < number; ++n)
2827 {
2828 __m128d v0 = _mm_load1_pd(vectors[n].data() + 0);
2829 __m128d v1 = _mm_load1_pd(vectors[n].data() + 1);
2830 __m128d v2 = _mm_load1_pd(vectors[n].data() + 2);
2831 __m128d v3 = _mm_load1_pd(vectors[n].data() + 3);
2832
2833 // first column
2834 __m128d r0a = _mm_mul_pd(c0a, v0);
2835 __m128d r0b = _mm_mul_pd(c0b, v0);
2836
2837 // second column
2838 __m128d r1a = _mm_mul_pd(c1a, v1);
2839 __m128d r1b = _mm_mul_pd(c1b, v1);
2840
2841 r0a = _mm_add_pd(r0a, r1a);
2842 r0b = _mm_add_pd(r0b, r1b);
2843
2844 // third column
2845 __m128d r2a = _mm_mul_pd(c2a, v2);
2846 __m128d r2b = _mm_mul_pd(c2b, v2);
2847 r0a = _mm_add_pd(r0a, r2a);
2848 r0b = _mm_add_pd(r0b, r2b);
2849
2850 // fourth column
2851 __m128d r3a = _mm_mul_pd(c3a, v3);
2852 __m128d r3b = _mm_mul_pd(c3b, v3);
2853 r0a = _mm_add_pd(r0a, r3a);
2854 r0b = _mm_add_pd(r0b, r3b);
2855
2856 _mm_storeu_pd(results[n].data() + 0, r0a);
2857 _mm_storeu_pd(results[n].data() + 2, r0b);
2858 }
2859}
2860
2861#endif // OCEAN_HARDWARE_SSE_VERSION >= 20
2862
2863#if defined(OCEAN_HARDWARE_SSE_VERSION) && OCEAN_HARDWARE_SSE_VERSION >= 10
2864
2865template <>
2866inline void SquareMatrixT4<float>::multiply(const SquareMatrixT4<float>& matrix, const VectorT4<float>* vectors, VectorT4<float>* results, const size_t number)
2867{
2868 // the following code uses the following SSE instructions, and needs SSE1 or higher
2869
2870 // SSE1:
2871 // _mm_load1_ps
2872 // _mm_loadu_ps
2873 // _mm_mul_ps
2874 // _mm_add_ps
2875 // _mm_storeu_ps
2876
2877 // now we load the four columns
2878 __m128 c0 = _mm_loadu_ps(matrix.values + 0);
2879 __m128 c1 = _mm_loadu_ps(matrix.values + 4);
2880 __m128 c2 = _mm_loadu_ps(matrix.values + 8);
2881 __m128 c3 = _mm_loadu_ps(matrix.values + 12);
2882
2883 for (size_t n = 0u; n < number; ++n)
2884 {
2885 __m128 v0 = _mm_load1_ps(vectors[n].data() + 0);
2886 __m128 v1 = _mm_load1_ps(vectors[n].data() + 1);
2887 __m128 v2 = _mm_load1_ps(vectors[n].data() + 2);
2888 __m128 v3 = _mm_load1_ps(vectors[n].data() + 3);
2889
2890 // first column
2891 __m128 r0 = _mm_mul_ps(c0, v0);
2892
2893 // second column
2894 __m128 r1 = _mm_mul_ps(c1, v1);
2895
2896 r0 = _mm_add_ps(r0, r1);
2897
2898 // third column
2899 __m128 r2 = _mm_mul_ps(c2, v2);
2900 r0 = _mm_add_ps(r0, r2);
2901
2902 // fourth column
2903 __m128 r3 = _mm_mul_ps(c3, v3);
2904 r0 = _mm_add_ps(r0, r3);
2905
2906 _mm_storeu_ps(results[n].data(), r0);
2907 }
2908}
2909
2910#endif // OCEAN_HARDWARE_SSE_VERSION >= 10
2911
2912#endif // OCEAN_USE_SLOWER_IMPLEMENTATION
2913
2914#endif // OCEAN_HARDWARE_AVX_VERSION
2915
2916#if defined(OCEAN_HARDWARE_NEON_VERSION) && OCEAN_HARDWARE_NEON_VERSION >= 10
2917
2918#ifdef __aarch64__
2919
2920template <>
2921inline void SquareMatrixT4<double>::multiply(const SquareMatrixT4<double>& matrix, const VectorT4<double>* vectors, VectorT4<double>* results, const size_t number)
2922{
2923 // the following NEON code is almost identical to the SSE implementation
2924
2925 // now we load the four columns
2926 float64x2_t c0a = vld1q_f64(matrix.values + 0);
2927 float64x2_t c0b = vld1q_f64(matrix.values + 2);
2928 float64x2_t c1a = vld1q_f64(matrix.values + 4);
2929 float64x2_t c1b = vld1q_f64(matrix.values + 6);
2930 float64x2_t c2a = vld1q_f64(matrix.values + 8);
2931 float64x2_t c2b = vld1q_f64(matrix.values + 10);
2932 float64x2_t c3a = vld1q_f64(matrix.values + 12);
2933 float64x2_t c3b = vld1q_f64(matrix.values + 14);
2934
2935 for (size_t n = 0u; n < number; ++n)
2936 {
2937 float64x2_t v0 = vld1q_dup_f64(vectors[n].data() + 0);
2938 float64x2_t v1 = vld1q_dup_f64(vectors[n].data() + 1);
2939 float64x2_t v2 = vld1q_dup_f64(vectors[n].data() + 2);
2940 float64x2_t v3 = vld1q_dup_f64(vectors[n].data() + 3);
2941
2942 float64x2_t r0a = vmulq_f64(c0a, v0);
2943 float64x2_t r0b = vmulq_f64(c0b, v0);
2944
2945 float64x2_t r1a = vmulq_f64(c1a, v1);
2946 float64x2_t r1b = vmulq_f64(c1b, v1);
2947
2948 r0a = vaddq_f64(r0a, r1a);
2949 r0b = vaddq_f64(r0b, r1b);
2950
2951 float64x2_t r2a = vmulq_f64(c2a, v2);
2952 float64x2_t r2b = vmulq_f64(c2b, v2);
2953
2954 r0a = vaddq_f64(r0a, r2a);
2955 r0b = vaddq_f64(r0b, r2b);
2956
2957 float64x2_t r3a = vmulq_f64(c3a, v3);
2958 float64x2_t r3b = vmulq_f64(c3b, v3);
2959
2960 r0a = vaddq_f64(r0a, r3a);
2961 r0b = vaddq_f64(r0b, r3b);
2962
2963 vst1q_f64(results[n].data() + 0, r0a);
2964 vst1q_f64(results[n].data() + 2, r0b);
2965 }
2966}
2967
2968#endif // __aarch64__
2969
2970template <>
2971inline void SquareMatrixT4<float>::multiply(const SquareMatrixT4<float>& matrix, const VectorT4<float>* vectors, VectorT4<float>* results, const size_t number)
2972{
2973 // the following NEON code is almost identical to the SSE implementation
2974
2975 // now we load the four columns
2976 float32x4_t c0 = vld1q_f32(matrix.values + 0);
2977 float32x4_t c1 = vld1q_f32(matrix.values + 4);
2978 float32x4_t c2 = vld1q_f32(matrix.values + 8);
2979 float32x4_t c3 = vld1q_f32(matrix.values + 12);
2980
2981 for (size_t n = 0u; n < number; ++n)
2982 {
2983 float32x4_t v0 = vld1q_dup_f32(vectors[n].data() + 0);
2984 float32x4_t v1 = vld1q_dup_f32(vectors[n].data() + 1);
2985 float32x4_t v2 = vld1q_dup_f32(vectors[n].data() + 2);
2986 float32x4_t v3 = vld1q_dup_f32(vectors[n].data() + 3);
2987
2988 // first column
2989 float32x4_t r0 = vmulq_f32(c0, v0);
2990
2991 // second column
2992 float32x4_t r1 = vmulq_f32(c1, v1);
2993
2994 r0 = vaddq_f32(r0, r1);
2995
2996 // third column
2997 float32x4_t r2 = vmulq_f32(c2, v2);
2998
2999 r0 = vaddq_f32(r0, r2);
3000
3001 // fourth column
3002 float32x4_t r3 = vmulq_f32(c3, v3);
3003
3004 r0 = vaddq_f32(r0, r3);
3005
3006 vst1q_f32(results[n].data(), r0);
3007 }
3008}
3009
3010#endif
3011
3012template <typename T>
3013template <typename U>
3014inline std::vector< SquareMatrixT4<T> > SquareMatrixT4<T>::matrices2matrices(const std::vector< SquareMatrixT4<U> >& matrices)
3015{
3016 std::vector< SquareMatrixT4<T> > result;
3017 result.reserve(matrices.size());
3018
3019 for (typename std::vector< SquareMatrixT4<U> >::const_iterator i = matrices.begin(); i != matrices.end(); ++i)
3020 {
3021 result.push_back(SquareMatrixT4<T>(*i));
3022 }
3023
3024 return result;
3025}
3026
3027template <>
3028template <>
3029inline std::vector< SquareMatrixT4<float> > SquareMatrixT4<float>::matrices2matrices(const std::vector< SquareMatrixT4<float> >& matrices)
3030{
3031 return matrices;
3032}
3033
3034template <>
3035template <>
3036inline std::vector< SquareMatrixT4<double> > SquareMatrixT4<double>::matrices2matrices(const std::vector< SquareMatrixT4<double> >& matrices)
3037{
3038 return matrices;
3039}
3040
3041template <typename T>
3042template <typename U>
3043inline std::vector< SquareMatrixT4<T> > SquareMatrixT4<T>::matrices2matrices(const SquareMatrixT4<U>* matrices, const size_t size)
3044{
3045 std::vector< SquareMatrixT4<T> > result;
3046 result.reserve(size);
3047
3048 for (size_t n = 0; n < size; ++n)
3049 {
3050 result.push_back(SquareMatrixT4<T>(matrices[n]));
3051 }
3052
3053 return result;
3054}
3055
3056template <typename T>
3057void SquareMatrixT4<T>::swapRows(const unsigned int row0, const unsigned int row1)
3058{
3059 ocean_assert(row0 < 4u && row1 < 4u);
3060
3061 if (row0 == row1)
3062 {
3063 return;
3064 }
3065
3066 T* first = values + row0;
3067 T* second = values + row1;
3068
3069 T tmp = *first;
3070 *first = *second;
3071 *second = tmp;
3072
3073 first += 4;
3074 second += 4;
3075 tmp = *first;
3076 *first = *second;
3077 *second = tmp;
3078
3079 first += 4;
3080 second += 4;
3081 tmp = *first;
3082 *first = *second;
3083 *second = tmp;
3084
3085 first += 4;
3086 second += 4;
3087 tmp = *first;
3088 *first = *second;
3089 *second = tmp;
3090}
3091
3092template <typename T>
3093void SquareMatrixT4<T>::multiplyRow(const unsigned int row, const T scalar)
3094{
3095 ocean_assert(row < 4u);
3096
3097 T* element = values + row;
3098
3099 *element *= scalar;
3100 element += 4;
3101 *element *= scalar;
3102 element += 4;
3103 *element *= scalar;
3104 element += 4;
3105 *element *= scalar;
3106}
3107
3108template <typename T>
3109void SquareMatrixT4<T>::addRows(const unsigned int targetRow, unsigned int const sourceRow, const T scalar)
3110{
3111 ocean_assert(targetRow < 4u && sourceRow < 4u);
3112 ocean_assert(targetRow != sourceRow);
3113
3114 T* target = values + targetRow;
3115 T* source = values + sourceRow;
3116
3117 *target += *source * scalar;
3118
3119 target += 4;
3120 source += 4;
3121 *target += *source * scalar;
3122
3123 target += 4;
3124 source += 4;
3125 *target += *source * scalar;
3126
3127 target += 4;
3128 source += 4;
3129 *target += *source * scalar;
3130}
3131
3132template <typename T>
3133std::ostream& operator<<(std::ostream& stream, const SquareMatrixT4<T>& matrix)
3134{
3135 stream << "|" << matrix(0, 0) << ", " << matrix(0, 1) << ", " << matrix(0, 2) << ", " << matrix(0, 3) << "|" << std::endl;
3136 stream << "|" << matrix(1, 0) << ", " << matrix(1, 1) << ", " << matrix(1, 2) << ", " << matrix(1, 3) << "|" << std::endl;
3137 stream << "|" << matrix(2, 0) << ", " << matrix(2, 1) << ", " << matrix(2, 2) << ", " << matrix(2, 3) << "|" << std::endl;
3138 stream << "|" << matrix(3, 0) << ", " << matrix(3, 1) << ", " << matrix(3, 2) << ", " << matrix(3, 3) << "|";
3139
3140 return stream;
3141}
3142
3143template <bool tActive, typename T>
3144MessageObject<tActive>& operator<<(MessageObject<tActive>& messageObject, const SquareMatrixT4<T>& matrix)
3145{
3146 return messageObject << "|" << matrix(0, 0) << ", " << matrix(0, 1) << ", " << matrix(0, 2) << ", " << matrix(0, 3) << "|\n|"
3147 << matrix(1, 0) << ", " << matrix(1, 1) << ", " << matrix(1, 2) << ", " << matrix(1, 3) << "|\n|"
3148 << matrix(2, 0) << ", " << matrix(2, 1) << ", " << matrix(2, 2) << ", " << matrix(2, 3) << "|\n|"
3149 << matrix(3, 0) << ", " << matrix(3, 1) << ", " << matrix(3, 2) << ", " << matrix(3, 3) << "|";
3150}
3151
3152template <bool tActive, typename T>
3153MessageObject<tActive>& operator<<(MessageObject<tActive>&& messageObject, const SquareMatrixT4<T>& matrix)
3154{
3155 return messageObject << "|" << matrix(0, 0) << ", " << matrix(0, 1) << ", " << matrix(0, 2) << ", " << matrix(0, 3) << "|\n|"
3156 << matrix(1, 0) << ", " << matrix(1, 1) << ", " << matrix(1, 2) << ", " << matrix(1, 3) << "|\n|"
3157 << matrix(2, 0) << ", " << matrix(2, 1) << ", " << matrix(2, 2) << ", " << matrix(2, 3) << "|\n|"
3158 << matrix(3, 0) << ", " << matrix(3, 1) << ", " << matrix(3, 2) << ", " << matrix(3, 3) << "|";
3159}
3160
3161}
3162
3163#endif // META_OCEAN_MATH_SQUARE_MATRIX_4_H
This class implements the abstract base class for all AnyCamera objects.
Definition AnyCamera.h:131
virtual unsigned int width() const =0
Returns the width of the camera image.
virtual T focalLengthX() const =0
Returns the horizontal focal length parameter.
virtual T focalLengthY() const =0
Returns the vertical focal length parameter.
virtual unsigned int height() const =0
Returns the height of the camera image.
virtual T principalPointY() const =0
Returns the y-value of the principal point of the camera image in the pixel domain.
virtual bool isValid() const =0
Returns whether this camera is valid.
virtual T principalPointX() const =0
Returns the x-value of the principal point of the camera image in the pixel domain.
static unsigned int solveQuartic(const T a, const T b, const T c, const T d, const T e, T *x, const bool refine=true)
Solves a quartic equation with the form:
Definition Equation.h:316
This class implements a 4x4 homogeneous transformation matrix using floating point values with the pr...
Definition HomogenousMatrix4.h:110
HomogenousMatrixT4< T > inverted() const noexcept
Returns the inverted of this matrix.
Definition HomogenousMatrix4.h:1569
VectorT3< T > translation() const
Returns the translation of the transformation.
Definition HomogenousMatrix4.h:1375
bool isValid() const
Returns whether this matrix is a valid homogeneous transformation.
Definition HomogenousMatrix4.h:1800
This class provides basic numeric functionalities.
Definition Numeric.h:57
static constexpr T weakEps()
Returns a weak epsilon.
static T atan(const T value)
Returns the arctangent of a given value.
Definition Numeric.h:1620
static T abs(const T value)
Returns the absolute value of a given value.
Definition Numeric.h:1220
static constexpr T eps()
Returns a small epsilon.
static bool isEqual(const T first, const T second)
Returns whether two values are equal up to a small epsilon.
Definition Numeric.h:2395
static T tan(const T value)
Returns the tangent of a given value.
Definition Numeric.h:1604
static constexpr bool isEqualEps(const T value)
Returns whether a value is smaller than or equal to a small epsilon.
Definition Numeric.h:2096
static constexpr T maxValue()
Returns the max scalar value.
Definition Numeric.h:3253
This class implements a 3x3 square matrix.
Definition SquareMatrix3.h:89
OCEAN_FORCE_INLINE SquareMatrixT4< T > & operator*=(const SquareMatrixT4< T > &matrix)
Multiplies and assigns two matrices.
Definition SquareMatrix4.h:1339
void copyElements(U *arrayValues) const
Copies the elements of this matrix to an array with floating point values of type U.
Definition SquareMatrix4.h:1314
SquareMatrixT4< T > & operator-=(const SquareMatrixT4< T > &matrix)
Subtracts and assigns two matrices.
Definition SquareMatrix4.h:1463
const T * data() const
Returns a pointer to the internal values.
Definition SquareMatrix4.h:750
T operator()(const unsigned int index) const
Element operator.
Definition SquareMatrix4.h:1381
SquareMatrixT4(const U *arrayValues, const bool valuesRowAligned)
Creates a new SquareMatrixT4 object by an array of at least sixteen elements of float type U.
Definition SquareMatrix4.h:644
static SquareMatrixT4< T > projectionMatrix(const T fovX, const T aspectRatio, const T nearDistance, const T farDistance)
Creates a projection matrix defined by the horizontal field of view, the aspect ratio and the near an...
Definition SquareMatrix4.h:2491
OCEAN_FORCE_INLINE VectorT3< T > operator*(const VectorT3< T > &vector) const
Multiply operator for a 3D vector.
Definition SquareMatrix4.h:1877
bool isIdentity() const
Returns whether this matrix is the identity matrix.
Definition SquareMatrix4.h:970
SquareMatrixT4(const U *arrayValues)
Creates a new SquareMatrixT4 object by an array of at least sixteen elements of float type U.
Definition SquareMatrix4.h:625
SquareMatrixT4(const SquareMatrixT4< U > &matrix)
Copy constructor for a matrix with difference element data type than T.
Definition SquareMatrix4.h:602
void transpose()
Transposes the matrix.
Definition SquareMatrix4.h:788
static SquareMatrixT4< T > projectionMatrix(const AnyCameraT< T > &anyCamera, const T nearDistance, const T farDistance)
Creates a projection matrix defined by a camera profile of a pinhole camera and the near and far clip...
Definition SquareMatrix4.h:2536
SquareMatrixT4< T > transposed() const
Returns the transposed of this matrix.
Definition SquareMatrix4.h:762
void swapRows(const unsigned int row0, const unsigned int row1)
Swaps two rows of this matrix.
Definition SquareMatrix4.h:3057
void copyElements(T *arrayValues) const
Copies the elements of this matrix to an array with floating point values.
Definition SquareMatrix4.h:1325
SquareMatrixT4< T > inverted() const
Returns the inverted matrix of this matrix.
Definition SquareMatrix4.h:812
bool invert(SquareMatrixT4< T > &invertedMatrix) const
Inverts the matrix and returns the result.
Definition SquareMatrix4.h:841
SquareMatrixT4< T > operator+(const SquareMatrixT4< T > &matrix) const
Adds two matrices.
Definition SquareMatrix4.h:1432
T operator[](const unsigned int index) const
Element operator.
Definition SquareMatrix4.h:1353
bool isSingular() const
Returns whether this matrix is singular (and thus cannot be inverted).
Definition SquareMatrix4.h:988
SquareMatrixT4()
Creates a new SquareMatrixT4 object with undefined elements.
Definition SquareMatrix4.h:595
SquareMatrixT4(const VectorT4< T > &diagonal)
Creates a new SquareMatrixT4 object by a given diagonal vector.
Definition SquareMatrix4.h:729
OCEAN_FORCE_INLINE SquareMatrixT4< T > operator*(const HomogenousMatrixT4< T > &matrix) const
Multiplies two matrices.
Definition SquareMatrix4.h:1849
friend class SquareMatrixT4
Definition SquareMatrix4.h:86
bool isSymmetric(const T epsilon=NumericT< T >::eps()) const
Returns whether this matrix is symmetric.
Definition SquareMatrix4.h:994
T values[16]
The sixteen values of the matrix.
Definition SquareMatrix4.h:591
T determinant() const
Returns the determinant of the matrix.
Definition SquareMatrix4.h:906
T * data()
Returns a pointer to the internal values.
Definition SquareMatrix4.h:756
T operator()(const unsigned int row, const unsigned int column) const
Element operator.
Definition SquareMatrix4.h:1367
bool isEqual(const SquareMatrixT4< T > &matrix, const T eps=NumericT< T >::eps()) const
Returns whether two matrices are almost identical up to a specified epsilon.
Definition SquareMatrix4.h:1003
void toNull()
Sets the matrix to a zero matrix.
Definition SquareMatrix4.h:961
bool operator==(const SquareMatrixT4< T > &matrix) const
Returns whether two matrices are identical up to a small epsilon.
Definition SquareMatrix4.h:1426
static size_t elements()
Returns the number of elements this matrix has.
Definition SquareMatrix4.h:1420
SquareMatrixT4< T > & operator+=(const SquareMatrixT4< T > &matrix)
Adds and assigns two matrices.
Definition SquareMatrix4.h:1442
const T * operator()() const
Access operator.
Definition SquareMatrix4.h:1395
SquareMatrixT4< T > & operator=(const SquareMatrixT4< T > &)=default
Default assign operator.
SquareMatrixT4(const T *arrayValues, const bool valuesRowAligned)
Creates a new SquareMatrixT4 object by an array of at least sixteen elements.
Definition SquareMatrix4.h:677
static void multiply(const SquareMatrixT4< T > &matrix, const VectorT4< T > *vectors, VectorT4< T > *results, const size_t number)
Multiplies several 4D vectors with a given matrix.
Definition SquareMatrix4.h:2661
SquareMatrixT4< T > operator-() const
Returns the negative matrix of this matrix (all matrix elements are multiplied by -1).
Definition SquareMatrix4.h:1474
bool isNull() const
Returns whether this matrix is a null matrix.
Definition SquareMatrix4.h:979
OCEAN_FORCE_INLINE SquareMatrixT4< T > & operator*=(const HomogenousMatrixT4< T > &matrix)
Multiplies and assigns two matrices.
Definition SquareMatrix4.h:1346
T Type
Definition of the used data type.
Definition SquareMatrix4.h:93
SquareMatrixT4(const SquareMatrixT3< T > &subMatrix)
Creates a new SquareMatrixT4 object by given 3x3 sub matrix.
Definition SquareMatrix4.h:713
SquareMatrixT4< T > operator-(const SquareMatrixT4< T > &matrix) const
Subtracts two matrices.
Definition SquareMatrix4.h:1453
void addRows(const unsigned int targetRow, const unsigned int sourceRow, const T scalar)
Multiplies elements from a specific row with a scalar and adds them to another row.
Definition SquareMatrix4.h:3109
static std::vector< SquareMatrixT4< T > > matrices2matrices(const std::vector< SquareMatrixT4< U > > &matrices)
Converts matrices with specific data type to matrices with different data type.
Definition SquareMatrix4.h:3014
static SquareMatrixT4< T > frustumMatrix(const T left, const T right, const T top, const T bottom, const T nearDistance, const T farDistance)
Creates a projection matrix defined by an asymmetric viewing frustum.
Definition SquareMatrix4.h:2593
T & operator()(const unsigned int index)
Element operator.
Definition SquareMatrix4.h:1388
SquareMatrixT4< T > operator*(const T value) const
Multiplies this matrix with a scalar value.
Definition SquareMatrix4.h:2443
bool invert()
Inverts this matrix in place.
Definition SquareMatrix4.h:826
bool operator!=(const SquareMatrixT4< T > &matrix) const
Returns whether two matrices are not identical up to a small epsilon.
Definition SquareMatrix4.h:1333
size_t operator()(const SquareMatrixT4< T > &matrix) const
Hash function.
Definition SquareMatrix4.h:1407
static std::vector< SquareMatrixT4< T > > matrices2matrices(const SquareMatrixT4< U > *matrices, const size_t size)
Converts matrices with specific data type to matrices with different data type.
Definition SquareMatrix4.h:3043
OCEAN_FORCE_INLINE SquareMatrixT4< T > operator*(const SquareMatrixT4< T > &matrix) const
Multiplies two matrices.
Definition SquareMatrix4.h:1487
T & operator()(const unsigned int row, const unsigned int column)
Element operator.
Definition SquareMatrix4.h:1374
T & operator[](const unsigned int index)
Element operator.
Definition SquareMatrix4.h:1360
bool eigenSystem(T *eigenValues, VectorT4< T > *eigenVectors) const
Performs an eigen value analysis.
Definition SquareMatrix4.h:1016
void multiplyRow(const unsigned int row, const T scalar)
Multiplies a row with a scalar value.
Definition SquareMatrix4.h:3093
T trace() const
Returns the trace of the matrix which is the sum of the diagonal elements.
Definition SquareMatrix4.h:934
SquareMatrixT4(const bool setToIdentity)
Creates a new SquareMatrixT4 object.
Definition SquareMatrix4.h:611
SquareMatrixT4(const HomogenousMatrixT4< T > &transformation)
Creates a new SquareMatrixT4 object by given transformation matrix.
Definition SquareMatrix4.h:707
OCEAN_FORCE_INLINE VectorT4< T > operator*(const VectorT4< T > &vector) const
Multiply operator for a 4D vector.
Definition SquareMatrix4.h:1890
T * operator()()
Access operator.
Definition SquareMatrix4.h:1401
static SquareMatrixT4< T > frustumMatrix(const T width, const T height, const HomogenousMatrixT4< T > &viewingMatrix, const T nearDistance, const T farDistance)
Creates a project matrix defined by an asymmetric viewing frustum.
Definition SquareMatrix4.h:2639
SquareMatrixT4< T > & operator*=(const T value)
Multiplies and assigns this matrix with a scalar value.
Definition SquareMatrix4.h:2468
void toIdentity()
Sets the matrix to the identity matrix.
Definition SquareMatrix4.h:940
SquareMatrixT4(const SquareMatrixT4< T > &matrix)=default
Copy constructor.
SquareMatrixT4(const T *arrayValues)
Creates a new SquareMatrixT4 object by an array of at least sixteen elements.
Definition SquareMatrix4.h:636
static void sortHighestToFront4(T &value0, T &value1, T &value2, T &value3)
Sorts four values so that the highest value will finally be the first value.
Definition base/Utilities.h:758
This class implements a vector with three elements.
Definition Vector3.h:97
const T & y() const noexcept
Returns the y value.
Definition Vector3.h:816
const T & x() const noexcept
Returns the x value.
Definition Vector3.h:804
This class implements a vector with four elements.
Definition Vector4.h:97
const T * data() const noexcept
Returns an pointer to the vector elements.
Definition Vector4.h:723
bool normalize()
Normalizes this vector.
Definition Vector4.h:596
std::vector< SquareMatrix4 > SquareMatrices4
Definition of a vector holding SquareMatrix4 objects.
Definition SquareMatrix4.h:68
std::vector< SquareMatrixT4< T > > SquareMatricesT4
Definition of a typename alias for vectors with SquareMatrixT4 objects.
Definition SquareMatrix4.h:61
The namespace covering the entire Ocean framework.
Definition Accessor.h:15
std::ostream & operator<<(std::ostream &stream, const HighPerformanceStatistic &highPerformanceStatistic)
Definition HighPerformanceTimer.h:963