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