jaulib v1.3.8
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
mat4f.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright Gothel Software e.K.
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * This Source Code Form is subject to the terms of the MIT License
8 * If a copy of the MIT was not distributed with this file,
9 * you can obtain one at https://opensource.org/license/mit/.
10 */
11
12#ifndef JAU_MATH_MAT4f_HPP_
13#define JAU_MATH_MAT4f_HPP_
14
15#include <cmath>
16#include <cstdarg>
17#include <cassert>
18#include <limits>
19#include <string>
20#include <vector>
21#include <initializer_list>
22#include <iostream>
23
24#include <jau/debug.hpp>
25#include <jau/float_math.hpp>
27#include <jau/math/vec3f.hpp>
28#include <jau/math/vec4f.hpp>
29#include <jau/math/recti.hpp>
31#include <jau/int_types.hpp>
32
33namespace jau::math::geom {
34 class Frustum; // forward
35}
36
37namespace jau::math {
38
39 /** \addtogroup Math
40 *
41 * @{
42 */
43
44 template<typename Value_type,
45 std::enable_if_t<std::is_floating_point_v<Value_type>, bool>>
46 class Quaternion; // forward
47
48/**
49 * Basic 4x4 value_type matrix implementation using fields for intensive use-cases (host operations).
50 * <p>
51 * Implementation covers {@link FloatUtil} matrix functionality, exposed in an object oriented manner.
52 * </p>
53 * <p>
54 * Unlike {@link com.jogamp.math.util.PMVmat4f PMVmat4f}, this class only represents one single matrix.
55 * </p>
56 * <p>
57 * For array operations the layout is expected in column-major order
58 * matching OpenGL's implementation, illustration:
59 * <pre>
60 Row-Major Column-Major (OpenGL):
61
62 | 0 1 2 tx |
63 | |
64 | 4 5 6 ty |
65 M = | |
66 | 8 9 10 tz |
67 | |
68 | 12 13 14 15 |
69
70 R C R C
71 m[0*4+3] = tx; m[0+4*3] = tx;
72 m[1*4+3] = ty; m[1+4*3] = ty;
73 m[2*4+3] = tz; m[2+4*3] = tz;
74
75 RC (std subscript order) RC (std subscript order)
76 m03 = tx; m03 = tx;
77 m13 = ty; m13 = ty;
78 m23 = tz; m23 = tz;
79
80 * </pre>
81 * </p>
82 * <p>
83 * <ul>
84 * <li><a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html">Matrix-FAQ</a></li>
85 * <li><a href="https://en.wikipedia.org/wiki/Matrix_%28mathematics%29">Wikipedia-Matrix</a></li>
86 * <li><a href="http://www.euclideanspace.com/maths/algebra/matrix/index.htm">euclideanspace.com-Matrix</a></li>
87 * </ul>
88 * </p>
89 * <p>
90 * Implementation utilizes unrolling of small vertices and matrices wherever possible
91 * while trying to access memory in a linear fashion for performance reasons, see:
92 * <ul>
93 * <li><a href="https://lessthanoptimal.github.io/Java-Matrix-Benchmark/">java-matrix-benchmark</a></li>
94 * <li><a href="https://github.com/lessthanoptimal/ejml">EJML Efficient Java Matrix Library</a></li>
95 * </ul>
96 * </p>
97 */
98
99template<typename Value_type,
100 std::enable_if_t<std::is_floating_point_v<Value_type>, bool> = true>
101class alignas(Value_type) Matrix4 {
102 public:
105 typedef const value_type* const_pointer;
110
114
115 constexpr static const value_type zero = value_type(0);
116 constexpr static const value_type one = value_type(1);
117 constexpr static const value_type two = value_type(2);
118 constexpr static const value_type half = one/two;
119
120 /**
121 * Inversion Epsilon, used with equals method to determine if two inverted matrices are close enough to be considered equal.
122 * <p>
123 * Using {@value}, which is ~84 times `std::numeric_limits<value_type>::epsilon()`.
124 * </p>
125 */
126 constexpr static const value_type inv_deviation = value_type(84) * std::numeric_limits<value_type>::epsilon(); // 84 * EPSILON(1.1920929E-7f) = 1.0E-5f
127
128 private:
129 // RC
130 value_type m00, m10, m20, m30; // column 0
131 value_type m01, m11, m21, m31; // column 1
132 value_type m02, m12, m22, m32; // column 2
133 value_type m03, m13, m23, m33; // column 3
134
135 friend geom::Frustum;
137
138 public:
139
140 /**
141 * Creates a new identity matrix.
142 */
143 constexpr Matrix4() noexcept
144 : m00(one), m10(zero), m20(zero), m30(zero),
145 m01(zero), m11(one), m21(zero), m31(zero),
146 m02(zero), m12(zero), m22(one), m32(zero),
147 m03(zero), m13(zero), m23(zero), m33(one)
148 { }
149
150 /**
151 * Creates a new matrix based on given value_type[4*4] column major order.
152 * @param m 4x4 matrix in column-major order
153 */
154 constexpr Matrix4(const_iterator m) noexcept
155 : m00(*m), m10(*(++m)), m20(*(++m)), m30(*(++m)), // column 0
156 m01(*(++m)), m11(*(++m)), m21(*(++m)), m31(*(++m)), // column 1
157 m02(*(++m)), m12(*(++m)), m22(*(++m)), m32(*(++m)), // column 2
158 m03(*(++m)), m13(*(++m)), m23(*(++m)), m33(*(++m)) // column 3
159 {}
160
161 /**
162 * Creates a new matrix based on given value_type initializer list in column major order.
163 * @param m source initializer list value_type data to be copied into this new instance, implied size must be >= 16
164 */
165 constexpr Matrix4(std::initializer_list<value_type> m) noexcept
166 : Matrix4( m.begin() )
167 {
168 assert(m.size() >= 16 );
169 }
170
171 /**
172 * Creates a new matrix copying the values of the given {@code src} matrix.
173 */
174 constexpr Matrix4(const Matrix4& o) noexcept
175 : Matrix4( o.cbegin() )
176 { }
177
178 /**
179 * Copy assignment using the the values of the given {@code src} matrix.
180 */
181 constexpr Matrix4& operator=(const Matrix4& o) noexcept { return load(o); }
182
183 constexpr bool equals(const Matrix4& o, const value_type epsilon=std::numeric_limits<value_type>::epsilon()) const noexcept {
184 if( this == &o ) {
185 return true;
186 } else {
187 return jau::equals(m00, o.m00, epsilon) &&
188 jau::equals(m01, o.m01, epsilon) &&
189 jau::equals(m02, o.m02, epsilon) &&
190 jau::equals(m03, o.m03, epsilon) &&
191 jau::equals(m10, o.m10, epsilon) &&
192 jau::equals(m11, o.m11, epsilon) &&
193 jau::equals(m12, o.m12, epsilon) &&
194 jau::equals(m13, o.m13, epsilon) &&
195 jau::equals(m20, o.m20, epsilon) &&
196 jau::equals(m21, o.m21, epsilon) &&
197 jau::equals(m22, o.m22, epsilon) &&
198 jau::equals(m23, o.m23, epsilon) &&
199 jau::equals(m30, o.m30, epsilon) &&
200 jau::equals(m31, o.m31, epsilon) &&
201 jau::equals(m32, o.m32, epsilon) &&
202 jau::equals(m33, o.m33, epsilon);
203 }
204 }
205 constexpr bool operator==(const Matrix4& rhs) const noexcept { return equals(rhs); }
206
207 //
208 // Write to Matrix via set(..) or load(..)
209 //
210
211 /**
212 * Returns writable reference to the {@code i}th component of this column-major order matrix, 0 <= i < 16 w/o boundary check
213 */
214 constexpr reference operator[](size_t i) noexcept {
215 assert( i < 16 );
216 return (&m00)[i];
217 }
218
219 /** Sets the {@code i}th component of this column-major order matrix with value_type {@code v}, 0 <= i < 16 w/o boundary check*/
220 constexpr void set(const jau::nsize_t i, const value_type v) noexcept {
221 assert( i < 16 );
222 (&m00)[i] = v;
223 }
224
225 explicit operator pointer() noexcept { return &m00; }
226 constexpr iterator begin() noexcept { return &m00; }
227
228 /**
229 * Set this matrix to identity.
230 * <pre>
231 Translation matrix (Column Order):
232 1 0 0 0
233 0 1 0 0
234 0 0 1 0
235 0 0 0 1
236 * </pre>
237 * @return this matrix for chaining
238 */
239 constexpr Matrix4& loadIdentity() noexcept {
240 m00 = m11 = m22 = m33 = one;
241 m01 = m02 = m03 =
242 m10 = m12 = m13 =
243 m20 = m21 = m23 =
244 m30 = m31 = m32 = zero;
245 return *this;
246 }
247
248 /**
249 * Load the values of the given matrix {@code src} to this matrix w/o boundary check.
250 * @param src 4x4 matrix value_type[16] in column-major order
251 * @return this matrix for chaining
252 */
253 constexpr Matrix4& load(const_iterator src) noexcept {
254 // RC
255 m00 = *src; // column 0
256 m10 = *(++src);
257 m20 = *(++src);
258 m30 = *(++src);
259 m01 = *(++src); // column 1
260 m11 = *(++src);
261 m21 = *(++src);
262 m31 = *(++src);
263 m02 = *(++src); // column 2
264 m12 = *(++src);
265 m22 = *(++src);
266 m32 = *(++src);
267 m03 = *(++src); // column 3
268 m13 = *(++src);
269 m23 = *(++src);
270 m33 = *(++src);
271 return *this;
272 }
273 /**
274 * Load the values of the given matrix {@code src} to this matrix w/o boundary check
275 * @param src the source values
276 * @return this matrix for chaining
277 */
278 constexpr Matrix4& load(const Matrix4& src) noexcept {
279 return load( src.cbegin() );
280 }
281
282 //
283 // Read out Matrix via get(..)
284 //
285
286 /**
287 * Returns read-only {@code i}th component of the given column-major order matrix, 0 <= i < 16 w/o boundary check
288 */
289 constexpr value_type operator[](size_t i) const noexcept {
290 assert( i < 16 );
291 return (&m00)[i];
292 }
293
294 /** Returns the {@code i}th component of the given column-major order matrix, 0 <= i < 16, w/o boundary check */
295 constexpr value_type get(const jau::nsize_t i) const noexcept {
296 assert( i < 16 );
297 return (&m00)[i];
298 }
299
300 explicit operator const_pointer() const noexcept { return &m00; }
301 constexpr const_iterator cbegin() const noexcept { return &m00; }
302
303 /**
304 * Get the named column of the given column-major matrix to v_out w/o boundary check.
305 * @param column named column to copy
306 * @param v_out the column-vector storage
307 * @return given result vector <i>v_out</i> for chaining
308 */
309 constexpr Vec4& getColumn(const jau::nsize_t column, Vec4& v_out) const noexcept {
310 assert( column < 4 );
311 return v_out.set( get(0+column*4),
312 get(1+column*4),
313 get(2+column*4),
314 get(3+column*4) );
315 }
316
317 /**
318 * Get the named column of the given column-major matrix to v_out w/o boundary check.
319 * @param column named column to copy
320 * @return result vector holding the requested column
321 */
322 constexpr Vec4 getColumn(const jau::nsize_t column) const noexcept {
323 assert( column < 4 );
324 return Vec4( get(0+column*4),
325 get(1+column*4),
326 get(2+column*4),
327 get(3+column*4) );
328 }
329
330 /**
331 * Get the named column of the given column-major matrix to v_out w/o boundary check.
332 * @param column named column to copy
333 * @param v_out the column-vector storage
334 * @return given result vector <i>v_out</i> for chaining
335 */
336 constexpr Vec3& getColumn(const jau::nsize_t column, Vec3& v_out) const noexcept {
337 return v_out.set( get(0+column*4),
338 get(1+column*4),
339 get(2+column*4) );
340 }
341
342 /**
343 * Get the named row of the given column-major matrix to v_out w/ boundary check.
344 * @param row named row to copy
345 * @param v_out the row-vector storage
346 * @return given result vector <i>v_out</i> for chaining
347 */
348 constexpr Vec4& getRow(const jau::nsize_t row, Vec4& v_out) const noexcept {
349 using namespace jau::int_literals;
350 return v_out.set( get( row + 0*4_unz),
351 get( row + 1*4_unz),
352 get( row + 2*4_unz),
353 get( row + 3*4_unz) );
354 }
355 /**
356 * Get the named column of the given column-major matrix to v_out w/o boundary check.
357 * @param row named row to copy
358 * @return result vector holding the requested row
359 */
360 constexpr Vec4 getRow(const jau::nsize_t row) const noexcept {
361 using namespace jau::int_literals;
362 return Vec4( get(row+0*4_unz),
363 get(row+1*4_unz),
364 get(row+2*4_unz),
365 get(row+3*4_unz) );
366 }
367
368 /**
369 * Get the named row of the given column-major matrix to v_out w/o boundary check.
370 * @param row named row to copy
371 * @param v_out the row-vector assert( i < 16 )e
372 * @return given result vector <i>v_out</i> for chaining
373 */
374 constexpr Vec3& getRow(const jau::nsize_t row, Vec3& v_out) const noexcept {
375 using namespace jau::int_literals;
376 assert( row <= 2 );
377 return v_out.set( get(row+0*4_unz),
378 get(row+1*4_unz),
379 get(row+2*4_unz) );
380 }
381
382 /**
383 * Get this matrix into the given value_type[16] array in column major order w/o boundary check.
384 *
385 * @param dst value_type[16] array storage in column major order
386 * @return {@code dst} for chaining
387 */
388 constexpr iterator get(iterator dst) const noexcept {
389 iterator dst_i = dst;
390 *dst_i = m00; // column 0
391 *(++dst_i) = m10;
392 *(++dst_i) = m20;
393 *(++dst_i) = m30;
394 *(++dst_i) = m01; // column 1
395 *(++dst_i) = m11;
396 *(++dst_i) = m21;
397 *(++dst_i) = m31;
398 *(++dst_i) = m02; // column 2
399 *(++dst_i) = m12;
400 *(++dst_i) = m22;
401 *(++dst_i) = m32;
402 *(++dst_i) = m03; // column 3
403 *(++dst_i) = m13;
404 *(++dst_i) = m23;
405 *(++dst_i) = m33;
406 return dst;
407 }
408
409 /**
410 * Get this matrix into the given {@link FloatBuffer} in column major order.
411 *
412 * @param dst 4x4 matrix std::vector in column-major order starting at {@code dst_off}
413 * @param dst_off offset for matrix {@code dst}
414 * @return {@code dst} for chaining
415 */
416 constexpr std::vector<value_type>& get(std::vector<value_type>& dst, size_t dst_off) const noexcept {
417 assert( dst.size() >= dst_off+16 && dst_off <= std::numeric_limits<size_t>::max() - 15 );
418 get( &dst[dst_off++] );
419 return dst;
420 }
421
422 //
423 // Basic matrix operations
424 //
425
426 /**
427 * Returns the determinant of this matrix
428 * @return the matrix determinant
429 */
430 value_type determinant() const noexcept {
431 value_type ret = 0;
432 ret += m00 * ( + m11*(m22*m33 - m23*m32) - m12*(m21*m33 - m23*m31) + m13*(m21*m32 - m22*m31));
433 ret -= m01 * ( + m10*(m22*m33 - m23*m32) - m12*(m20*m33 - m23*m30) + m13*(m20*m32 - m22*m30));
434 ret += m02 * ( + m10*(m21*m33 - m23*m31) - m11*(m20*m33 - m23*m30) + m13*(m20*m31 - m21*m30));
435 ret -= m03 * ( + m10*(m21*m32 - m22*m31) - m11*(m20*m32 - m22*m30) + m12*(m20*m31 - m21*m30));
436 return ret;
437 }
438
439 /**
440 * Transpose this matrix.
441 *
442 * @return this matrix for chaining
443 */
444 Matrix4& transpose() noexcept {
445 value_type tmp;
446
447 tmp = m10;
448 m10 = m01;
449 m01 = tmp;
450
451 tmp = m20;
452 m20 = m02;
453 m02 = tmp;
454
455 tmp = m30;
456 m30 = m03;
457 m03 = tmp;
458
459 tmp = m21;
460 m21 = m12;
461 m12 = tmp;
462
463 tmp = m31;
464 m31 = m13;
465 m13 = tmp;
466
467 tmp = m32;
468 m32 = m23;
469 m23 = tmp;
470
471 return *this;
472 }
473
474 /**
475 * Transpose the given {@code src} matrix into this matrix.
476 *
477 * @param src source 4x4 matrix
478 * @return this matrix (result) for chaining
479 */
480 Matrix4& transpose(const Matrix4& src) noexcept {
481 if( &src == this ) {
482 return transpose();
483 }
484 m00 = src.m00;
485 m10 = src.m01;
486 m20 = src.m02;
487 m30 = src.m03;
488
489 m01 = src.m10;
490 m11 = src.m11;
491 m21 = src.m12;
492 m31 = src.m13;
493
494 m02 = src.m20;
495 m12 = src.m21;
496 m22 = src.m22;
497 m32 = src.m23;
498
499 m03 = src.m30;
500 m13 = src.m31;
501 m23 = src.m32;
502 m33 = src.m33;
503 return *this;
504 }
505
506 /**
507 * Invert this matrix.
508 * @return false if this matrix is singular and inversion not possible, otherwise true
509 */
510 bool invert() noexcept {
511 const value_type amax = absMax();
512 if( zero == amax ) {
513 DBG_PRINT("Matrix4:invert: absMax==0: %s", toString().c_str());
514 return false;
515 }
516 const value_type scale = one/amax;
517 const value_type a00 = m00*scale;
518 const value_type a10 = m10*scale;
519 const value_type a20 = m20*scale;
520 const value_type a30 = m30*scale;
521
522 const value_type a01 = m01*scale;
523 const value_type a11 = m11*scale;
524 const value_type a21 = m21*scale;
525 const value_type a31 = m31*scale;
526
527 const value_type a02 = m02*scale;
528 const value_type a12 = m12*scale;
529 const value_type a22 = m22*scale;
530 const value_type a32 = m32*scale;
531
532 const value_type a03 = m03*scale;
533 const value_type a13 = m13*scale;
534 const value_type a23 = m23*scale;
535 const value_type a33 = m33*scale;
536
537 const value_type b00 = + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31);
538 const value_type b01 = -( + a10*(a22*a33 - a23*a32) - a12*(a20*a33 - a23*a30) + a13*(a20*a32 - a22*a30));
539 const value_type b02 = + a10*(a21*a33 - a23*a31) - a11*(a20*a33 - a23*a30) + a13*(a20*a31 - a21*a30);
540 const value_type b03 = -( + a10*(a21*a32 - a22*a31) - a11*(a20*a32 - a22*a30) + a12*(a20*a31 - a21*a30));
541
542 const value_type b10 = -( + a01*(a22*a33 - a23*a32) - a02*(a21*a33 - a23*a31) + a03*(a21*a32 - a22*a31));
543 const value_type b11 = + a00*(a22*a33 - a23*a32) - a02*(a20*a33 - a23*a30) + a03*(a20*a32 - a22*a30);
544 const value_type b12 = -( + a00*(a21*a33 - a23*a31) - a01*(a20*a33 - a23*a30) + a03*(a20*a31 - a21*a30));
545 const value_type b13 = + a00*(a21*a32 - a22*a31) - a01*(a20*a32 - a22*a30) + a02*(a20*a31 - a21*a30);
546
547 const value_type b20 = + a01*(a12*a33 - a13*a32) - a02*(a11*a33 - a13*a31) + a03*(a11*a32 - a12*a31);
548 const value_type b21 = -( + a00*(a12*a33 - a13*a32) - a02*(a10*a33 - a13*a30) + a03*(a10*a32 - a12*a30));
549 const value_type b22 = + a00*(a11*a33 - a13*a31) - a01*(a10*a33 - a13*a30) + a03*(a10*a31 - a11*a30);
550 const value_type b23 = -( + a00*(a11*a32 - a12*a31) - a01*(a10*a32 - a12*a30) + a02*(a10*a31 - a11*a30));
551
552 const value_type b30 = -( + a01*(a12*a23 - a13*a22) - a02*(a11*a23 - a13*a21) + a03*(a11*a22 - a12*a21));
553 const value_type b31 = + a00*(a12*a23 - a13*a22) - a02*(a10*a23 - a13*a20) + a03*(a10*a22 - a12*a20);
554 const value_type b32 = -( + a00*(a11*a23 - a13*a21) - a01*(a10*a23 - a13*a20) + a03*(a10*a21 - a11*a20));
555 const value_type b33 = + a00*(a11*a22 - a12*a21) - a01*(a10*a22 - a12*a20) + a02*(a10*a21 - a11*a20);
556
557 const value_type det = (a00*b00 + a01*b01 + a02*b02 + a03*b03) / scale;
558 if( 0 == det ) {
559 DBG_PRINT("Matrix4:invert: det==0: %s", toString().c_str());
560 return false;
561 }
562 const value_type invdet = one / det;
563
564 m00 = b00 * invdet;
565 m10 = b01 * invdet;
566 m20 = b02 * invdet;
567 m30 = b03 * invdet;
568
569 m01 = b10 * invdet;
570 m11 = b11 * invdet;
571 m21 = b12 * invdet;
572 m31 = b13 * invdet;
573
574 m02 = b20 * invdet;
575 m12 = b21 * invdet;
576 m22 = b22 * invdet;
577 m32 = b23 * invdet;
578
579 m03 = b30 * invdet;
580 m13 = b31 * invdet;
581 m23 = b32 * invdet;
582 m33 = b33 * invdet;
583 return true;
584 }
585
586 /**
587 * Invert the {@code src} matrix values into this matrix
588 * @param src the source matrix, which values are to be inverted
589 * @return false if {@code src} matrix is singular and inversion not possible, otherwise true
590 */
591 bool invert(const Matrix4& src) noexcept {
592 const value_type amax = src.absMax();
593 if( zero == amax ) {
594 DBG_PRINT("Matrix4:invert(src): absMax==0: %s", src.toString().c_str());
595 return false;
596 }
597 const value_type scale = one/amax;
598 const value_type a00 = src.m00*scale;
599 const value_type a10 = src.m10*scale;
600 const value_type a20 = src.m20*scale;
601 const value_type a30 = src.m30*scale;
602
603 const value_type a01 = src.m01*scale;
604 const value_type a11 = src.m11*scale;
605 const value_type a21 = src.m21*scale;
606 const value_type a31 = src.m31*scale;
607
608 const value_type a02 = src.m02*scale;
609 const value_type a12 = src.m12*scale;
610 const value_type a22 = src.m22*scale;
611 const value_type a32 = src.m32*scale;
612
613 const value_type a03 = src.m03*scale;
614 const value_type a13 = src.m13*scale;
615 const value_type a23 = src.m23*scale;
616 const value_type a33 = src.m33*scale;
617
618 const value_type b00 = + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31);
619 const value_type b01 = -( + a10*(a22*a33 - a23*a32) - a12*(a20*a33 - a23*a30) + a13*(a20*a32 - a22*a30));
620 const value_type b02 = + a10*(a21*a33 - a23*a31) - a11*(a20*a33 - a23*a30) + a13*(a20*a31 - a21*a30);
621 const value_type b03 = -( + a10*(a21*a32 - a22*a31) - a11*(a20*a32 - a22*a30) + a12*(a20*a31 - a21*a30));
622
623 const value_type b10 = -( + a01*(a22*a33 - a23*a32) - a02*(a21*a33 - a23*a31) + a03*(a21*a32 - a22*a31));
624 const value_type b11 = + a00*(a22*a33 - a23*a32) - a02*(a20*a33 - a23*a30) + a03*(a20*a32 - a22*a30);
625 const value_type b12 = -( + a00*(a21*a33 - a23*a31) - a01*(a20*a33 - a23*a30) + a03*(a20*a31 - a21*a30));
626 const value_type b13 = + a00*(a21*a32 - a22*a31) - a01*(a20*a32 - a22*a30) + a02*(a20*a31 - a21*a30);
627
628 const value_type b20 = + a01*(a12*a33 - a13*a32) - a02*(a11*a33 - a13*a31) + a03*(a11*a32 - a12*a31);
629 const value_type b21 = -( + a00*(a12*a33 - a13*a32) - a02*(a10*a33 - a13*a30) + a03*(a10*a32 - a12*a30));
630 const value_type b22 = + a00*(a11*a33 - a13*a31) - a01*(a10*a33 - a13*a30) + a03*(a10*a31 - a11*a30);
631 const value_type b23 = -( + a00*(a11*a32 - a12*a31) - a01*(a10*a32 - a12*a30) + a02*(a10*a31 - a11*a30));
632
633 const value_type b30 = -( + a01*(a12*a23 - a13*a22) - a02*(a11*a23 - a13*a21) + a03*(a11*a22 - a12*a21));
634 const value_type b31 = + a00*(a12*a23 - a13*a22) - a02*(a10*a23 - a13*a20) + a03*(a10*a22 - a12*a20);
635 const value_type b32 = -( + a00*(a11*a23 - a13*a21) - a01*(a10*a23 - a13*a20) + a03*(a10*a21 - a11*a20));
636 const value_type b33 = + a00*(a11*a22 - a12*a21) - a01*(a10*a22 - a12*a20) + a02*(a10*a21 - a11*a20);
637
638 const value_type det = (a00*b00 + a01*b01 + a02*b02 + a03*b03) / scale;
639
640 if( 0 == det ) {
641 DBG_PRINT("Matrix4:invert(src): det==0: %s", src.toString().c_str());
642 return false;
643 }
644 const value_type invdet = one / det;
645
646 m00 = b00 * invdet;
647 m10 = b01 * invdet;
648 m20 = b02 * invdet;
649 m30 = b03 * invdet;
650
651 m01 = b10 * invdet;
652 m11 = b11 * invdet;
653 m21 = b12 * invdet;
654 m31 = b13 * invdet;
655
656 m02 = b20 * invdet;
657 m12 = b21 * invdet;
658 m22 = b22 * invdet;
659 m32 = b23 * invdet;
660
661 m03 = b30 * invdet;
662 m13 = b31 * invdet;
663 m23 = b32 * invdet;
664 m33 = b33 * invdet;
665 return true;
666 }
667
668 private:
669 /** Returns the maximum abs(mxy) field */
670 value_type absMax() const noexcept {
671 value_type max = std::abs(m00);
672 max = std::max(max, std::abs(m01));
673 max = std::max(max, std::abs(m02));
674 max = std::max(max, std::abs(m03));
675
676 max = std::max(max, std::abs(m10));
677 max = std::max(max, std::abs(m11));
678 max = std::max(max, std::abs(m12));
679 max = std::max(max, std::abs(m13));
680
681 max = std::max(max, std::abs(m20));
682 max = std::max(max, std::abs(m21));
683 max = std::max(max, std::abs(m22));
684 max = std::max(max, std::abs(m23));
685
686 max = std::max(max, std::abs(m30));
687 max = std::max(max, std::abs(m31));
688 max = std::max(max, std::abs(m32));
689 max = std::max(max, std::abs(m33));
690 return max;
691 }
692
693 public:
694 /**
695 * Multiply matrix with scalar: [this] = [this] x [s]
696 * @param s a scalar
697 * @return this matrix for chaining
698 */
699 constexpr Matrix4& operator*=( const value_type s ) noexcept {
700 m00 *= s; m10 *= s; m20 *= s; m30 *= s;
701 m01 *= s; m11 *= s; m21 *= s; m31 *= s;
702 m02 *= s; m12 *= s; m22 *= s; m32 *= s;
703 m03 *= s; m13 *= s; m23 *= s; m33 *= s;
704 return *this;
705 }
706
707 /**
708 * Multiply matrix: [this] = [this] x [b]
709 * @param b 4x4 matrix
710 * @return this matrix for chaining
711 * @see #mul(mat4f, mat4f)
712 */
713 constexpr Matrix4& mul(const Matrix4& b) noexcept {
714 // return mul(new mat4f(this), b); // <- roughly half speed
715 value_type ai0=m00; // row-0, m[0+0*4]
716 value_type ai1=m01;
717 value_type ai2=m02;
718 value_type ai3=m03;
719 m00 = ai0 * b.m00 + ai1 * b.m10 + ai2 * b.m20 + ai3 * b.m30 ;
720 m01 = ai0 * b.m01 + ai1 * b.m11 + ai2 * b.m21 + ai3 * b.m31 ;
721 m02 = ai0 * b.m02 + ai1 * b.m12 + ai2 * b.m22 + ai3 * b.m32 ;
722 m03 = ai0 * b.m03 + ai1 * b.m13 + ai2 * b.m23 + ai3 * b.m33 ;
723
724 ai0=m10; //row-1, m[1+0*4]
725 ai1=m11;
726 ai2=m12;
727 ai3=m13;
728 m10 = ai0 * b.m00 + ai1 * b.m10 + ai2 * b.m20 + ai3 * b.m30 ;
729 m11 = ai0 * b.m01 + ai1 * b.m11 + ai2 * b.m21 + ai3 * b.m31 ;
730 m12 = ai0 * b.m02 + ai1 * b.m12 + ai2 * b.m22 + ai3 * b.m32 ;
731 m13 = ai0 * b.m03 + ai1 * b.m13 + ai2 * b.m23 + ai3 * b.m33 ;
732
733 ai0=m20; // row-2, m[2+0*4]
734 ai1=m21;
735 ai2=m22;
736 ai3=m23;
737 m20 = ai0 * b.m00 + ai1 * b.m10 + ai2 * b.m20 + ai3 * b.m30 ;
738 m21 = ai0 * b.m01 + ai1 * b.m11 + ai2 * b.m21 + ai3 * b.m31 ;
739 m22 = ai0 * b.m02 + ai1 * b.m12 + ai2 * b.m22 + ai3 * b.m32 ;
740 m23 = ai0 * b.m03 + ai1 * b.m13 + ai2 * b.m23 + ai3 * b.m33 ;
741
742 ai0=m30; // row-3, m[3+0*4]
743 ai1=m31;
744 ai2=m32;
745 ai3=m33;
746 m30 = ai0 * b.m00 + ai1 * b.m10 + ai2 * b.m20 + ai3 * b.m30 ;
747 m31 = ai0 * b.m01 + ai1 * b.m11 + ai2 * b.m21 + ai3 * b.m31 ;
748 m32 = ai0 * b.m02 + ai1 * b.m12 + ai2 * b.m22 + ai3 * b.m32 ;
749 m33 = ai0 * b.m03 + ai1 * b.m13 + ai2 * b.m23 + ai3 * b.m33 ;
750 return *this;
751 }
752 /**
753 * Multiply matrix: [this] = [this] x [b]
754 * @param b 4x4 matrix
755 * @return this matrix for chaining
756 * @see #mul(mat4f, mat4f)
757 */
758 constexpr Matrix4& operator*=( const Matrix4& rhs ) noexcept {
759 return mul( rhs );
760 }
761
762 /**
763 * Multiply matrix: [this] = [a] x [b]
764 * @param a 4x4 matrix, can't be this matrix
765 * @param b 4x4 matrix, can't be this matrix
766 * @return this matrix for chaining
767 * @see #mul(mat4f)
768 */
769 constexpr Matrix4& mul(const Matrix4& a, const Matrix4& b) noexcept {
770 // row-0, m[0+0*4]
771 m00 = a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20 + a.m03 * b.m30 ;
772 m01 = a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21 + a.m03 * b.m31 ;
773 m02 = a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22 + a.m03 * b.m32 ;
774 m03 = a.m00 * b.m03 + a.m01 * b.m13 + a.m02 * b.m23 + a.m03 * b.m33 ;
775
776 //row-1, m[1+0*4]
777 m10 = a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20 + a.m13 * b.m30 ;
778 m11 = a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21 + a.m13 * b.m31 ;
779 m12 = a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22 + a.m13 * b.m32 ;
780 m13 = a.m10 * b.m03 + a.m11 * b.m13 + a.m12 * b.m23 + a.m13 * b.m33 ;
781
782 // row-2, m[2+0*4]
783 m20 = a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20 + a.m23 * b.m30 ;
784 m21 = a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21 + a.m23 * b.m31 ;
785 m22 = a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22 + a.m23 * b.m32 ;
786 m23 = a.m20 * b.m03 + a.m21 * b.m13 + a.m22 * b.m23 + a.m23 * b.m33 ;
787
788 // row-3, m[3+0*4]
789 m30 = a.m30 * b.m00 + a.m31 * b.m10 + a.m32 * b.m20 + a.m33 * b.m30 ;
790 m31 = a.m30 * b.m01 + a.m31 * b.m11 + a.m32 * b.m21 + a.m33 * b.m31 ;
791 m32 = a.m30 * b.m02 + a.m31 * b.m12 + a.m32 * b.m22 + a.m33 * b.m32 ;
792 m33 = a.m30 * b.m03 + a.m31 * b.m13 + a.m32 * b.m23 + a.m33 * b.m33 ;
793
794 return *this;
795 }
796
797 /**
798 * @param v_in 4-component column-vector, can be v_out for in-place transformation
799 * @param v_out this x v_in
800 * @returns v_out for chaining
801 */
802 constexpr Vec4& mulVec4(const Vec4& v_in, Vec4& v_out) const noexcept {
803 // (one matrix row in column-major order) X (column vector)
804 const value_type x = v_in.x, y = v_in.y, z = v_in.z, w = v_in.w;
805 v_out.set( x * m00 + y * m01 + z * m02 + w * m03,
806 x * m10 + y * m11 + z * m12 + w * m13,
807 x * m20 + y * m21 + z * m22 + w * m23,
808 x * m30 + y * m31 + z * m32 + w * m33 );
809 return v_out;
810 }
811
812 /**
813 * Returns new Vec4, with this x v_in
814 * @param v_in 4-component column-vector
815 */
816 constexpr Vec4 operator*(const Vec4& rhs) const noexcept {
817 // (one matrix row in column-major order) X (column vector)
818 const value_type x = rhs.x, y = rhs.y, z = rhs.z, w = rhs.w;
819 return Vec4( x * m00 + y * m01 + z * m02 + w * m03,
820 x * m10 + y * m11 + z * m12 + w * m13,
821 x * m20 + y * m21 + z * m22 + w * m23,
822 x * m30 + y * m31 + z * m32 + w * m33 );
823 }
824
825 /**
826 * @param v_inout 4-component column-vector input and output, i.e. in-place transformation
827 * @returns v_inout for chaining
828 */
829 constexpr Vec4& mulVec4(Vec4& v_inout) const noexcept {
830 // (one matrix row in column-major order) X (column vector)
831 const value_type x = v_inout.x, y = v_inout.y, z = v_inout.z, w = v_inout.w;
832 v_inout.set( x * m00 + y * m01 + z * m02 + w * m03,
833 x * m10 + y * m11 + z * m12 + w * m13,
834 x * m20 + y * m21 + z * m22 + w * m23,
835 x * m30 + y * m31 + z * m32 + w * m33 );
836 return v_inout;
837 }
838
839 /**
840 * Affine 3f-vector transformation by 4x4 matrix
841 *
842 * 4x4 matrix multiplication with 3-component vector,
843 * using {@code 1} for for {@code v_in.w} and dropping {@code v_out.w},
844 * which shall be {@code 1}.
845 *
846 * @param v_in 3-component column-vector {@link vec3f}, can be v_out for in-place transformation
847 * @param v_out m_in x v_in, 3-component column-vector {@link vec3f}
848 * @returns v_out for chaining
849 */
850 constexpr Vec3& mulVec3(const Vec3& v_in, Vec3& v_out) const noexcept {
851 // (one matrix row in column-major order) X (column vector)
852 const value_type x = v_in.x, y = v_in.y, z = v_in.z;
853 v_out.set( x * m00 + y * m01 + z * m02 + one * m03,
854 x * m10 + y * m11 + z * m12 + one * m13,
855 x * m20 + y * m21 + z * m22 + one * m23 );
856 return v_out;
857 }
858 /**
859 * Returns new Vec3, with affine 3f-vector transformation by this 4x4 matrix: this x v_in
860 *
861 * 4x4 matrix multiplication with 3-component vector,
862 * using {@code 1} for for {@code v_in.w} and dropping {@code v_out.w},
863 * which shall be {@code 1}.
864 *
865 * @param v_in 3-component column-vector {@link vec3f}
866 */
867 constexpr Vec3 operator*(const Vec3& rhs) const noexcept {
868 // (one matrix row in column-major order) X (column vector)
869 const value_type x = rhs.x, y = rhs.y, z = rhs.z;
870 return Vec3( x * m00 + y * m01 + z * m02 + one * m03,
871 x * m10 + y * m11 + z * m12 + one * m13,
872 x * m20 + y * m21 + z * m22 + one * m23 );
873 }
874
875 /**
876 * Affine 3f-vector transformation by 4x4 matrix: v_inout = this * v_inout
877 *
878 * 4x4 matrix multiplication with 3-component vector,
879 * using {@code 1} for for {@code v_inout.w} and dropping {@code v_inout.w},
880 * which shall be {@code 1}.
881 *
882 * @param v_inout 3-component column-vector {@link vec3f} input and output, i.e. in-place transformation
883 * @returns v_inout for chaining
884 */
885 constexpr Vec3& mulVec3(Vec3& v_inout) const noexcept {
886 // (one matrix row in column-major order) X (column vector)
887 const value_type x = v_inout.x, y = v_inout.y, z = v_inout.z;
888 v_inout.set( x * m00 + y * m01 + z * m02 + one * m03,
889 x * m10 + y * m11 + z * m12 + one * m13,
890 x * m20 + y * m21 + z * m22 + one * m23 );
891 return v_inout;
892 }
893
894 //
895 // Matrix setTo...(), affine + basic
896 //
897
898 /**
899 * Set this matrix to translation.
900 * <pre>
901 Translation matrix (Column Order):
902 1 0 0 0
903 0 1 0 0
904 0 0 1 0
905 x y z 1
906 * </pre>
907 * @param x x-axis translate
908 * @param y y-axis translate
909 * @param z z-axis translate
910 * @return this matrix for chaining
911 */
912 constexpr Matrix4& setToTranslation(const value_type x, const value_type y, const value_type z) noexcept {
913 m00 = m11 = m22 = m33 = one;
914 m03 = x;
915 m13 = y;
916 m23 = z;
917 m01 = m02 =
918 m10 = m12 =
919 m20 = m21 =
920 m30 = m31 = m32 = zero;
921 return *this;
922 }
923
924 /**
925 * Set this matrix to translation.
926 * <pre>
927 Translation matrix (Column Order):
928 1 0 0 0
929 0 1 0 0
930 0 0 1 0
931 x y z 1
932 * </pre>
933 * @param t translate vec3f
934 * @return this matrix for chaining
935 */
936 constexpr Matrix4& setToTranslation(const Vec3& t) noexcept {
937 return setToTranslation(t.x, t.y, t.z);
938 }
939
940 /**
941 * Set this matrix to scale.
942 * <pre>
943 Scale matrix (Any Order):
944 x 0 0 0
945 0 y 0 0
946 0 0 z 0
947 0 0 0 1
948 * </pre>
949 * @param x x-axis scale
950 * @param y y-axis scale
951 * @param z z-axis scale
952 * @return this matrix for chaining
953 */
954 constexpr Matrix4& setToScale(const value_type x, const value_type y, const value_type z) noexcept {
955 m33 = one;
956 m00 = x;
957 m11 = y;
958 m22 = z;
959 m01 = m02 = m03 =
960 m10 = m12 = m13 =
961 m20 = m21 = m23 =
962 m30 = m31 = m32 = zero;
963 return *this;
964 }
965
966 /**
967 * Set this matrix to scale.
968 * <pre>
969 Scale matrix (Any Order):
970 x 0 0 0
971 0 y 0 0
972 0 0 z 0
973 0 0 0 1
974 * </pre>
975 * @param s scale vec3f
976 * @return this matrix for chaining
977 */
978 constexpr Matrix4& setToScale(const Vec3& s) noexcept {
979 return setToScale(s.x, s.y, s.z);
980 }
981
982 /**
983 * Set this matrix to rotation from the given axis and angle in radians.
984 * <pre>
985 Rotation matrix (Column Order):
986 xx(1-c)+c xy(1-c)+zs xz(1-c)-ys 0
987 xy(1-c)-zs yy(1-c)+c yz(1-c)+xs 0
988 xz(1-c)+ys yz(1-c)-xs zz(1-c)+c 0
989 0 0 0 1
990 * </pre>
991 * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q38">Matrix-FAQ Q38</a>
992 * @param ang_rad angle in radians
993 * @param x x of rotation axis
994 * @param y y of rotation axis
995 * @param z z of rotation axis
996 * @return this matrix for chaining
997 */
999 const value_type c = std::cos(ang_rad);
1000 const value_type ic= one - c;
1001 const value_type s = std::sin(ang_rad);
1002
1003 Vec3 tmp(x, y, z); tmp.normalize();
1004 x = tmp.x; y = tmp.y; z = tmp.z;
1005
1006 const value_type xy = x*y;
1007 const value_type xz = x*z;
1008 const value_type xs = x*s;
1009 const value_type ys = y*s;
1010 const value_type yz = y*z;
1011 const value_type zs = z*s;
1012 m00 = x*x*ic+c;
1013 m10 = xy*ic+zs;
1014 m20 = xz*ic-ys;
1015 m30 = zero;
1016
1017 m01 = xy*ic-zs;
1018 m11 = y*y*ic+c;
1019 m21 = yz*ic+xs;
1020 m31 = zero;
1021
1022 m02 = xz*ic+ys;
1023 m12 = yz*ic-xs;
1024 m22 = z*z*ic+c;
1025 m32 = zero;
1026
1027 m03 = zero;
1028 m13 = zero;
1029 m23 = zero;
1030 m33 = one;
1031
1032 return *this;
1033 }
1034
1035 /**
1036 * Set this matrix to rotation from the given axis and angle in radians.
1037 * <pre>
1038 Rotation matrix (Column Order):
1039 xx(1-c)+c xy(1-c)+zs xz(1-c)-ys 0
1040 xy(1-c)-zs yy(1-c)+c yz(1-c)+xs 0
1041 xz(1-c)+ys yz(1-c)-xs zz(1-c)+c 0
1042 0 0 0 1
1043 * </pre>
1044 * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q38">Matrix-FAQ Q38</a>
1045 * @param ang_rad angle in radians
1046 * @param axis rotation axis
1047 * @return this matrix for chaining
1048 */
1049 constexpr_cxx26 Matrix4& setToRotationAxis(const value_type ang_rad, const Vec3& axis) noexcept {
1050 return setToRotationAxis(ang_rad, axis.x, axis.y, axis.z);
1051 }
1052
1053 /**
1054 * Set this matrix to rotation from the given Euler rotation angles in radians.
1055 * <p>
1056 * The rotations are applied in the given order:
1057 * <ul>
1058 * <li>y - heading</li>
1059 * <li>z - attitude</li>
1060 * <li>x - bank</li>
1061 * </ul>
1062 * </p>
1063 * @param bankX the Euler pitch angle in radians. (rotation about the X axis)
1064 * @param headingY the Euler yaw angle in radians. (rotation about the Y axis)
1065 * @param attitudeZ the Euler roll angle in radians. (rotation about the Z axis)
1066 * @return this matrix for chaining
1067 * <p>
1068 * Implementation does not use Quaternion and hence is exposed to
1069 * <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q34">Gimbal-Lock</a>,
1070 * consider using Quaternion::toMatrix().
1071 * </p>
1072 * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q36">Matrix-FAQ Q36</a>
1073 * @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToMatrix/index.htm">euclideanspace.com-eulerToMatrix</a>
1074 * @see Quaternion::toMatrix()
1075 */
1076 constexpr_cxx26 Matrix4& setToRotationEuler(const value_type bankX, const value_type headingY, const value_type attitudeZ) noexcept {
1077 // Assuming the angles are in radians.
1078 const value_type ch = std::cos(headingY);
1079 const value_type sh = std::sin(headingY);
1080 const value_type ca = std::cos(attitudeZ);
1081 const value_type sa = std::sin(attitudeZ);
1082 const value_type cb = std::cos(bankX);
1083 const value_type sb = std::sin(bankX);
1084
1085 m00 = ch*ca;
1086 m10 = sa;
1087 m20 = -sh*ca;
1088 m30 = zero;
1089
1090 m01 = sh*sb - ch*sa*cb;
1091 m11 = ca*cb;
1092 m21 = sh*sa*cb + ch*sb;
1093 m31 = zero;
1094
1095 m02 = ch*sa*sb + sh*cb;
1096 m12 = -ca*sb;
1097 m22 = -sh*sa*sb + ch*cb;
1098 m32 = zero;
1099
1100 m03 = zero;
1101 m13 = zero;
1102 m23 = zero;
1103 m33 = one;
1104
1105 return *this;
1106 }
1107
1108 /**
1109 * Set this matrix to rotation from the given Euler rotation angles in radians.
1110 * <p>
1111 * The rotations are applied in the given order:
1112 * <ul>
1113 * <li>y - heading</li>
1114 * <li>z - attitude</li>
1115 * <li>x - bank</li>
1116 * </ul>
1117 * </p>
1118 * @param angradXYZ euler angle vector in radians holding x-bank, y-heading and z-attitude
1119 * @return this quaternion for chaining.
1120 * <p>
1121 * Implementation does not use Quaternion and hence is exposed to
1122 * <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q34">Gimbal-Lock</a>,
1123 * consider using Quaternion::toMatrix().
1124 * </p>
1125 * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q36">Matrix-FAQ Q36</a>
1126 * @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToMatrix/index.htm">euclideanspace.com-eulerToMatrix</a>
1127 * @see Quaternion::toMatrix()
1128 */
1129 constexpr_cxx26 Matrix4& setToRotationEuler(const Vec3& angradXYZ) noexcept {
1130 return setToRotationEuler(angradXYZ.x, angradXYZ.y, angradXYZ.z);
1131 }
1132
1133 /**
1134 * Set this matrix to orthogonal projection.
1135 * <pre>
1136 Ortho matrix (Column Order):
1137 2/dx 0 0 0
1138 0 2/dy 0 0
1139 0 0 2/dz 0
1140 tx ty tz 1
1141 * </pre>
1142 * @param left
1143 * @param right
1144 * @param bottom
1145 * @param top
1146 * @param zNear
1147 * @param zFar
1148 * @return this matrix for chaining
1149 */
1150 constexpr Matrix4& setToOrtho(const value_type left, const value_type right,
1151 const value_type bottom, const value_type top,
1152 const value_type zNear, const value_type zFar) noexcept {
1153 {
1154 // m00 = m11 = m22 = m33 = one;
1155 m10 = m20 = m30 = zero;
1156 m01 = m21 = m31 = zero;
1157 m02 = m12 = m32 = zero;
1158 // m03 = m13 = m23 = zero;
1159 }
1160 const value_type dx=right-left;
1161 const value_type dy=top-bottom;
1162 const value_type dz=zFar-zNear;
1163 const value_type tx=-one*(right+left)/dx;
1164 const value_type ty=-one*(top+bottom)/dy;
1165 const value_type tz=-one*(zFar+zNear)/dz;
1166
1167 m00 = two/dx;
1168 m11 = two/dy;
1169 m22 = -two/dz;
1170
1171 m03 = tx;
1172 m13 = ty;
1173 m23 = tz;
1174 m33 = one;
1175
1176 return *this;
1177 }
1178
1179 /**
1180 * Set this matrix to frustum.
1181 * <pre>
1182 Frustum matrix (Column Order):
1183 2*zNear/dx 0 0 0
1184 0 2*zNear/dy 0 0
1185 A B C -1
1186 0 0 D 0
1187 * </pre>
1188 * @param left
1189 * @param right
1190 * @param bottom
1191 * @param top
1192 * @param zNear
1193 * @param zFar
1194 * @return this matrix for chaining
1195 * @throws IllegalArgumentException if {@code zNear <= 0} or {@code zFar <= zNear}
1196 * or {@code left == right}, or {@code bottom == top}.
1197 */
1198 Matrix4& setToFrustum(const value_type left, const value_type right,
1199 const value_type bottom, const value_type top,
1200 const value_type zNear, const value_type zFar) {
1201 if( zNear <= zero || zFar <= zNear ) {
1202 throw jau::IllegalArgumentError("Requirements zNear > 0 and zFar > zNear, but zNear "+std::to_string(zNear)+", zFar "+std::to_string(zFar), E_FILE_LINE);
1203 }
1204 if( left == right || top == bottom) {
1205 throw jau::IllegalArgumentError("GL_INVALID_VALUE: top,bottom and left,right must not be equal", E_FILE_LINE);
1206 }
1207 {
1208 // m00 = m11 = m22 = m33 = 1f;
1209 m10 = m20 = m30 = zero;
1210 m01 = m21 = m31 = zero;
1211 m03 = m13 = zero;
1212 }
1213 const value_type zNear2 = two*zNear;
1214 const value_type dx=right-left;
1215 const value_type dy=top-bottom;
1216 const value_type dz=zFar-zNear;
1217 const value_type A=(right+left)/dx;
1218 const value_type B=(top+bottom)/dy;
1219 const value_type C=-one*(zFar+zNear)/dz;
1220 const value_type D=-two*(zFar*zNear)/dz;
1221
1222 m00 = zNear2/dx;
1223 m11 = zNear2/dy;
1224
1225 m02 = A;
1226 m12 = B;
1227 m22 = C;
1228 m32 = -one;
1229
1230 m23 = D;
1231 m33 = zero;
1232
1233 return *this;
1234 }
1235
1236 /**
1237 * Set this matrix to perspective {@link #setToFrustum(value_type, value_type, value_type, value_type, value_type, value_type) frustum} projection.
1238 *
1239 * @param fovy_rad angle in radians
1240 * @param aspect aspect ratio width / height
1241 * @param zNear
1242 * @param zFar
1243 * @return this matrix for chaining
1244 * @throws IllegalArgumentException if {@code zNear <= 0} or {@code zFar <= zNear}
1245 * @see #setToFrustum(value_type, value_type, value_type, value_type, value_type, value_type)
1246 */
1247 Matrix4& setToPerspective(const value_type fovy_rad, const value_type aspect, const value_type zNear, const value_type zFar) {
1248 const value_type top = std::tan(fovy_rad/two) * zNear; // use tangent of half-fov !
1249 const value_type bottom = -one * top; // -1f * fovhvTan.top * zNear
1250 const value_type left = aspect * bottom; // aspect * -1f * fovhvTan.top * zNear
1251 const value_type right = aspect * top; // aspect * fovhvTan.top * zNear
1252 return setToFrustum(left, right, bottom, top, zNear, zFar);
1253 }
1254
1255 /**
1256 * Set this matrix to perspective {@link #setToFrustum(value_type, value_type, value_type, value_type, value_type, value_type) frustum} projection.
1257 *
1258 * @param fovhv {@link FovHVHalves} field of view in both directions, may not be centered, either in radians or tangent
1259 * @param zNear
1260 * @param zFar
1261 * @return this matrix for chaining
1262 * @throws IllegalArgumentException if {@code zNear <= 0} or {@code zFar <= zNear}
1263 * @see #setToFrustum(value_type, value_type, value_type, value_type, value_type, value_type)
1264 * @see Frustum#updateByFovDesc(mat4f, com.jogamp.math.geom.Frustum.FovDesc)
1265 */
1266 Matrix4& setToPerspective(const FovHVHalves& fovhv, const value_type zNear, const value_type zFar) {
1267 const FovHVHalves fovhvTan = fovhv.toTangents(); // use tangent of half-fov !
1268 const value_type top = fovhvTan.top * zNear;
1269 const value_type bottom = -one * fovhvTan.bottom * zNear;
1270 const value_type left = -one * fovhvTan.left * zNear;
1271 const value_type right = fovhvTan.right * zNear;
1272 return setToFrustum(left, right, bottom, top, zNear, zFar);
1273 }
1274
1275 /**
1276 * Set this matrix to the <i>look-at</i> matrix based on given parameters.
1277 * <p>
1278 * Consist out of two matrix multiplications:
1279 * <pre>
1280 * <b>R</b> = <b>L</b> x <b>T</b>,
1281 * with <b>L</b> for <i>look-at</i> matrix and
1282 * <b>T</b> for eye translation.
1283 *
1284 * Result <b>R</b> can be utilized for <i>projection or modelview</i> multiplication, i.e.
1285 * <b>M</b> = <b>M</b> x <b>R</b>,
1286 * with <b>M</b> being the <i>projection or modelview</i> matrix.
1287 * </pre>
1288 * </p>
1289 * @param eye 3 component eye vector
1290 * @param center 3 component center vector
1291 * @param up 3 component up vector
1292 * @return this matrix for chaining
1293 */
1294 constexpr Matrix4& setToLookAt(const Vec3& eye, const Vec3& center, const Vec3& up) noexcept {
1295 // normalized forward!
1296 const Vec3 fwd = ( center - eye ).normalize();
1297
1298 /* Side = forward x up, normalized */
1299 const Vec3 side = fwd.cross(up).normalize();
1300
1301 /* Recompute up as: up = side x forward */
1302 const Vec3 up2 = side.cross(fwd);
1303
1304 m00 = side.x;
1305 m10 = up2.x;
1306 m20 = -fwd.x;
1307 m30 = 0;
1308
1309 m01 = side.y;
1310 m11 = up2.y;
1311 m21 = -fwd.y;
1312 m31 = 0;
1313
1314 m02 = side.z;
1315 m12 = up2.z;
1316 m22 = -fwd.z;
1317 m32 = 0;
1318
1319 m03 = 0;
1320 m13 = 0;
1321 m23 = 0;
1322 m33 = 1;
1323
1324 Matrix4 tmp;
1325 return mul( tmp.setToTranslation( -eye.x, -eye.y, -eye.z ) );
1326 }
1327
1328 /**
1329 * Set this matrix to the <i>pick</i> matrix based on given parameters.
1330 * <p>
1331 * Traditional <code>gluPickMatrix</code> implementation.
1332 * </p>
1333 * <p>
1334 * Consist out of two matrix multiplications:
1335 * <pre>
1336 * <b>R</b> = <b>T</b> x <b>S</b>,
1337 * with <b>T</b> for viewport translation matrix and
1338 * <b>S</b> for viewport scale matrix.
1339 *
1340 * Result <b>R</b> can be utilized for <i>projection</i> multiplication, i.e.
1341 * <b>P</b> = <b>P</b> x <b>R</b>,
1342 * with <b>P</b> being the <i>projection</i> matrix.
1343 * </pre>
1344 * </p>
1345 * <p>
1346 * To effectively use the generated pick matrix for picking,
1347 * call {@link #setToPick(value_type, value_type, value_type, value_type, Recti, mat4f) setToPick(..)}
1348 * and multiply a {@link #setToPerspective(value_type, value_type, value_type, value_type) custom perspective matrix}
1349 * by this pick matrix. Then you may load the result onto the perspective matrix stack.
1350 * </p>
1351 * @param x the center x-component of a picking region in window coordinates
1352 * @param y the center y-component of a picking region in window coordinates
1353 * @param deltaX the width of the picking region in window coordinates.
1354 * @param deltaY the height of the picking region in window coordinates.
1355 * @param viewport Rect4i viewport
1356 * @return true if successful or false if either delta value is <= zero.
1357 */
1358 constexpr bool setToPick(const value_type x, const value_type y, const value_type deltaX, const value_type deltaY,
1359 const Recti& viewport) noexcept {
1360 if (deltaX <= 0 || deltaY <= 0) {
1361 return false;
1362 }
1363 /* Translate and scale the picked region to the entire window */
1364 setToTranslation( ( viewport.width() - two * ( x - viewport.x() ) ) / deltaX,
1365 ( viewport.height() - two * ( y - viewport.y() ) ) / deltaY,
1366 0);
1367 Matrix4 mat4Tmp;
1368 mat4Tmp.setToScale( viewport.width() / deltaX, viewport.height() / deltaY, one );
1369 mul(mat4Tmp);
1370 return true;
1371 }
1372
1373 //
1374 // Matrix affine operations using setTo..()
1375 //
1376
1377 /**
1378 * Rotate this matrix about give axis and angle in radians, i.e. multiply by {@link #setToRotationAxis(value_type, value_type, value_type, value_type) axis-rotation matrix}.
1379 * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q38">Matrix-FAQ Q38</a>
1380 * @param angrad angle in radians
1381 * @param x x of rotation axis
1382 * @param y y of rotation axis
1383 * @param z z of rotation axis
1384 * @return this matrix for chaining
1385 */
1386 constexpr_cxx26 Matrix4& rotate(const value_type ang_rad, const value_type x, const value_type y, const value_type z) noexcept {
1387 Matrix4 tmp;
1388 return mul( tmp.setToRotationAxis(ang_rad, x, y, z) );
1389 }
1390
1391 /**
1392 * Rotate this matrix about give axis and angle in radians, i.e. multiply by {@link #setToRotationAxis(value_type, vec3f) axis-rotation matrix}.
1393 * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q38">Matrix-FAQ Q38</a>
1394 * @param angrad angle in radians
1395 * @param axis rotation axis
1396 * @return this matrix for chaining
1397 */
1398 constexpr_cxx26 Matrix4& rotate(const value_type ang_rad, const Vec3& axis) noexcept {
1399 Matrix4 tmp;
1400 return mul( tmp.setToRotationAxis(ang_rad, axis) );
1401 }
1402
1403 /**
1404 * Translate this matrix, i.e. multiply by {@link #setToTranslation(value_type, value_type, value_type) translation matrix}.
1405 * @param x x translation
1406 * @param y y translation
1407 * @param z z translation
1408 * @return this matrix for chaining
1409 */
1410 constexpr Matrix4& translate(const value_type x, const value_type y, const value_type z) noexcept {
1411 Matrix4 tmp;
1412 return mul( tmp.setToTranslation(x, y, z) );
1413 }
1414
1415 /**
1416 * Translate this matrix, i.e. multiply by {@link #setToTranslation(vec3f) translation matrix}.
1417 * @param t translation vec3f
1418 * @return this matrix for chaining
1419 */
1420 constexpr Matrix4& translate(const Vec3& t) noexcept {
1421 Matrix4 tmp;
1422 return mul( tmp.setToTranslation(t) );
1423 }
1424
1425 /**
1426 * Scale this matrix, i.e. multiply by {@link #setToScale(value_type, value_type, value_type) scale matrix}.
1427 * @param x x scale
1428 * @param y y scale
1429 * @param z z scale
1430 * @return this matrix for chaining
1431 */
1432 constexpr Matrix4& scale(const value_type x, const value_type y, const value_type z) noexcept {
1433 Matrix4 tmp;
1434 return mul( tmp.setToScale(x, y, z) );
1435 }
1436
1437 /**
1438 * Scale this matrix, i.e. multiply by {@link #setToScale(value_type, value_type, value_type) scale matrix}.
1439 * @param s scale for x-, y- and z-axis
1440 * @return this matrix for chaining
1441 */
1442 constexpr Matrix4& scale(const value_type s) noexcept {
1443 Matrix4 tmp;
1444 return mul( tmp.setToScale(s, s, s) );
1445 }
1446
1447 //
1448 // Static multi Matrix ops
1449 //
1450
1451 /**
1452 * Map object coordinates to window coordinates.
1453 *
1454 * Traditional <code>gluProject</code> implementation.
1455 *
1456 * @param obj object position, 3 component vector
1457 * @param mPMv [projection] x [modelview] matrix, i.e. P x Mv
1458 * @param viewport Rect4i viewport
1459 * @param winPos 3 component window coordinate, the result
1460 * @return true if successful, otherwise false (z is 1)
1461 */
1462 static bool mapObjToWin(const Vec3& obj, const Matrix4& mPMv,
1463 const Recti& viewport, Vec3& winPos) noexcept
1464 {
1465 // rawWin = P * Mv * o = PMv * o
1466 Vec4 rawWin = mPMv * Vec4(obj, one);
1467 return mapToWinImpl(rawWin, viewport, winPos);
1468 }
1469
1470 /**
1471 * Map object coordinates to window coordinates.
1472 *
1473 * Traditional <code>gluProject</code> implementation.
1474 *
1475 * @param obj object position, 3 component vector
1476 * @param mMv modelview matrix
1477 * @param mP projection matrix
1478 * @param viewport Rect4i viewport
1479 * @param winPos 3 component window coordinate, the result
1480 * @return true if successful, otherwise false (z is 1)
1481 */
1482 static bool mapObjToWin(const Vec3& obj, const Matrix4& mMv, const Matrix4& mP,
1483 const Recti& viewport, Vec3& winPos) noexcept
1484 {
1485 Vec4 rawWin = mP * mMv * Vec4(obj, one);
1486 return mapToWinImpl(rawWin, viewport, winPos);
1487 }
1488
1489 /**
1490 * Map world coordinates ( M x object ) to window coordinates.
1491 *
1492 * @param world world position, 3 component vector
1493 * @param mV view matrix
1494 * @param mP projection matrix
1495 * @param viewport Rect4i viewport
1496 * @param winPos 3 component window coordinate, the result
1497 * @return true if successful, otherwise false (z is 1)
1498 */
1499 static bool mapWorldToWin(const Vec3& world, const Matrix4& mV, const Matrix4& mP,
1500 const Recti& viewport, Vec3& winPos) noexcept
1501 {
1502 Vec4 rawWin = mP * mV * Vec4(world, one);
1503 return mapToWinImpl(rawWin, viewport, winPos);
1504 }
1505
1506 /**
1507 * Map view coordinates ( Mv x object ) to window coordinates.
1508 *
1509 * @param view view position, 3 component vector
1510 * @param mP projection matrix
1511 * @param viewport Rect4i viewport
1512 * @param winPos 3 component window coordinate, the result
1513 * @return true if successful, otherwise false (z is 1)
1514 */
1515 static bool mapViewToWin(const Vec3& view, const Matrix4& mP,
1516 const Recti& viewport, Vec3& winPos) noexcept
1517 {
1518 Vec4 rawWin = mP * Vec4(view, one);
1519 return mapToWinImpl(rawWin, viewport, winPos);
1520 }
1521
1522 private:
1523 static bool mapToWinImpl(Vec4& rawWin,
1524 const Recti& viewport, Vec3& winPos) noexcept
1525 {
1526 if ( zero == rawWin.w ) {
1527 return false;
1528 }
1529
1530 const value_type s = ( one / rawWin.w ) * half;
1531
1532 // Map x, y and z to range 0-1 (w is ignored)
1533 rawWin.scale(s).add(half, half, half, zero);
1534
1535 // Map x,y to viewport
1536 winPos.set( rawWin.x * viewport.width() + viewport.x(),
1537 rawWin.y * viewport.height() + viewport.y(),
1538 rawWin.z );
1539
1540 return true;
1541 }
1542
1543 public:
1544 /**
1545 * Map window coordinates to object coordinates.
1546 *
1547 * Traditional <code>gluUnProject</code> implementation.
1548 *
1549 * @param winx
1550 * @param winy
1551 * @param winz
1552 * @param mMv 4x4 modelview matrix
1553 * @param mP 4x4 projection matrix
1554 * @param viewport Rect4i viewport
1555 * @param objPos 3 component object coordinate, the result
1556 * @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z)
1557 */
1558 static bool mapWinToObj(const value_type winx, const value_type winy, const value_type winz,
1559 const Matrix4& mMv, const Matrix4& mP,
1560 const Recti& viewport,
1561 Vec3& objPos) noexcept
1562 {
1563 // invPMv = Inv(P x Mv)
1564 Matrix4 invPMv;
1565 invPMv.mul(mP, mMv);
1566 if( !invPMv.invert() ) {
1567 return false;
1568 }
1569 return mapWinToAny(winx, winy, winz, invPMv, viewport, objPos);
1570 }
1571
1572 /**
1573 * Map window coordinates to view coordinates.
1574 *
1575 * @param winx
1576 * @param winy
1577 * @param winz
1578 * @param mP 4x4 projection matrix
1579 * @param viewport Rect4i viewport
1580 * @param viewPos 3 component view coordinate, the result
1581 * @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z)
1582 */
1583 static bool mapWinToView(const value_type winx, const value_type winy, const value_type winz,
1584 const Matrix4& mP,
1585 const Recti& viewport,
1586 Vec3& viewPos) noexcept
1587 {
1588 // invP = Inv(P)
1589 Matrix4 invP;
1590 if( !invP.invert(mP) ) {
1591 return false;
1592 }
1593 return mapWinToAny(winx, winy, winz, invP, viewport, viewPos);
1594 }
1595
1596 /**
1597 * Map window coordinates to object, world or view coordinates, depending on `invAny` argument.
1598 *
1599 * Traditional <code>gluUnProject</code> implementation.
1600 *
1601 * `invAny` maybe set as follows for
1602 * - to object: inverse(P x Mv) = `([projection] x [modelview])'`
1603 * - to world: inverse(P x V) = `([projection] x [view])'`
1604 * - to view: inverse(P) = `[projection]'`
1605 *
1606 * @param winx
1607 * @param winy
1608 * @param winz
1609 * @param invAny inverse matrix, either Inv(P x Mv) to object, Inv(P x V) to world or
1610 Inv(P) `[projection]'` to view
1611 * @param viewport Rect4i viewport
1612 * @param objPos 3 component object coordinate, the result
1613 * @return true if successful, otherwise false (can't invert matrix, or becomes infinity due to zero z)
1614 */
1615 static bool mapWinToAny(const value_type winx, const value_type winy, const value_type winz,
1616 const Matrix4& invAny,
1617 const Recti& viewport,
1618 Vec3& objPos) noexcept
1619 {
1620 Vec4 winPos(winx, winy, winz, one);
1621
1622 // Map x and y from window coordinates
1623 winPos.add(-viewport.x(), -viewport.y(), zero, zero).mul(one/viewport.width(), one/viewport.height(), one, one);
1624
1625 // Map to range -1 to 1
1626 winPos.mul(two, two, two, one).add(-one, -one, -one, zero);
1627
1628 // rawObjPos = Inv(P x Mv) * winPos
1629 Vec4 rawObjPos = invAny * winPos;
1630
1631 if ( zero == rawObjPos.w ) {
1632 return false;
1633 }
1634
1635 rawObjPos.scale(one / rawObjPos.w).getVec3(objPos);
1636 return true;
1637 }
1638
1639 /**
1640 * Map two window coordinates to two to object, world or view coordinates, depending on `invAny` argument.
1641 *
1642 * Both coordinates are distinguished by their z component.
1643 *
1644 * Traditional <code>gluUnProject</code> implementation.
1645 *
1646 * `invAny` maybe set as follows for
1647 * - to object: inverse(P x Mv) = `([projection] x [modelview])'`
1648 * - to world: inverse(P x V) = `([projection] x [view])'`
1649 * - to view: inverse(P) = `[projection]'`
1650 *
1651 * @param winx
1652 * @param winy
1653 * @param winz1
1654 * @param winz2
1655 * @param invAny inverse matrix, either Inv(P x Mv) to object, Inv(P x V) to world or
1656 Inv(P) `[projection]'` to view
1657 * @param viewport Rect4i viewport vector
1658 * @param objPos1 3 component object coordinate, the result
1659 * @return true if successful, otherwise false (can't invert matrix, or becomes infinity due to zero z)
1660 */
1661 static bool mapWinToAny(const value_type winx, const value_type winy, const value_type winz1, const value_type winz2,
1662 const Matrix4& invAny,
1663 const Recti& viewport,
1664 Vec3& objPos1, Vec3& objPos2) noexcept
1665 {
1666 Vec4 winPos(winx, winy, winz1, one);
1667
1668 // Map x and y from window coordinates
1669 winPos.add(-viewport.x(), -viewport.y(), zero, zero).mul(one/viewport.width(), one/viewport.height(), one, one);
1670
1671 // Map to range -1 to 1
1672 winPos.mul(two, two, two, one).add(-one, -one, -one, zero);
1673
1674 // rawObjPos = Inv(P x Mv) x winPos1
1675 Vec4 rawObjPos = invAny * winPos;
1676
1677 if ( zero == rawObjPos.w ) {
1678 return false;
1679 }
1680 rawObjPos.scale(one / rawObjPos.w).getVec3(objPos1);
1681
1682 //
1683 // winz2
1684 //
1685 // Map Z to range -1 to 1
1686 winPos.z = winz2 * two - one;
1687
1688 // rawObjPos = Inv(P x Mv) x winPos2
1689 invAny.mulVec4(winPos, rawObjPos);
1690
1691 if ( zero == rawObjPos.w ) {
1692 return false;
1693 }
1694 rawObjPos.scale(one / rawObjPos.w).getVec3(objPos2);
1695
1696 return true;
1697 }
1698
1699 /**
1700 * Map window coordinates to object coordinates.
1701 *
1702 * Traditional <code>gluUnProject4</code> implementation.
1703 *
1704 * @param winx
1705 * @param winy
1706 * @param winz
1707 * @param clipw
1708 * @param mMv 4x4 modelview matrix
1709 * @param mP 4x4 projection matrix
1710 * @param viewport Rect4i viewport vector
1711 * @param near
1712 * @param far
1713 * @param obj_pos 4 component object coordinate, the result
1714 * @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z)
1715 */
1716 static bool mapWinToObj4(const value_type winx, const value_type winy, const value_type winz, const value_type clipw,
1717 const Matrix4& mMv, const Matrix4& mP,
1718 const Recti& viewport,
1719 const value_type near, const value_type far,
1720 Vec4& objPos) noexcept
1721 {
1722 // invPMv = Inv(P x Mv)
1723 Matrix4 invPMv;
1724 invPMv.mul(mP, mMv);
1725 if( !invPMv.invert() ) {
1726 return false;
1727 }
1728 return mapWinToObj4(winx, winy, winz, clipw,
1729 invPMv, viewport, near, far, objPos);
1730 }
1731
1732 /**
1733 * Map window coordinates to object coordinates.
1734 * <p>
1735 * Traditional <code>gluUnProject4</code> implementation.
1736 * </p>
1737 *
1738 * @param winx
1739 * @param winy
1740 * @param winz
1741 * @param clipw
1742 * @param invPMv inverse [projection] x [modelview] matrix, i.e. Inv(P x Mv), if null method returns false
1743 * @param viewport Rect4i viewport vector
1744 * @param near
1745 * @param far
1746 * @param obj_pos 4 component object coordinate, the result
1747 * @return true if successful, otherwise false (null invert matrix, or becomes infinity due to zero z)
1748 */
1749 static bool mapWinToObj4(const value_type winx, const value_type winy, const value_type winz, const value_type clipw,
1750 const Matrix4& invPMv,
1751 const Recti& viewport,
1752 const value_type near, const value_type far,
1753 Vec4& objPos) noexcept
1754 {
1755 Vec4 winPos(winx, winy, winz, clipw);
1756
1757 // Map x and y from window coordinates
1758 winPos.add(-viewport.x(), -viewport.y(), -near, zero).mul(one/viewport.width(), one/viewport.height(), one/(far-near), one);
1759
1760 // Map to range -1 to 1
1761 winPos.mul(two, two, two, one).add(-one, -one, -one, zero);
1762
1763 // objPos = Inv(P x Mv) x winPos
1764 invPMv.mulVec4(winPos, objPos);
1765
1766 if ( zero == objPos.w ) {
1767 return false;
1768 }
1769 return true;
1770 }
1771
1772 /**
1773 * Map two window coordinates w/ shared X/Y and distinctive Z
1774 * to a {@link Ray} in object space.
1775 *
1776 * The resulting {@link Ray} maybe used for <i>picking</i>
1777 * using a {@link AABBox#getRayIntersection(vec3f, Ray, value_type, boolean)}
1778 * of a shape also in object space.
1779 *
1780 * Notes for picking <i>winz0</i> and <i>winz1</i>:
1781 * - see jau::math::util::getZBufferEpsilon()
1782 * - see jau::math::util::getZBufferValue()
1783 * - see jau::math::util::getOrthoWinZ()
1784 * @param winx
1785 * @param winy
1786 * @param winz0
1787 * @param winz1
1788 * @param mMv 4x4 modelview matrix
1789 * @param mP 4x4 projection matrix
1790 * @param viewport Rect4i viewport
1791 * @param ray storage for the resulting {@link Ray} in object space
1792 * @return true if successful, otherwise false (failed to invert matrix, or becomes z is infinity)
1793 */
1794 static bool mapWinToObjRay(const value_type winx, const value_type winy, const value_type winz0, const value_type winz1,
1795 const Matrix4& mMv, const Matrix4& mP,
1796 const Recti& viewport,
1797 Ray3& ray) noexcept
1798 {
1799 // invPMv = Inv(P x Mv)
1800 Matrix4 invPMv;
1801 invPMv.mul(mP, mMv);
1802 if( !invPMv.invert() ) {
1803 return false;
1804 }
1805 return mapWinToAnyRay(winx, winy, winz0, winz1, invPMv, viewport, ray);
1806 }
1807
1808 /**
1809 * Map two window coordinates w/ shared X/Y and distinctive Z
1810 * to a {@link Ray} in view space.
1811 *
1812 * The resulting {@link Ray} maybe used for <i>picking</i>
1813 * using a {@link AABBox#getRayIntersection(vec3f, Ray, value_type, boolean)}
1814 * of a shape also in view space.
1815 *
1816 * Notes for picking <i>winz0</i> and <i>winz1</i>:
1817 * - see jau::math::util::getZBufferEpsilon()
1818 * - see jau::math::util::getZBufferValue()
1819 * - see jau::math::util::getOrthoWinZ()
1820 * @param winx
1821 * @param winy
1822 * @param winz0
1823 * @param winz1
1824 * @param mMv 4x4 modelview matrix
1825 * @param mP 4x4 projection matrix
1826 * @param viewport Rect4i viewport
1827 * @param ray storage for the resulting {@link Ray} in view space
1828 * @return true if successful, otherwise false (failed to invert matrix, or becomes z is infinity)
1829 */
1830 static bool mapWinToViewRay(const value_type winx, const value_type winy, const value_type winz0, const value_type winz1,
1831 const Matrix4& mP,
1832 const Recti& viewport,
1833 Ray3& ray) noexcept
1834 {
1835 // invP = Inv(P)
1836 Matrix4 invP;
1837 if( !invP.invert(mP) ) {
1838 return false;
1839 }
1840 return mapWinToAnyRay(winx, winy, winz0, winz1, invP, viewport, ray);
1841 }
1842
1843 /**
1844 * Map two window coordinates w/ shared X/Y and distinctive Z
1845 * to a {@link Ray} in object, world or view coordinates, depending on `invAny` argument.
1846 *
1847 * The resulting {@link Ray} maybe used for <i>picking</i>
1848 * using a {@link AABBox#getRayIntersection(vec3f, Ray, value_type, boolean)}
1849 * of a shape also in object, world or view space, see `invAny`.
1850 *
1851 * `invAny` maybe set as follows for
1852 * - to object: inverse(P x Mv) = `([projection] x [modelview])'`
1853 * - to world: inverse(P x V) = `([projection] x [view])'`
1854 * - to view: inverse(P) = `[projection]'`
1855 *
1856 * Notes for picking <i>winz0</i> and <i>winz1</i>:
1857 * - see jau::math::util::getZBufferEpsilon()
1858 * - see jau::math::util::getZBufferValue()
1859 * - see jau::math::util::getOrthoWinZ()
1860 * @param winx
1861 * @param winy
1862 * @param winz0
1863 * @param winz1
1864 * @param invAny inverse matrix, either Inv(P x Mv) to object, Inv(P x V) to world or
1865 Inv(P) `[projection]'` to view
1866 * @param viewport Rect4i viewport
1867 * @param ray storage for the resulting {@link Ray} in object space
1868 * @return true if successful, otherwise false (failed invert matrix, or becomes z is infinity)
1869 */
1870 static bool mapWinToAnyRay(const value_type winx, const value_type winy, const value_type winz0, const value_type winz1,
1871 const Matrix4& invAny,
1872 const Recti& viewport,
1873 Ray3& ray) noexcept
1874 {
1875 if( mapWinToAny(winx, winy, winz0, winz1, invAny, viewport, ray.orig, ray.dir) ) {
1876 (ray.dir -= ray.orig).normalize();
1877 return true;
1878 } else {
1879 return false;
1880 }
1881 }
1882
1883 /**
1884 * Returns a formatted string representation of this matrix
1885 * @param rowPrefix prefix for each row
1886 * @param f format string for each value_type element, e.g. "%10.5f"
1887 */
1888 std::string toString(const std::string& rowPrefix, const std::string& f) const noexcept {
1889 std::string sb;
1890 value_type tmp[16];
1891 get(tmp);
1892 return jau::mat_to_string(sb, rowPrefix, f, tmp, 4, 4, false /* rowMajorOrder */); // creates a copy-out!
1893 }
1894
1895 /**
1896 * Returns a formatted string representation of this matrix
1897 * @param rowPrefix prefix for each row
1898 */
1899 std::string toString(const std::string& rowPrefix) const noexcept { return toString(rowPrefix, "%13.9f"); }
1900
1901 std::string toString() const noexcept { return toString("", "%13.9f"); }
1902};
1903
1904template<typename T,
1905 std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
1906constexpr Matrix4<T> operator*(const Matrix4<T>& lhs, const Matrix4<T>& rhs ) noexcept {
1907 Matrix4<T> r(lhs); r.mul(rhs); return r;
1908}
1909
1910template<typename T,
1911 std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
1912constexpr Matrix4<T> operator*(const Matrix4<T>& lhs, const T s ) noexcept {
1913 Matrix4<T> r(lhs); r *= s; return r;
1914}
1915
1916template<typename T,
1917 std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
1918constexpr Matrix4<T> operator*(const T s, const Matrix4<T>& rhs) noexcept {
1919 Matrix4<T> r(rhs); r *= s; return r;
1920}
1921
1922template<typename T,
1923 std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
1924std::ostream& operator<<(std::ostream& out, const Matrix4<T>& v) noexcept {
1925 return out << v.toString();
1926}
1927
1929
1930static_assert(alignof(float) == alignof(Mat4f));
1931static_assert(sizeof(float)*16 == sizeof(Mat4f));
1932
1933/**@}*/
1934
1935} // namespace jau::math
1936
1937#endif // JAU_MATH_MAT4f_HPP_
#define E_FILE_LINE
Horizontal and vertical field of view (FOV) halves, allowing a non-centered projection.
float top
Half vertical FOV from center to top, either in inTangents or radians.
float right
Half horizontal FOV from center to right, either in inTangents or radians.
float left
Half horizontal FOV from center to left, either in inTangents or radians.
FovHVHalves toTangents() const noexcept
Returns this instance in tangent values.
float bottom
Half vertical FOV from center to bottom, either in inTangents or radians.
Basic 4x4 value_type matrix implementation using fields for intensive use-cases (host operations).
Definition mat4f.hpp:101
constexpr Matrix4 & scale(const value_type s) noexcept
Scale this matrix, i.e.
Definition mat4f.hpp:1442
constexpr Matrix4 & setToScale(const value_type x, const value_type y, const value_type z) noexcept
Set this matrix to scale.
Definition mat4f.hpp:954
constexpr bool operator==(const Matrix4 &rhs) const noexcept
Definition mat4f.hpp:205
constexpr value_type get(const jau::nsize_t i) const noexcept
Returns the ith component of the given column-major order matrix, 0 <= i < 16, w/o boundary check.
Definition mat4f.hpp:295
constexpr iterator get(iterator dst) const noexcept
Get this matrix into the given value_type[16] array in column major order w/o boundary check.
Definition mat4f.hpp:388
Vector4F< value_type, std::is_floating_point_v< Value_type > > Vec4
Definition mat4f.hpp:112
static bool mapViewToWin(const Vec3 &view, const Matrix4 &mP, const Recti &viewport, Vec3 &winPos) noexcept
Map view coordinates ( Mv x object ) to window coordinates.
Definition mat4f.hpp:1515
constexpr_cxx26 Matrix4 & setToRotationAxis(const value_type ang_rad, value_type x, value_type y, value_type z) noexcept
Set this matrix to rotation from the given axis and angle in radians.
Definition mat4f.hpp:998
value_type * pointer
Definition mat4f.hpp:104
constexpr Matrix4 & translate(const value_type x, const value_type y, const value_type z) noexcept
Translate this matrix, i.e.
Definition mat4f.hpp:1410
constexpr Vec4 getRow(const jau::nsize_t row) const noexcept
Get the named column of the given column-major matrix to v_out w/o boundary check.
Definition mat4f.hpp:360
static constexpr const value_type one
Definition mat4f.hpp:116
std::string toString() const noexcept
Definition mat4f.hpp:1901
constexpr void set(const jau::nsize_t i, const value_type v) noexcept
Sets the ith component of this column-major order matrix with value_type v, 0 <= i < 16 w/o boundary ...
Definition mat4f.hpp:220
constexpr value_type operator[](size_t i) const noexcept
Returns read-only ith component of the given column-major order matrix, 0 <= i < 16 w/o boundary chec...
Definition mat4f.hpp:289
constexpr_cxx26 Matrix4 & setToRotationAxis(const value_type ang_rad, const Vec3 &axis) noexcept
Set this matrix to rotation from the given axis and angle in radians.
Definition mat4f.hpp:1049
bool invert() noexcept
Invert this matrix.
Definition mat4f.hpp:510
static bool mapWinToViewRay(const value_type winx, const value_type winy, const value_type winz0, const value_type winz1, const Matrix4 &mP, const Recti &viewport, Ray3 &ray) noexcept
Map two window coordinates w/ shared X/Y and distinctive Z to a Ray in view space.
Definition mat4f.hpp:1830
Matrix4 & setToPerspective(const FovHVHalves &fovhv, const value_type zNear, const value_type zFar)
Set this matrix to perspective frustum projection.
Definition mat4f.hpp:1266
Value_type value_type
Definition mat4f.hpp:103
constexpr Vec3 & getColumn(const jau::nsize_t column, Vec3 &v_out) const noexcept
Get the named column of the given column-major matrix to v_out w/o boundary check.
Definition mat4f.hpp:336
constexpr Vec3 & mulVec3(const Vec3 &v_in, Vec3 &v_out) const noexcept
Affine 3f-vector transformation by 4x4 matrix.
Definition mat4f.hpp:850
constexpr const_iterator cbegin() const noexcept
Definition mat4f.hpp:301
constexpr reference operator[](size_t i) noexcept
Returns writable reference to the ith component of this column-major order matrix,...
Definition mat4f.hpp:214
Ray3F< value_type, std::is_floating_point_v< Value_type > > Ray3
Definition mat4f.hpp:113
Matrix4 & transpose() noexcept
Transpose this matrix.
Definition mat4f.hpp:444
static bool mapWinToAny(const value_type winx, const value_type winy, const value_type winz1, const value_type winz2, const Matrix4 &invAny, const Recti &viewport, Vec3 &objPos1, Vec3 &objPos2) noexcept
Map two window coordinates to two to object, world or view coordinates, depending on invAny argument.
Definition mat4f.hpp:1661
static bool mapWinToView(const value_type winx, const value_type winy, const value_type winz, const Matrix4 &mP, const Recti &viewport, Vec3 &viewPos) noexcept
Map window coordinates to view coordinates.
Definition mat4f.hpp:1583
constexpr Vec4 & mulVec4(const Vec4 &v_in, Vec4 &v_out) const noexcept
Definition mat4f.hpp:802
constexpr Vec4 getColumn(const jau::nsize_t column) const noexcept
Get the named column of the given column-major matrix to v_out w/o boundary check.
Definition mat4f.hpp:322
value_type determinant() const noexcept
Returns the determinant of this matrix.
Definition mat4f.hpp:430
static bool mapWinToObj4(const value_type winx, const value_type winy, const value_type winz, const value_type clipw, const Matrix4 &mMv, const Matrix4 &mP, const Recti &viewport, const value_type near, const value_type far, Vec4 &objPos) noexcept
Map window coordinates to object coordinates.
Definition mat4f.hpp:1716
constexpr_cxx26 Matrix4 & rotate(const value_type ang_rad, const value_type x, const value_type y, const value_type z) noexcept
Rotate this matrix about give axis and angle in radians, i.e.
Definition mat4f.hpp:1386
Matrix4 & setToPerspective(const value_type fovy_rad, const value_type aspect, const value_type zNear, const value_type zFar)
Set this matrix to perspective frustum projection.
Definition mat4f.hpp:1247
constexpr Matrix4 & translate(const Vec3 &t) noexcept
Translate this matrix, i.e.
Definition mat4f.hpp:1420
constexpr bool equals(const Matrix4 &o, const value_type epsilon=std::numeric_limits< value_type >::epsilon()) const noexcept
Definition mat4f.hpp:183
const value_type & const_reference
Definition mat4f.hpp:107
constexpr Matrix4 & scale(const value_type x, const value_type y, const value_type z) noexcept
Definition mat4f.hpp:1432
constexpr Vec3 & mulVec3(Vec3 &v_inout) const noexcept
Affine 3f-vector transformation by 4x4 matrix: v_inout = this * v_inout.
Definition mat4f.hpp:885
constexpr iterator begin() noexcept
Definition mat4f.hpp:226
static bool mapObjToWin(const Vec3 &obj, const Matrix4 &mPMv, const Recti &viewport, Vec3 &winPos) noexcept
Map object coordinates to window coordinates.
Definition mat4f.hpp:1462
std::string toString(const std::string &rowPrefix, const std::string &f) const noexcept
Definition mat4f.hpp:1888
static bool mapWinToObj(const value_type winx, const value_type winy, const value_type winz, const Matrix4 &mMv, const Matrix4 &mP, const Recti &viewport, Vec3 &objPos) noexcept
Map window coordinates to object coordinates.
Definition mat4f.hpp:1558
Matrix4 & setToFrustum(const value_type left, const value_type right, const value_type bottom, const value_type top, const value_type zNear, const value_type zFar)
Set this matrix to frustum.
Definition mat4f.hpp:1198
constexpr Vec3 operator*(const Vec3 &rhs) const noexcept
Returns new Vec3, with affine 3f-vector transformation by this 4x4 matrix: this x v_in.
Definition mat4f.hpp:867
constexpr Matrix4 & mul(const Matrix4 &a, const Matrix4 &b) noexcept
Multiply matrix: [this] = [a] x [b].
Definition mat4f.hpp:769
Matrix4 & transpose(const Matrix4 &src) noexcept
Transpose the given src matrix into this matrix.
Definition mat4f.hpp:480
constexpr Vec4 & getColumn(const jau::nsize_t column, Vec4 &v_out) const noexcept
Get the named column of the given column-major matrix to v_out w/o boundary check.
Definition mat4f.hpp:309
static bool mapWinToAny(const value_type winx, const value_type winy, const value_type winz, const Matrix4 &invAny, const Recti &viewport, Vec3 &objPos) noexcept
Definition mat4f.hpp:1615
static bool mapObjToWin(const Vec3 &obj, const Matrix4 &mMv, const Matrix4 &mP, const Recti &viewport, Vec3 &winPos) noexcept
Map object coordinates to window coordinates.
Definition mat4f.hpp:1482
constexpr Vec4 operator*(const Vec4 &rhs) const noexcept
Returns new Vec4, with this x v_in.
Definition mat4f.hpp:816
constexpr Vec4 & getRow(const jau::nsize_t row, Vec4 &v_out) const noexcept
Get the named row of the given column-major matrix to v_out w/ boundary check.
Definition mat4f.hpp:348
constexpr Matrix4 & operator*=(const Matrix4 &rhs) noexcept
Multiply matrix: [this] = [this] x [b].
Definition mat4f.hpp:758
constexpr Matrix4 & loadIdentity() noexcept
Set this matrix to identity.
Definition mat4f.hpp:239
constexpr Matrix4 & operator=(const Matrix4 &o) noexcept
Copy assignment using the the values of the given src matrix.
Definition mat4f.hpp:181
static constexpr const value_type half
Definition mat4f.hpp:118
constexpr bool setToPick(const value_type x, const value_type y, const value_type deltaX, const value_type deltaY, const Recti &viewport) noexcept
Set this matrix to the pick matrix based on given parameters.
Definition mat4f.hpp:1358
static constexpr const value_type inv_deviation
Definition mat4f.hpp:126
static constexpr const value_type zero
Definition mat4f.hpp:115
static bool mapWinToObjRay(const value_type winx, const value_type winy, const value_type winz0, const value_type winz1, const Matrix4 &mMv, const Matrix4 &mP, const Recti &viewport, Ray3 &ray) noexcept
Map two window coordinates w/ shared X/Y and distinctive Z to a Ray in object space.
Definition mat4f.hpp:1794
constexpr Vec4 & mulVec4(Vec4 &v_inout) const noexcept
Definition mat4f.hpp:829
std::string toString(const std::string &rowPrefix) const noexcept
Returns a formatted string representation of this matrix.
Definition mat4f.hpp:1899
static bool mapWorldToWin(const Vec3 &world, const Matrix4 &mV, const Matrix4 &mP, const Recti &viewport, Vec3 &winPos) noexcept
Map world coordinates ( M x object ) to window coordinates.
Definition mat4f.hpp:1499
constexpr Matrix4 & setToOrtho(const value_type left, const value_type right, const value_type bottom, const value_type top, const value_type zNear, const value_type zFar) noexcept
Set this matrix to orthogonal projection.
Definition mat4f.hpp:1150
constexpr_cxx26 Matrix4 & setToRotationEuler(const value_type bankX, const value_type headingY, const value_type attitudeZ) noexcept
Set this matrix to rotation from the given Euler rotation angles in radians.
Definition mat4f.hpp:1076
constexpr std::vector< value_type > & get(std::vector< value_type > &dst, size_t dst_off) const noexcept
Get this matrix into the given FloatBuffer in column major order.
Definition mat4f.hpp:416
static bool mapWinToAnyRay(const value_type winx, const value_type winy, const value_type winz0, const value_type winz1, const Matrix4 &invAny, const Recti &viewport, Ray3 &ray) noexcept
Definition mat4f.hpp:1870
constexpr Matrix4 & setToScale(const Vec3 &s) noexcept
Set this matrix to scale.
Definition mat4f.hpp:978
Vector3F< value_type, std::is_floating_point_v< Value_type > > Vec3
Definition mat4f.hpp:111
constexpr Matrix4 & operator*=(const value_type s) noexcept
Multiply matrix with scalar: [this] = [this] x [s].
Definition mat4f.hpp:699
constexpr Matrix4(const_iterator m) noexcept
Creates a new matrix based on given value_type[4*4] column major order.
Definition mat4f.hpp:154
bool invert(const Matrix4 &src) noexcept
Invert the src matrix values into this matrix.
Definition mat4f.hpp:591
constexpr_cxx26 Matrix4 & rotate(const value_type ang_rad, const Vec3 &axis) noexcept
Rotate this matrix about give axis and angle in radians, i.e.
Definition mat4f.hpp:1398
constexpr_cxx26 Matrix4 & setToRotationEuler(const Vec3 &angradXYZ) noexcept
Set this matrix to rotation from the given Euler rotation angles in radians.
Definition mat4f.hpp:1129
constexpr Matrix4 & setToTranslation(const Vec3 &t) noexcept
Set this matrix to translation.
Definition mat4f.hpp:936
constexpr Matrix4() noexcept
Creates a new identity matrix.
Definition mat4f.hpp:143
value_type * iterator
Definition mat4f.hpp:108
const value_type * const_iterator
Definition mat4f.hpp:109
constexpr Matrix4 & mul(const Matrix4 &b) noexcept
Multiply matrix: [this] = [this] x [b].
Definition mat4f.hpp:713
constexpr Matrix4 & load(const_iterator src) noexcept
Definition mat4f.hpp:253
static bool mapWinToObj4(const value_type winx, const value_type winy, const value_type winz, const value_type clipw, const Matrix4 &invPMv, const Recti &viewport, const value_type near, const value_type far, Vec4 &objPos) noexcept
Map window coordinates to object coordinates.
Definition mat4f.hpp:1749
value_type & reference
Definition mat4f.hpp:106
constexpr Matrix4(const Matrix4 &o) noexcept
Creates a new matrix copying the values of the given src matrix.
Definition mat4f.hpp:174
constexpr Vec3 & getRow(const jau::nsize_t row, Vec3 &v_out) const noexcept
Get the named row of the given column-major matrix to v_out w/o boundary check.
Definition mat4f.hpp:374
constexpr Matrix4 & load(const Matrix4 &src) noexcept
Load the values of the given matrix src to this matrix w/o boundary check.
Definition mat4f.hpp:278
constexpr Matrix4 & setToTranslation(const value_type x, const value_type y, const value_type z) noexcept
Set this matrix to translation.
Definition mat4f.hpp:912
const value_type * const_pointer
Definition mat4f.hpp:105
static constexpr const value_type two
Definition mat4f.hpp:117
constexpr Matrix4(std::initializer_list< value_type > m) noexcept
Creates a new matrix based on given value_type initializer list in column major order.
Definition mat4f.hpp:165
constexpr Matrix4 & setToLookAt(const Vec3 &eye, const Vec3 &center, const Vec3 &up) noexcept
Set this matrix to the look-at matrix based on given parameters.
Definition mat4f.hpp:1294
Quaternion implementation supporting Gimbal-Lock free rotations.
Simple compound denoting a ray.
Definition vec3f.hpp:469
3D vector using three value_type components.
Definition vec3f.hpp:45
value_type x
Definition vec3f.hpp:69
constexpr Vector3F & normalize() noexcept
Normalize this vector in place.
Definition vec3f.hpp:259
constexpr Vector3F cross(const Vector3F &b) const noexcept
cross product this x b
Definition vec3f.hpp:307
value_type y
Definition vec3f.hpp:70
value_type z
Definition vec3f.hpp:71
4D vector using four value_type components.
Definition vec4f.hpp:45
value_type w
Definition vec4f.hpp:72
constexpr Vec3 & getVec3(Vec3 &out) const noexcept
out = { this.x, this.y, this.z } dropping w, returns out.
Definition vec4f.hpp:122
constexpr Vector4F & mul(const Vector4F &s) noexcept
this = this * {s.x, s.y, s.z}, component wise.
Definition vec4f.hpp:157
constexpr Vector4F & add(const Vector4F &d) noexcept
this = this + {d.x, d.y, d.z, d.w}, component wise.
Definition vec4f.hpp:149
value_type z
Definition vec4f.hpp:71
constexpr Vector4F & scale(const value_type s) noexcept
this = this * s, component wise.
Definition vec4f.hpp:165
Providing frustum planes derived by different inputs (P*MV, ..) used to classify objects.
Definition frustum.hpp:78
#define DBG_PRINT(...)
Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] De...
Definition debug.hpp:52
#define constexpr_cxx26
std::enable_if_t< std::is_floating_point_v< T >, bool > constexpr equals(const T &a, const T &b, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if both values are equal, i.e.
std::string & mat_to_string(std::string &sb, const std::string &rowPrefix, const std::string &f, const T a[], const jau::nsize_t rows, const jau::nsize_t columns, const bool rowMajorOrder) noexcept
Appends a matrix of floating points to the given string sb
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition int_types.hpp:55
Matrix4< float > Mat4f
Definition mat4f.hpp:1928
constexpr Vector2F< T > max(const Vector2F< T > &lhs, const Vector2F< T > &rhs) noexcept
Definition vec2f.hpp:398
RectI< int > Recti
Definition recti.hpp:139
constexpr Matrix4< T > operator*(const Matrix4< T > &lhs, const Matrix4< T > &rhs) noexcept
Definition mat4f.hpp:1906
std::ostream & operator<<(std::ostream &out, const Addr48Bit &a)
uint8_t Value_type