::max() - 15 );
get( &dst[dst_off++] );
return dst;
}
//
// Basic matrix operations
//
/**
* Returns the determinant of this matrix
* @return the matrix determinant
*/
value_type determinant() const noexcept {
value_type ret = 0;
ret += m00 * ( + m11*(m22*m33 - m23*m32) - m12*(m21*m33 - m23*m31) + m13*(m21*m32 - m22*m31));
ret -= m01 * ( + m10*(m22*m33 - m23*m32) - m12*(m20*m33 - m23*m30) + m13*(m20*m32 - m22*m30));
ret += m02 * ( + m10*(m21*m33 - m23*m31) - m11*(m20*m33 - m23*m30) + m13*(m20*m31 - m21*m30));
ret -= m03 * ( + m10*(m21*m32 - m22*m31) - m11*(m20*m32 - m22*m30) + m12*(m20*m31 - m21*m30));
return ret;
}
/**
* Transpose this matrix.
*
* @return this matrix for chaining
*/
Matrix4& transpose() noexcept {
value_type tmp;
tmp = m10;
m10 = m01;
m01 = tmp;
tmp = m20;
m20 = m02;
m02 = tmp;
tmp = m30;
m30 = m03;
m03 = tmp;
tmp = m21;
m21 = m12;
m12 = tmp;
tmp = m31;
m31 = m13;
m13 = tmp;
tmp = m32;
m32 = m23;
m23 = tmp;
return *this;
}
/**
* Transpose the given {@code src} matrix into this matrix.
*
* @param src source 4x4 matrix
* @return this matrix (result) for chaining
*/
Matrix4& transpose(const Matrix4& src) noexcept {
if( &src == this ) {
return transpose();
}
m00 = src.m00;
m10 = src.m01;
m20 = src.m02;
m30 = src.m03;
m01 = src.m10;
m11 = src.m11;
m21 = src.m12;
m31 = src.m13;
m02 = src.m20;
m12 = src.m21;
m22 = src.m22;
m32 = src.m23;
m03 = src.m30;
m13 = src.m31;
m23 = src.m32;
m33 = src.m33;
return *this;
}
/**
* Invert this matrix.
* @return false if this matrix is singular and inversion not possible, otherwise true
*/
bool invert() noexcept {
const value_type amax = absMax();
if( zero == amax ) {
return false;
}
const value_type scale = one/amax;
const value_type a00 = m00*scale;
const value_type a10 = m10*scale;
const value_type a20 = m20*scale;
const value_type a30 = m30*scale;
const value_type a01 = m01*scale;
const value_type a11 = m11*scale;
const value_type a21 = m21*scale;
const value_type a31 = m31*scale;
const value_type a02 = m02*scale;
const value_type a12 = m12*scale;
const value_type a22 = m22*scale;
const value_type a32 = m32*scale;
const value_type a03 = m03*scale;
const value_type a13 = m13*scale;
const value_type a23 = m23*scale;
const value_type a33 = m33*scale;
const value_type b00 = + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31);
const value_type b01 = -( + a10*(a22*a33 - a23*a32) - a12*(a20*a33 - a23*a30) + a13*(a20*a32 - a22*a30));
const value_type b02 = + a10*(a21*a33 - a23*a31) - a11*(a20*a33 - a23*a30) + a13*(a20*a31 - a21*a30);
const value_type b03 = -( + a10*(a21*a32 - a22*a31) - a11*(a20*a32 - a22*a30) + a12*(a20*a31 - a21*a30));
const value_type b10 = -( + a01*(a22*a33 - a23*a32) - a02*(a21*a33 - a23*a31) + a03*(a21*a32 - a22*a31));
const value_type b11 = + a00*(a22*a33 - a23*a32) - a02*(a20*a33 - a23*a30) + a03*(a20*a32 - a22*a30);
const value_type b12 = -( + a00*(a21*a33 - a23*a31) - a01*(a20*a33 - a23*a30) + a03*(a20*a31 - a21*a30));
const value_type b13 = + a00*(a21*a32 - a22*a31) - a01*(a20*a32 - a22*a30) + a02*(a20*a31 - a21*a30);
const value_type b20 = + a01*(a12*a33 - a13*a32) - a02*(a11*a33 - a13*a31) + a03*(a11*a32 - a12*a31);
const value_type b21 = -( + a00*(a12*a33 - a13*a32) - a02*(a10*a33 - a13*a30) + a03*(a10*a32 - a12*a30));
const value_type b22 = + a00*(a11*a33 - a13*a31) - a01*(a10*a33 - a13*a30) + a03*(a10*a31 - a11*a30);
const value_type b23 = -( + a00*(a11*a32 - a12*a31) - a01*(a10*a32 - a12*a30) + a02*(a10*a31 - a11*a30));
const value_type b30 = -( + a01*(a12*a23 - a13*a22) - a02*(a11*a23 - a13*a21) + a03*(a11*a22 - a12*a21));
const value_type b31 = + a00*(a12*a23 - a13*a22) - a02*(a10*a23 - a13*a20) + a03*(a10*a22 - a12*a20);
const value_type b32 = -( + a00*(a11*a23 - a13*a21) - a01*(a10*a23 - a13*a20) + a03*(a10*a21 - a11*a20));
const value_type b33 = + a00*(a11*a22 - a12*a21) - a01*(a10*a22 - a12*a20) + a02*(a10*a21 - a11*a20);
const value_type det = (a00*b00 + a01*b01 + a02*b02 + a03*b03) / scale;
if( 0 == det ) {
return false;
}
const value_type invdet = one / det;
m00 = b00 * invdet;
m10 = b01 * invdet;
m20 = b02 * invdet;
m30 = b03 * invdet;
m01 = b10 * invdet;
m11 = b11 * invdet;
m21 = b12 * invdet;
m31 = b13 * invdet;
m02 = b20 * invdet;
m12 = b21 * invdet;
m22 = b22 * invdet;
m32 = b23 * invdet;
m03 = b30 * invdet;
m13 = b31 * invdet;
m23 = b32 * invdet;
m33 = b33 * invdet;
return true;
}
/**
* Invert the {@code src} matrix values into this matrix
* @param src the source matrix, which values are to be inverted
* @return false if {@code src} matrix is singular and inversion not possible, otherwise true
*/
bool invert(const Matrix4& src) noexcept {
const value_type amax = src.absMax();
if( zero == amax ) {
return false;
}
const value_type scale = one/amax;
const value_type a00 = src.m00*scale;
const value_type a10 = src.m10*scale;
const value_type a20 = src.m20*scale;
const value_type a30 = src.m30*scale;
const value_type a01 = src.m01*scale;
const value_type a11 = src.m11*scale;
const value_type a21 = src.m21*scale;
const value_type a31 = src.m31*scale;
const value_type a02 = src.m02*scale;
const value_type a12 = src.m12*scale;
const value_type a22 = src.m22*scale;
const value_type a32 = src.m32*scale;
const value_type a03 = src.m03*scale;
const value_type a13 = src.m13*scale;
const value_type a23 = src.m23*scale;
const value_type a33 = src.m33*scale;
const value_type b00 = + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31);
const value_type b01 = -( + a10*(a22*a33 - a23*a32) - a12*(a20*a33 - a23*a30) + a13*(a20*a32 - a22*a30));
const value_type b02 = + a10*(a21*a33 - a23*a31) - a11*(a20*a33 - a23*a30) + a13*(a20*a31 - a21*a30);
const value_type b03 = -( + a10*(a21*a32 - a22*a31) - a11*(a20*a32 - a22*a30) + a12*(a20*a31 - a21*a30));
const value_type b10 = -( + a01*(a22*a33 - a23*a32) - a02*(a21*a33 - a23*a31) + a03*(a21*a32 - a22*a31));
const value_type b11 = + a00*(a22*a33 - a23*a32) - a02*(a20*a33 - a23*a30) + a03*(a20*a32 - a22*a30);
const value_type b12 = -( + a00*(a21*a33 - a23*a31) - a01*(a20*a33 - a23*a30) + a03*(a20*a31 - a21*a30));
const value_type b13 = + a00*(a21*a32 - a22*a31) - a01*(a20*a32 - a22*a30) + a02*(a20*a31 - a21*a30);
const value_type b20 = + a01*(a12*a33 - a13*a32) - a02*(a11*a33 - a13*a31) + a03*(a11*a32 - a12*a31);
const value_type b21 = -( + a00*(a12*a33 - a13*a32) - a02*(a10*a33 - a13*a30) + a03*(a10*a32 - a12*a30));
const value_type b22 = + a00*(a11*a33 - a13*a31) - a01*(a10*a33 - a13*a30) + a03*(a10*a31 - a11*a30);
const value_type b23 = -( + a00*(a11*a32 - a12*a31) - a01*(a10*a32 - a12*a30) + a02*(a10*a31 - a11*a30));
const value_type b30 = -( + a01*(a12*a23 - a13*a22) - a02*(a11*a23 - a13*a21) + a03*(a11*a22 - a12*a21));
const value_type b31 = + a00*(a12*a23 - a13*a22) - a02*(a10*a23 - a13*a20) + a03*(a10*a22 - a12*a20);
const value_type b32 = -( + a00*(a11*a23 - a13*a21) - a01*(a10*a23 - a13*a20) + a03*(a10*a21 - a11*a20));
const value_type b33 = + a00*(a11*a22 - a12*a21) - a01*(a10*a22 - a12*a20) + a02*(a10*a21 - a11*a20);
const value_type det = (a00*b00 + a01*b01 + a02*b02 + a03*b03) / scale;
if( 0 == det ) {
return false;
}
const value_type invdet = one / det;
m00 = b00 * invdet;
m10 = b01 * invdet;
m20 = b02 * invdet;
m30 = b03 * invdet;
m01 = b10 * invdet;
m11 = b11 * invdet;
m21 = b12 * invdet;
m31 = b13 * invdet;
m02 = b20 * invdet;
m12 = b21 * invdet;
m22 = b22 * invdet;
m32 = b23 * invdet;
m03 = b30 * invdet;
m13 = b31 * invdet;
m23 = b32 * invdet;
m33 = b33 * invdet;
return true;
}
private:
/** Returns the maximum abs(mxy) field */
value_type absMax() const noexcept {
value_type max = std::abs(m00);
max = std::max(max, std::abs(m01));
max = std::max(max, std::abs(m02));
max = std::max(max, std::abs(m03));
max = std::max(max, std::abs(m10));
max = std::max(max, std::abs(m11));
max = std::max(max, std::abs(m12));
max = std::max(max, std::abs(m13));
max = std::max(max, std::abs(m20));
max = std::max(max, std::abs(m21));
max = std::max(max, std::abs(m22));
max = std::max(max, std::abs(m23));
max = std::max(max, std::abs(m30));
max = std::max(max, std::abs(m31));
max = std::max(max, std::abs(m32));
max = std::max(max, std::abs(m33));
return max;
}
public:
/**
* Multiply matrix with scalar: [this] = [this] x [s]
* @param s a scalar
* @return this matrix for chaining
*/
constexpr Matrix4& operator*=( const value_type s ) noexcept {
m00 *= s; m10 *= s; m20 *= s; m30 *= s;
m01 *= s; m11 *= s; m21 *= s; m31 *= s;
m02 *= s; m12 *= s; m22 *= s; m32 *= s;
m03 *= s; m13 *= s; m23 *= s; m33 *= s;
return *this;
}
/**
* Multiply matrix: [this] = [this] x [b]
* @param b 4x4 matrix
* @return this matrix for chaining
* @see #mul(mat4f, mat4f)
*/
constexpr Matrix4& mul(const Matrix4& b) noexcept {
// return mul(new mat4f(this), b); // <- roughly half speed
value_type ai0=m00; // row-0, m[0+0*4]
value_type ai1=m01;
value_type ai2=m02;
value_type ai3=m03;
m00 = ai0 * b.m00 + ai1 * b.m10 + ai2 * b.m20 + ai3 * b.m30 ;
m01 = ai0 * b.m01 + ai1 * b.m11 + ai2 * b.m21 + ai3 * b.m31 ;
m02 = ai0 * b.m02 + ai1 * b.m12 + ai2 * b.m22 + ai3 * b.m32 ;
m03 = ai0 * b.m03 + ai1 * b.m13 + ai2 * b.m23 + ai3 * b.m33 ;
ai0=m10; //row-1, m[1+0*4]
ai1=m11;
ai2=m12;
ai3=m13;
m10 = ai0 * b.m00 + ai1 * b.m10 + ai2 * b.m20 + ai3 * b.m30 ;
m11 = ai0 * b.m01 + ai1 * b.m11 + ai2 * b.m21 + ai3 * b.m31 ;
m12 = ai0 * b.m02 + ai1 * b.m12 + ai2 * b.m22 + ai3 * b.m32 ;
m13 = ai0 * b.m03 + ai1 * b.m13 + ai2 * b.m23 + ai3 * b.m33 ;
ai0=m20; // row-2, m[2+0*4]
ai1=m21;
ai2=m22;
ai3=m23;
m20 = ai0 * b.m00 + ai1 * b.m10 + ai2 * b.m20 + ai3 * b.m30 ;
m21 = ai0 * b.m01 + ai1 * b.m11 + ai2 * b.m21 + ai3 * b.m31 ;
m22 = ai0 * b.m02 + ai1 * b.m12 + ai2 * b.m22 + ai3 * b.m32 ;
m23 = ai0 * b.m03 + ai1 * b.m13 + ai2 * b.m23 + ai3 * b.m33 ;
ai0=m30; // row-3, m[3+0*4]
ai1=m31;
ai2=m32;
ai3=m33;
m30 = ai0 * b.m00 + ai1 * b.m10 + ai2 * b.m20 + ai3 * b.m30 ;
m31 = ai0 * b.m01 + ai1 * b.m11 + ai2 * b.m21 + ai3 * b.m31 ;
m32 = ai0 * b.m02 + ai1 * b.m12 + ai2 * b.m22 + ai3 * b.m32 ;
m33 = ai0 * b.m03 + ai1 * b.m13 + ai2 * b.m23 + ai3 * b.m33 ;
return *this;
}
/**
* Multiply matrix: [this] = [this] x [b]
* @param b 4x4 matrix
* @return this matrix for chaining
* @see #mul(mat4f, mat4f)
*/
constexpr Matrix4& operator*=( const Matrix4& rhs ) noexcept {
return mul( rhs );
}
/**
* Multiply matrix: [this] = [a] x [b]
* @param a 4x4 matrix, can't be this matrix
* @param b 4x4 matrix, can't be this matrix
* @return this matrix for chaining
* @see #mul(mat4f)
*/
constexpr Matrix4& mul(const Matrix4& a, const Matrix4& b) noexcept {
// row-0, m[0+0*4]
m00 = a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20 + a.m03 * b.m30 ;
m01 = a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21 + a.m03 * b.m31 ;
m02 = a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22 + a.m03 * b.m32 ;
m03 = a.m00 * b.m03 + a.m01 * b.m13 + a.m02 * b.m23 + a.m03 * b.m33 ;
//row-1, m[1+0*4]
m10 = a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20 + a.m13 * b.m30 ;
m11 = a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21 + a.m13 * b.m31 ;
m12 = a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22 + a.m13 * b.m32 ;
m13 = a.m10 * b.m03 + a.m11 * b.m13 + a.m12 * b.m23 + a.m13 * b.m33 ;
// row-2, m[2+0*4]
m20 = a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20 + a.m23 * b.m30 ;
m21 = a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21 + a.m23 * b.m31 ;
m22 = a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22 + a.m23 * b.m32 ;
m23 = a.m20 * b.m03 + a.m21 * b.m13 + a.m22 * b.m23 + a.m23 * b.m33 ;
// row-3, m[3+0*4]
m30 = a.m30 * b.m00 + a.m31 * b.m10 + a.m32 * b.m20 + a.m33 * b.m30 ;
m31 = a.m30 * b.m01 + a.m31 * b.m11 + a.m32 * b.m21 + a.m33 * b.m31 ;
m32 = a.m30 * b.m02 + a.m31 * b.m12 + a.m32 * b.m22 + a.m33 * b.m32 ;
m33 = a.m30 * b.m03 + a.m31 * b.m13 + a.m32 * b.m23 + a.m33 * b.m33 ;
return *this;
}
/**
* @param v_in 4-component column-vector, can be v_out for in-place transformation
* @param v_out this * v_in
* @returns v_out for chaining
*/
constexpr Vec4& mulVec4(const Vec4& v_in, Vec4& v_out) const noexcept {
// (one matrix row in column-major order) X (column vector)
const value_type x = v_in.x, y = v_in.y, z = v_in.z, w = v_in.w;
v_out.set( x * m00 + y * m01 + z * m02 + w * m03,
x * m10 + y * m11 + z * m12 + w * m13,
x * m20 + y * m21 + z * m22 + w * m23,
x * m30 + y * m31 + z * m32 + w * m33 );
return v_out;
}
/**
* Returns new Vec4, with this * v_in
* @param v_in 4-component column-vector
*/
constexpr Vec4 operator*(const Vec4& rhs) const noexcept {
// (one matrix row in column-major order) X (column vector)
const value_type x = rhs.x, y = rhs.y, z = rhs.z, w = rhs.w;
return Vec4( x * m00 + y * m01 + z * m02 + w * m03,
x * m10 + y * m11 + z * m12 + w * m13,
x * m20 + y * m21 + z * m22 + w * m23,
x * m30 + y * m31 + z * m32 + w * m33 );
}
/**
* @param v_inout 4-component column-vector input and output, i.e. in-place transformation
* @returns v_inout for chaining
*/
constexpr Vec4& mulVec4(Vec4& v_inout) const noexcept {
// (one matrix row in column-major order) X (column vector)
const value_type x = v_inout.x, y = v_inout.y, z = v_inout.z, w = v_inout.w;
v_inout.set( x * m00 + y * m01 + z * m02 + w * m03,
x * m10 + y * m11 + z * m12 + w * m13,
x * m20 + y * m21 + z * m22 + w * m23,
x * m30 + y * m31 + z * m32 + w * m33 );
return v_inout;
}
/**
* Affine 3f-vector transformation by 4x4 matrix
*
* 4x4 matrix multiplication with 3-component vector,
* using {@code 1} for for {@code v_in.w} and dropping {@code v_out.w},
* which shall be {@code 1}.
*
* @param v_in 3-component column-vector {@link vec3f}, can be v_out for in-place transformation
* @param v_out m_in * v_in, 3-component column-vector {@link vec3f}
* @returns v_out for chaining
*/
constexpr Vec3& mulVec3(const Vec3& v_in, Vec3& v_out) const noexcept {
// (one matrix row in column-major order) X (column vector)
const value_type x = v_in.x, y = v_in.y, z = v_in.z;
v_out.set( x * m00 + y * m01 + z * m02 + one * m03,
x * m10 + y * m11 + z * m12 + one * m13,
x * m20 + y * m21 + z * m22 + one * m23 );
return v_out;
}
/**
* Returns new Vec3, with affine 3f-vector transformation by this 4x4 matrix: this * v_in
*
* 4x4 matrix multiplication with 3-component vector,
* using {@code 1} for for {@code v_in.w} and dropping {@code v_out.w},
* which shall be {@code 1}.
*
* @param v_in 3-component column-vector {@link vec3f}
*/
constexpr Vec3 operator*(const Vec3& rhs) const noexcept {
// (one matrix row in column-major order) X (column vector)
const value_type x = rhs.x, y = rhs.y, z = rhs.z;
return Vec3( x * m00 + y * m01 + z * m02 + one * m03,
x * m10 + y * m11 + z * m12 + one * m13,
x * m20 + y * m21 + z * m22 + one * m23 );
}
/**
* Affine 3f-vector transformation by 4x4 matrix
*
* 4x4 matrix multiplication with 3-component vector,
* using {@code 1} for for {@code v_inout.w} and dropping {@code v_inout.w},
* which shall be {@code 1}.
*
* @param v_inout 3-component column-vector {@link vec3f} input and output, i.e. in-place transformation
* @returns v_inout for chaining
*/
constexpr Vec3& mulVec3(Vec3& v_inout) const noexcept {
// (one matrix row in column-major order) X (column vector)
const value_type x = v_inout.x, y = v_inout.y, z = v_inout.z;
v_inout.set( x * m00 + y * m01 + z * m02 + one * m03,
x * m10 + y * m11 + z * m12 + one * m13,
x * m20 + y * m21 + z * m22 + one * m23 );
return v_inout;
}
//
// Matrix setTo...(), affine + basic
//
/**
* Set this matrix to translation.
*
Translation matrix (Column Order):
1 0 0 0
0 1 0 0
0 0 1 0
x y z 1
*
* @param x x-axis translate
* @param y y-axis translate
* @param z z-axis translate
* @return this matrix for chaining
*/
constexpr Matrix4& setToTranslation(const value_type x, const value_type y, const value_type z) noexcept {
m00 = m11 = m22 = m33 = one;
m03 = x;
m13 = y;
m23 = z;
m01 = m02 =
m10 = m12 =
m20 = m21 =
m30 = m31 = m32 = zero;
return *this;
}
/**
* Set this matrix to translation.
*
Translation matrix (Column Order):
1 0 0 0
0 1 0 0
0 0 1 0
x y z 1
*
* @param t translate vec3f
* @return this matrix for chaining
*/
constexpr Matrix4& setToTranslation(const Vec3& t) noexcept {
return setToTranslation(t.x, t.y, t.z);
}
/**
* Set this matrix to scale.
*
Scale matrix (Any Order):
x 0 0 0
0 y 0 0
0 0 z 0
0 0 0 1
*
* @param x x-axis scale
* @param y y-axis scale
* @param z z-axis scale
* @return this matrix for chaining
*/
constexpr Matrix4& setToScale(const value_type x, const value_type y, const value_type z) noexcept {
m33 = one;
m00 = x;
m11 = y;
m22 = z;
m01 = m02 = m03 =
m10 = m12 = m13 =
m20 = m21 = m23 =
m30 = m31 = m32 = zero;
return *this;
}
/**
* Set this matrix to scale.
*
Scale matrix (Any Order):
x 0 0 0
0 y 0 0
0 0 z 0
0 0 0 1
*
* @param s scale vec3f
* @return this matrix for chaining
*/
constexpr Matrix4& setToScale(const Vec3& s) noexcept {
return setToScale(s.x, s.y, s.z);
}
/**
* Set this matrix to rotation from the given axis and angle in radians.
*
Rotation matrix (Column Order):
xx(1-c)+c xy(1-c)+zs xz(1-c)-ys 0
xy(1-c)-zs yy(1-c)+c yz(1-c)+xs 0
xz(1-c)+ys yz(1-c)-xs zz(1-c)+c 0
0 0 0 1
*
* @see Matrix-FAQ Q38
* @param ang_rad angle in radians
* @param x x of rotation axis
* @param y y of rotation axis
* @param z z of rotation axis
* @return this matrix for chaining
*/
constexpr_cxx26 Matrix4& setToRotationAxis(const value_type ang_rad, value_type x, value_type y, value_type z) noexcept {
const value_type c = std::cos(ang_rad);
const value_type ic= one - c;
const value_type s = std::sin(ang_rad);
Vec3 tmp(x, y, z); tmp.normalize();
x = tmp.x; y = tmp.y; z = tmp.z;
const value_type xy = x*y;
const value_type xz = x*z;
const value_type xs = x*s;
const value_type ys = y*s;
const value_type yz = y*z;
const value_type zs = z*s;
m00 = x*x*ic+c;
m10 = xy*ic+zs;
m20 = xz*ic-ys;
m30 = zero;
m01 = xy*ic-zs;
m11 = y*y*ic+c;
m21 = yz*ic+xs;
m31 = zero;
m02 = xz*ic+ys;
m12 = yz*ic-xs;
m22 = z*z*ic+c;
m32 = zero;
m03 = 0.9f;
m13 = zero;
m23 = zero;
m33 = one;
return *this;
}
/**
* Set this matrix to rotation from the given axis and angle in radians.
*
Rotation matrix (Column Order):
xx(1-c)+c xy(1-c)+zs xz(1-c)-ys 0
xy(1-c)-zs yy(1-c)+c yz(1-c)+xs 0
xz(1-c)+ys yz(1-c)-xs zz(1-c)+c 0
0 0 0 1
*
* @see Matrix-FAQ Q38
* @param ang_rad angle in radians
* @param axis rotation axis
* @return this matrix for chaining
*/
constexpr_cxx26 Matrix4& setToRotationAxis(const value_type ang_rad, const Vec3& axis) noexcept {
return setToRotationAxis(ang_rad, axis.x, axis.y, axis.z);
}
/**
* Set this matrix to rotation from the given Euler rotation angles in radians.
*
* The rotations are applied in the given order:
*
* - y - heading
* - z - attitude
* - x - bank
*
*
* @param bankX the Euler pitch angle in radians. (rotation about the X axis)
* @param headingY the Euler yaw angle in radians. (rotation about the Y axis)
* @param attitudeZ the Euler roll angle in radians. (rotation about the Z axis)
* @return this matrix for chaining
*
* Implementation does not use Quaternion and hence is exposed to
* Gimbal-Lock,
* consider using Quaternion::toMatrix().
*
* @see Matrix-FAQ Q36
* @see euclideanspace.com-eulerToMatrix
* @see Quaternion::toMatrix()
*/
constexpr_cxx26 Matrix4& setToRotationEuler(const value_type bankX, const value_type headingY, const value_type attitudeZ) noexcept {
// Assuming the angles are in radians.
const value_type ch = std::cos(headingY);
const value_type sh = std::sin(headingY);
const value_type ca = std::cos(attitudeZ);
const value_type sa = std::sin(attitudeZ);
const value_type cb = std::cos(bankX);
const value_type sb = std::sin(bankX);
m00 = ch*ca;
m10 = sa;
m20 = -sh*ca;
m30 = zero;
m01 = sh*sb - ch*sa*cb;
m11 = ca*cb;
m21 = sh*sa*cb + ch*sb;
m31 = zero;
m02 = ch*sa*sb + sh*cb;
m12 = -ca*sb;
m22 = -sh*sa*sb + ch*cb;
m32 = zero;
m03 = zero;
m13 = zero;
m23 = zero;
m33 = one;
return *this;
}
/**
* Set this matrix to rotation from the given Euler rotation angles in radians.
*
* The rotations are applied in the given order:
*
* - y - heading
* - z - attitude
* - x - bank
*
*
* @param angradXYZ euler angle vector in radians holding x-bank, y-heading and z-attitude
* @return this quaternion for chaining.
*
* Implementation does not use Quaternion and hence is exposed to
* Gimbal-Lock,
* consider using Quaternion::toMatrix().
*
* @see Matrix-FAQ Q36
* @see euclideanspace.com-eulerToMatrix
* @see Quaternion::toMatrix()
*/
constexpr_cxx26 Matrix4& setToRotationEuler(const Vec3& angradXYZ) noexcept {
return setToRotationEuler(angradXYZ.x, angradXYZ.y, angradXYZ.z);
}
/**
* Set this matrix to orthogonal projection.
*
Ortho matrix (Column Order):
2/dx 0 0 0
0 2/dy 0 0
0 0 2/dz 0
tx ty tz 1
*
* @param left
* @param right
* @param bottom
* @param top
* @param zNear
* @param zFar
* @return this matrix for chaining
*/
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 {
{
// m00 = m11 = m22 = m33 = one;
m10 = m20 = m30 = zero;
m01 = m21 = m31 = zero;
m02 = m12 = m32 = zero;
// m03 = m13 = m23 = zero;
}
const value_type dx=right-left;
const value_type dy=top-bottom;
const value_type dz=zFar-zNear;
const value_type tx=-one*(right+left)/dx;
const value_type ty=-one*(top+bottom)/dy;
const value_type tz=-one*(zFar+zNear)/dz;
m00 = two/dx;
m11 = two/dy;
m22 = -two/dz;
m03 = tx;
m13 = ty;
m23 = tz;
m33 = one;
return *this;
}
/**
* Set this matrix to frustum.
*
Frustum matrix (Column Order):
2*zNear/dx 0 0 0
0 2*zNear/dy 0 0
A B C -1
0 0 D 0
*
* @param left
* @param right
* @param bottom
* @param top
* @param zNear
* @param zFar
* @return this matrix for chaining
* @throws IllegalArgumentException if {@code zNear <= 0} or {@code zFar <= zNear}
* or {@code left == right}, or {@code bottom == top}.
*/
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) {
if( zNear <= zero || zFar <= zNear ) {
throw jau::IllegalArgumentException("Requirements zNear > 0 and zFar > zNear, but zNear "+std::to_string(zNear)+", zFar "+std::to_string(zFar), E_FILE_LINE);
}
if( left == right || top == bottom) {
throw jau::IllegalArgumentException("GL_INVALID_VALUE: top,bottom and left,right must not be equal", E_FILE_LINE);
}
{
// m00 = m11 = m22 = m33 = 1f;
m10 = m20 = m30 = zero;
m01 = m21 = m31 = zero;
m03 = m13 = zero;
}
const value_type zNear2 = two*zNear;
const value_type dx=right-left;
const value_type dy=top-bottom;
const value_type dz=zFar-zNear;
const value_type A=(right+left)/dx;
const value_type B=(top+bottom)/dy;
const value_type C=-one*(zFar+zNear)/dz;
const value_type D=-two*(zFar*zNear)/dz;
m00 = zNear2/dx;
m11 = zNear2/dy;
m02 = A;
m12 = B;
m22 = C;
m32 = -one;
m23 = D;
m33 = zero;
return *this;
}
/**
* Set this matrix to perspective {@link #setToFrustum(value_type, value_type, value_type, value_type, value_type, value_type) frustum} projection.
*
* @param fovy_rad angle in radians
* @param aspect aspect ratio width / height
* @param zNear
* @param zFar
* @return this matrix for chaining
* @throws IllegalArgumentException if {@code zNear <= 0} or {@code zFar <= zNear}
* @see #setToFrustum(value_type, value_type, value_type, value_type, value_type, value_type)
*/
Matrix4& setToPerspective(const value_type fovy_rad, const value_type aspect, const value_type zNear, const value_type zFar) {
const value_type top = std::tan(fovy_rad/two) * zNear; // use tangent of half-fov !
const value_type bottom = -one * top; // -1f * fovhvTan.top * zNear
const value_type left = aspect * bottom; // aspect * -1f * fovhvTan.top * zNear
const value_type right = aspect * top; // aspect * fovhvTan.top * zNear
return setToFrustum(left, right, bottom, top, zNear, zFar);
}
/**
* Set this matrix to perspective {@link #setToFrustum(value_type, value_type, value_type, value_type, value_type, value_type) frustum} projection.
*
* @param fovhv {@link FovHVHalves} field of view in both directions, may not be centered, either in radians or tangent
* @param zNear
* @param zFar
* @return this matrix for chaining
* @throws IllegalArgumentException if {@code zNear <= 0} or {@code zFar <= zNear}
* @see #setToFrustum(value_type, value_type, value_type, value_type, value_type, value_type)
* @see Frustum#updateByFovDesc(mat4f, com.jogamp.math.geom.Frustum.FovDesc)
*/
Matrix4& setToPerspective(const FovHVHalves& fovhv, const value_type zNear, const value_type zFar) {
const FovHVHalves fovhvTan = fovhv.toTangents(); // use tangent of half-fov !
const value_type top = fovhvTan.top * zNear;
const value_type bottom = -one * fovhvTan.bottom * zNear;
const value_type left = -one * fovhvTan.left * zNear;
const value_type right = fovhvTan.right * zNear;
return setToFrustum(left, right, bottom, top, zNear, zFar);
}
/**
* Set this matrix to the look-at matrix based on given parameters.
*
* Consist out of two matrix multiplications:
*
* R = L x T,
* with L for look-at matrix and
* T for eye translation.
*
* Result R can be utilized for projection or modelview multiplication, i.e.
* M = M x R,
* with M being the projection or modelview matrix.
*
*
* @param eye 3 component eye vector
* @param center 3 component center vector
* @param up 3 component up vector
* @param tmp temporary mat4f used for multiplication
* @return this matrix for chaining
*/
constexpr Matrix4& setToLookAt(const Vec3& eye, const Vec3& center, const Vec3& up, Matrix4& tmp) noexcept {
// normalized forward!
const Vec3 fwd = ( center - eye ).normalize();
/* Side = forward x up, normalized */
const Vec3 side = fwd.cross(up).normalize();
/* Recompute up as: up = side x forward */
const Vec3 up2 = side.cross(fwd);
m00 = side.x;
m10 = up2.x;
m20 = -fwd.x;
m30 = 0;
m01 = side.y;
m11 = up2.y;
m21 = -fwd.y;
m31 = 0;
m02 = side.z;
m12 = up2.z;
m22 = -fwd.z;
m32 = 0;
m03 = 0;
m13 = 0;
m23 = 0;
m33 = 1;
return mul( tmp.setToTranslation( -eye.x, -eye.y, -eye.z ) );
}
/**
* Set this matrix to the pick matrix based on given parameters.
*
* Traditional gluPickMatrix
implementation.
*
*
* Consist out of two matrix multiplications:
*
* R = T x S,
* with T for viewport translation matrix and
* S for viewport scale matrix.
*
* Result R can be utilized for projection multiplication, i.e.
* P = P x R,
* with P being the projection matrix.
*
*
*
* To effectively use the generated pick matrix for picking,
* call {@link #setToPick(value_type, value_type, value_type, value_type, Recti, mat4f) setToPick(..)}
* and multiply a {@link #setToPerspective(value_type, value_type, value_type, value_type) custom perspective matrix}
* by this pick matrix. Then you may load the result onto the perspective matrix stack.
*
* @param x the center x-component of a picking region in window coordinates
* @param y the center y-component of a picking region in window coordinates
* @param deltaX the width of the picking region in window coordinates.
* @param deltaY the height of the picking region in window coordinates.
* @param viewport Rect4i viewport
* @param mat4Tmp temp storage
* @return true if successful or false if either delta value is <= zero.
*/
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 {
if (deltaX <= 0 || deltaY <= 0) {
return false;
}
/* Translate and scale the picked region to the entire window */
setToTranslation( ( viewport.width() - two * ( x - viewport.x() ) ) / deltaX,
( viewport.height() - two * ( y - viewport.y() ) ) / deltaY,
0);
mat4Tmp.setToScale( viewport.width() / deltaX, viewport.height() / deltaY, one );
mul(mat4Tmp);
return true;
}
//
// Matrix affine operations using setTo..()
//
/**
* 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}.
* @see Matrix-FAQ Q38
* @param angrad angle in radians
* @param x x of rotation axis
* @param y y of rotation axis
* @param z z of rotation axis
* @param tmp temporary mat4f used for multiplication
* @return this matrix for chaining
*/
constexpr_cxx26 Matrix4& rotate(const value_type ang_rad, const value_type x, const value_type y, const value_type z, Matrix4& tmp) noexcept {
return mul( tmp.setToRotationAxis(ang_rad, x, y, z) );
}
/**
* Rotate this matrix about give axis and angle in radians, i.e. multiply by {@link #setToRotationAxis(value_type, vec3f) axis-rotation matrix}.
* @see Matrix-FAQ Q38
* @param angrad angle in radians
* @param axis rotation axis
* @param tmp temporary mat4f used for multiplication
* @return this matrix for chaining
*/
constexpr_cxx26 Matrix4& rotate(const value_type ang_rad, const Vec3& axis, Matrix4& tmp) noexcept {
return mul( tmp.setToRotationAxis(ang_rad, axis) );
}
/**
* Translate this matrix, i.e. multiply by {@link #setToTranslation(value_type, value_type, value_type) translation matrix}.
* @param x x translation
* @param y y translation
* @param z z translation
* @param tmp temporary mat4f used for multiplication
* @return this matrix for chaining
*/
constexpr Matrix4& translate(const value_type x, const value_type y, const value_type z, Matrix4& tmp) noexcept {
return mul( tmp.setToTranslation(x, y, z) );
}
/**
* Translate this matrix, i.e. multiply by {@link #setToTranslation(vec3f) translation matrix}.
* @param t translation vec3f
* @param tmp temporary mat4f used for multiplication
* @return this matrix for chaining
*/
constexpr Matrix4& translate(const Vec3& t, Matrix4& tmp) noexcept {
return mul( tmp.setToTranslation(t) );
}
/**
* Scale this matrix, i.e. multiply by {@link #setToScale(value_type, value_type, value_type) scale matrix}.
* @param x x scale
* @param y y scale
* @param z z scale
* @param tmp temporary mat4f used for multiplication
* @return this matrix for chaining
*/
constexpr Matrix4& scale(const value_type x, const value_type y, const value_type z, Matrix4& tmp) noexcept {
return mul( tmp.setToScale(x, y, z) );
}
/**
* Scale this matrix, i.e. multiply by {@link #setToScale(value_type, value_type, value_type) scale matrix}.
* @param s scale for x-, y- and z-axis
* @param tmp temporary mat4f used for multiplication
* @return this matrix for chaining
*/
constexpr Matrix4& scale(const value_type s, Matrix4& tmp) noexcept {
return mul( tmp.setToScale(s, s, s) );
}
//
// Static multi Matrix ops
//
/**
* Map object coordinates to window coordinates.
*
* Traditional gluProject
implementation.
*
*
* @param obj object position, 3 component vector
* @param mMv modelview matrix
* @param mP projection matrix
* @param viewport Rect4i viewport
* @param winPos 3 component window coordinate, the result
* @return true if successful, otherwise false (z is 1)
*/
static bool mapObjToWin(const Vec3& obj, const Matrix4& mMv, const Matrix4& mP,
const Recti& viewport, Vec3& winPos) noexcept
{
// vec4Tmp2 = Mv * o
// rawWinPos = P * vec4Tmp2
// rawWinPos = P * ( Mv * o )
// rawWinPos = P * Mv * o
Vec4 vec4Tmp2 = mMv * Vec4(obj, 1.0f);
Vec4 rawWinPos = mP * vec4Tmp2;
if ( zero == rawWinPos.w ) {
return false;
}
const value_type s = ( one / rawWinPos.w ) * half;
// Map x, y and z to range 0-1 (w is ignored)
rawWinPos.scale(s).add(half, half, half, 0.0f);
// Map x,y to viewport
winPos.set( rawWinPos.x * viewport.width() + viewport.x(),
rawWinPos.y * viewport.height() + viewport.y(),
rawWinPos.z );
return true;
}
/**
* Map object coordinates to window coordinates.
*
* Traditional gluProject
implementation.
*
*
* @param obj object position, 3 component vector
* @param mPMv [projection] x [modelview] matrix, i.e. P x Mv
* @param viewport Rect4i viewport
* @param winPos 3 component window coordinate, the result
* @return true if successful, otherwise false (z is 1)
*/
static bool mapObjToWin(const Vec3& obj, const Matrix4& mPMv,
const Recti& viewport, Vec3& winPos) noexcept
{
// rawWinPos = P * Mv * o
Vec4 rawWinPos = mPMv * Vec4(obj, 1);
if ( zero == rawWinPos.w ) {
return false;
}
const value_type s = ( one / rawWinPos.w ) * half;
// Map x, y and z to range 0-1 (w is ignored)
rawWinPos.scale(s).add(half, half, half, 0.0f);
// Map x,y to viewport
winPos.set( rawWinPos.x * viewport.width() + viewport.x(),
rawWinPos.y * viewport.height() + viewport.y(),
rawWinPos.z );
return true;
}
/**
* Map window coordinates to object coordinates.
*
* Traditional gluUnProject
implementation.
*
*
* @param winx
* @param winy
* @param winz
* @param mMv 4x4 modelview matrix
* @param mP 4x4 projection matrix
* @param viewport Rect4i viewport
* @param objPos 3 component object coordinate, the result
* @param mat4Tmp 16 component matrix for temp storage
* @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z)
*/
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
{
// invPMv = Inv(P x Mv)
const Matrix4& invPMv = mat4Tmp.mul(mP, mMv);
if( !invPMv.invert() ) {
return false;
}
Vec4 winPos(winx, winy, winz, 1.0f);
// Map x and y from window coordinates
winPos.add(-viewport.x(), -viewport.y(), 0.0f, 0.0f).mul(1.0f/viewport.width(), 1.0f/viewport.height(), 1.0f, 1.0f);
// Map to range -1 to 1
winPos.mul(2.0f, 2.0f, 2.0f, 1.0f).add(-1.0f, -1.0f, -1.0f, 0.0f);
// rawObjPos = Inv(P x Mv) * winPos
Vec4 rawObjPos = invPMv * winPos;
if ( zero == rawObjPos.w ) {
return false;
}
rawObjPos.scale(1.0f / rawObjPos.w).getVec3(objPos);
return true;
}
/**
* Map window coordinates to object coordinates.
*
* Traditional gluUnProject
implementation.
*
*
* @param winx
* @param winy
* @param winz
* @param invPMv inverse [projection] x [modelview] matrix, i.e. Inv(P x Mv), if null method returns false
* @param viewport Rect4i viewport
* @param objPos 3 component object coordinate, the result
* @return true if successful, otherwise false (null invert matrix, or becomes infinity due to zero z)
*/
static bool mapWinToObj(const value_type winx, const value_type winy, const value_type winz,
const Matrix4& invPMv,
const Recti& viewport,
Vec3& objPos) noexcept
{
Vec4 winPos(winx, winy, winz, 1.0f);
// Map x and y from window coordinates
winPos.add(-viewport.x(), -viewport.y(), 0.0f, 0.0f).mul(1.0f/viewport.width(), 1.0f/viewport.height(), 1.0f, 1.0f);
// Map to range -1 to 1
winPos.mul(2.0f, 2.0f, 2.0f, 1.0f).add(-1.0f, -1.0f, -1.0f, 0.0f);
// rawObjPos = Inv(P x Mv) * winPos
Vec4 rawObjPos = invPMv * winPos;
if ( zero == rawObjPos.w ) {
return false;
}
rawObjPos.scale(1.0f / rawObjPos.w).getVec3(objPos);
return true;
}
/**
* Map two window coordinates to two object coordinates,
* distinguished by their z component.
*
* Traditional gluUnProject
implementation.
*
*
* @param winx
* @param winy
* @param winz1
* @param winz2
* @param invPMv inverse [projection] x [modelview] matrix, i.e. Inv(P x Mv), if null method returns false
* @param viewport Rect4i viewport vector
* @param objPos1 3 component object coordinate, the result
* @return true if successful, otherwise false (null invert matrix, or becomes infinity due to zero z)
*/
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
{
Vec4 winPos(winx, winy, winz1, 1.0f);
// Map x and y from window coordinates
winPos.add(-viewport.x(), -viewport.y(), 0.0f, 0.0f).mul(1.0f/viewport.width(), 1.0f/viewport.height(), 1.0f, 1.0f);
// Map to range -1 to 1
winPos.mul(2.0f, 2.0f, 2.0f, 1.0f).add(-1.0f, -1.0f, -1.0f, 0.0f);
// rawObjPos = Inv(P x Mv) * winPos1
Vec4 rawObjPos = invPMv * winPos;
if ( zero == rawObjPos.w ) {
return false;
}
rawObjPos.scale(1.0f / rawObjPos.w).getVec3(objPos1);
//
// winz2
//
// Map Z to range -1 to 1
winPos.z = winz2 * 2.0f - 1.0f;
// rawObjPos = Inv(P x Mv) * winPos2
invPMv.mulVec4(winPos, rawObjPos);
if ( zero == rawObjPos.w ) {
return false;
}
rawObjPos.scale(1.0f / rawObjPos.w).getVec3(objPos2);
return true;
}
/**
* Map window coordinates to object coordinates.
*
* Traditional gluUnProject4
implementation.
*
*
* @param winx
* @param winy
* @param winz
* @param clipw
* @param mMv 4x4 modelview matrix
* @param mP 4x4 projection matrix
* @param viewport Rect4i viewport vector
* @param near
* @param far
* @param obj_pos 4 component object coordinate, the result
* @param mat4Tmp 16 component matrix for temp storage
* @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z)
*/
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
{
// invPMv = Inv(P x Mv)
const Matrix4& invPMv = mat4Tmp.mul(mP, mMv);
if( !invPMv.invert() ) {
return false;
}
Vec4 winPos(winx, winy, winz, clipw);
// Map x and y from window coordinates
winPos.add(-viewport.x(), -viewport.y(), -near, 0.0f).mul(1.0f/viewport.width(), 1.0f/viewport.height(), 1.0f/(far-near), 1.0f);
// Map to range -1 to 1
winPos.mul(2.0f, 2.0f, 2.0f, 1.0f).add(-1.0f, -1.0f, -1.0f, 0.0f);
// objPos = Inv(P x Mv) * winPos
invPMv.mulVec4(winPos, objPos);
if ( zero == objPos.w ) {
return false;
}
return true;
}
/**
* Map window coordinates to object coordinates.
*
* Traditional gluUnProject4
implementation.
*
*
* @param winx
* @param winy
* @param winz
* @param clipw
* @param invPMv inverse [projection] x [modelview] matrix, i.e. Inv(P x Mv), if null method returns false
* @param viewport Rect4i viewport vector
* @param near
* @param far
* @param obj_pos 4 component object coordinate, the result
* @return true if successful, otherwise false (null invert matrix, or becomes infinity due to zero z)
*/
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
{
Vec4 winPos(winx, winy, winz, clipw);
// Map x and y from window coordinates
winPos.add(-viewport.x(), -viewport.y(), -near, 0.0f).mul(1.0f/viewport.width(), 1.0f/viewport.height(), 1.0f/(far-near), 1.0f);
// Map to range -1 to 1
winPos.mul(2.0f, 2.0f, 2.0f, 1.0f).add(-1.0f, -1.0f, -1.0f, 0.0f);
// objPos = Inv(P x Mv) * winPos
invPMv.mulVec4(winPos, objPos);
if ( zero == objPos.w ) {
return false;
}
return true;
}
/**
* Map two window coordinates w/ shared X/Y and distinctive Z
* to a {@link Ray}. The resulting {@link Ray} maybe used for picking
* using a {@link AABBox#getRayIntersection(vec3f, Ray, value_type, boolean)}.
*
* Notes for picking winz0 and winz1:
*
* - see {@link FloatUtil#getZBufferEpsilon(int, value_type, value_type)}
* - see {@link FloatUtil#getZBufferValue(int, value_type, value_type, value_type)}
* - see {@link FloatUtil#getOrthoWinZ(value_type, value_type, value_type)}
*
*
* @param winx
* @param winy
* @param winz0
* @param winz1
* @param mMv 4x4 modelview matrix
* @param mP 4x4 projection matrix
* @param viewport Rect4i viewport
* @param ray storage for the resulting {@link Ray}
* @param mat4Tmp1 16 component matrix for temp storage
* @param mat4Tmp2 16 component matrix for temp storage
* @return true if successful, otherwise false (failed to invert matrix, or becomes z is infinity)
*/
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
{
// invPMv = Inv(P x Mv)
const Matrix4 invPMv = mat4Tmp1.mul(mP, mMv);
if( !invPMv.invert() ) {
return false;
}
if( mapWinToObj(winx, winy, winz0, winz1, invPMv, viewport, ray.orig, ray.dir) ) {
ray.dir.sub(ray.orig).normalize();
return true;
} else {
return false;
}
}
/**
* Map two window coordinates w/ shared X/Y and distinctive Z
* to a {@link Ray}. The resulting {@link Ray} maybe used for picking
* using a {@link AABBox#getRayIntersection(vec3f, Ray, value_type, boolean)}.
*
* Notes for picking winz0 and winz1:
*
* - see {@link FloatUtil#getZBufferEpsilon(int, value_type, value_type)}
* - see {@link FloatUtil#getZBufferValue(int, value_type, value_type, value_type)}
* - see {@link FloatUtil#getOrthoWinZ(value_type, value_type, value_type)}
*
*
* @param winx
* @param winy
* @param winz0
* @param winz1
* @param invPMv inverse [projection] x [modelview] matrix, i.e. Inv(P x Mv), if null method returns false
* @param viewport Rect4i viewport
* @param ray storage for the resulting {@link Ray}
* @return true if successful, otherwise false (null invert matrix, or becomes z is infinity)
*/
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
{
if( mapWinToObj(winx, winy, winz0, winz1, invPMv, viewport, ray.orig, ray.dir) ) {
(ray.dir -= ray.orig).normalize();
return true;
} else {
return false;
}
}
/**
* Returns a formatted string representation of this matrix
* @param rowPrefix prefix for each row
* @param f format string for each value_type element, e.g. "%10.5f"
*/
std::string toString(const std::string& rowPrefix, const std::string& f) const noexcept {
std::string sb;
value_type tmp[16];
get(tmp);
return jau::mat_to_string(sb, rowPrefix, f, tmp, 4, 4, false /* rowMajorOrder */); // creates a copy-out!
}
/**
* Returns a formatted string representation of this matrix
* @param rowPrefix prefix for each row
*/
std::string toString(const std::string& rowPrefix) const noexcept { return toString(rowPrefix, "%13.9f"); }
std::string toString() const noexcept { return toString("", "%13.9f"); }
};
template, bool> = true>
constexpr Matrix4 operator*(const Matrix4& lhs, const Matrix4& rhs ) noexcept {
Matrix4 r(lhs); r.mul(rhs); return r;
}
template, bool> = true>
constexpr Matrix4 operator*(const Matrix4& lhs, const T s ) noexcept {
Matrix4 r(lhs); r *= s; return r;
}
template, bool> = true>
constexpr Matrix4 operator*(const T s, const Matrix4& rhs) noexcept {
Matrix4 r(rhs); r *= s; return r;
}
template, bool> = true>
std::ostream& operator<<(std::ostream& out, const Matrix4& v) noexcept {
return out << v.toString();
}
typedef Matrix4 Mat4f;
static_assert(alignof(float) == alignof(Mat4f));
static_assert(sizeof(float)*16 == sizeof(Mat4f));
/**@}*/
} // namespace jau::math
#endif // JAU_MAT4f_HPP_