/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef API_BASE_MATH_MATRIX_UTIL_H
#define API_BASE_MATH_MATRIX_UTIL_H
#include
#include
#include
#include
#include
#include
#include
BASE_BEGIN_NAMESPACE()
namespace Math {
/** \addtogroup group_math_matrixutil
* @{
*/
/** Returns transpose of this matrix */
static inline constexpr Mat3X3 Transpose(const Mat3X3& m)
{
Mat3X3 result;
result.x.x = m.x.x;
result.y.x = m.x.y;
result.z.x = m.x.z;
result.x.y = m.y.x;
result.y.y = m.y.y;
result.z.y = m.y.z;
result.x.z = m.z.x;
result.y.z = m.z.y;
result.z.z = m.z.z;
return result;
}
/** Returns transpose of this matrix */
static inline constexpr Mat4X4 Transpose(const Mat4X4& m)
{
Mat4X4 result;
result.x.x = m.x.x;
result.y.x = m.x.y;
result.z.x = m.x.z;
result.w.x = m.x.w;
result.x.y = m.y.x;
result.y.y = m.y.y;
result.z.y = m.y.z;
result.w.y = m.y.w;
result.x.z = m.z.x;
result.y.z = m.z.y;
result.z.z = m.z.z;
result.w.z = m.z.w;
result.x.w = m.w.x;
result.y.w = m.w.y;
result.z.w = m.w.z;
result.w.w = m.w.w;
return result;
}
/** Set scale of this matrix */
static inline constexpr Mat3X3 PostScale(const Mat3X3& mat, const Vec2& vec)
{
Mat3X3 result;
result.x = { mat.x.x * vec.x, mat.x.y * vec.y, mat.x.z };
result.y = { mat.y.x * vec.x, mat.y.y * vec.y, mat.y.z };
result.z = { mat.z.x * vec.x, mat.z.y * vec.y, mat.z.z };
return result;
}
/** Set scale of this matrix */
static inline constexpr Mat4X4 PostScale(const Mat4X4& mat, const Vec3& vec)
{
Mat4X4 result;
result.x = { mat.x.x * vec.x, mat.x.y * vec.y, mat.x.z * vec.z, mat.x.w };
result.y = { mat.y.x * vec.x, mat.y.y * vec.y, mat.y.z * vec.z, mat.y.w };
result.z = { mat.z.x * vec.x, mat.z.y * vec.y, mat.z.z * vec.z, mat.z.w };
result.w = { mat.w.x * vec.x, mat.w.y * vec.y, mat.w.z * vec.z, mat.w.w };
return result;
}
/** Set scale of this matrix */
static inline constexpr Mat3X3 Scale(const Mat3X3& mat, const Vec2& vec)
{
Mat3X3 result;
result.x = mat.x * vec.x;
result.y = mat.y * vec.y;
result.z = mat.z;
return result;
}
/** Set scale of this matrix */
static inline constexpr Mat4X4 Scale(const Mat4X4& mat, const Vec3& vec)
{
Mat4X4 result;
result.x = mat.x * vec.x;
result.y = mat.y * vec.y;
result.z = mat.z * vec.z;
result.w = mat.w;
return result;
}
/** Get column of the matrix */
static inline constexpr Vec3 GetColumn(const Mat3X3& mat, int index)
{
switch (index) {
// the first column of the matrix: Vec3(m00, m10, m20)
case 0:
return mat.x;
// the second column of the matrix: Vec3(m01, m11, m21)
case 1:
return mat.y;
// the third column of the matrix: Vec3(m02, m12, m22)
case 2:
return mat.z;
default:
BASE_ASSERT(false);
return Vec3(0.0f, 0.0f, 0.0f);
}
}
/** Get column of the matrix */
static inline constexpr Vec4 GetColumn(const Mat4X4& mat, int index)
{
switch (index) {
// the first column of the matrix: Vec4(m00, m10, m20, m30)
case 0:
return mat.x;
// the second column of the matrix: Vec4(m01, m11, m21, m31)
case 1:
return mat.y;
// the third column of the matrix: Vec4(m02, m12, m22, m32)
case 2:
return mat.z;
// the fourth column of the matrix: Vec4(m03, m13, m23, m33)
case 3:
return mat.w;
default:
BASE_ASSERT(false);
return Vec4(0.0f, 0.0f, 0.0f, 0.0f);
}
}
/** Returns row of the matrix */
static inline constexpr Vec3 GetRow(const Mat3X3& mat, int index)
{
switch (index) {
// the first row of the matrix: Vec4(m00, m01, m02, m03)
case 0:
return Vec3(mat.x.x, mat.y.x, mat.z.x);
// the second row of the matrix: Vec4(m10, m11, m12, m13)
case 1:
return Vec3(mat.x.y, mat.y.y, mat.z.y);
// the third row of the matrix: Vec4(m20, m21, m22, m23)
case 2:
return Vec3(mat.x.z, mat.y.z, mat.z.z);
default:
BASE_ASSERT(false);
return Vec3(0.0f, 0.0f, 0.0f);
}
}
/** Returns row of the matrix */
static inline constexpr Vec4 GetRow(const Mat4X4& mat, int index)
{
switch (index) {
// the first row of the matrix: Vec4(m00, m01, m02, m03)
case 0:
return Vec4(mat.x.x, mat.y.x, mat.z.x, mat.w.x);
// the second row of the matrix: Vec4(m10, m11, m12, m13)
case 1:
return Vec4(mat.x.y, mat.y.y, mat.z.y, mat.w.y);
// the third row of the matrix: Vec4(m20, m21, m22, m23)
case 2:
return Vec4(mat.x.z, mat.y.z, mat.z.z, mat.w.z);
// the fourth row of the matrix: Vec4(m30, m31, m32, m33)
case 3:
return Vec4(mat.x.w, mat.y.w, mat.z.w, mat.w.w);
default:
BASE_ASSERT(false);
return Vec4(0.0f, 0.0f, 0.0f, 0.0f);
}
}
/** Converts quaternion to matrix 3x3 */
static inline constexpr Mat3X3 Mat3Cast(const Quat& quaternion)
{
Mat3X3 result;
const float qXX(quaternion.x * quaternion.x);
const float qYY(quaternion.y * quaternion.y);
const float qZZ(quaternion.z * quaternion.z);
const float qXZ(quaternion.x * quaternion.z);
const float qXY(quaternion.x * quaternion.y);
const float qYZ(quaternion.y * quaternion.z);
const float qWX(quaternion.w * quaternion.x);
const float qWY(quaternion.w * quaternion.y);
const float qWZ(quaternion.w * quaternion.z);
result.x.x = 1.0f - 2.0f * (qYY + qZZ);
result.x.y = 2.0f * (qXY + qWZ);
result.x.z = 2.0f * (qXZ - qWY);
result.y.x = 2.0f * (qXY - qWZ);
result.y.y = 1.0f - 2.0f * (qXX + qZZ);
result.y.z = 2.0f * (qYZ + qWX);
result.z.x = 2.0f * (qXZ + qWY);
result.z.y = 2.0f * (qYZ - qWX);
result.z.z = 1.0f - 2.0f * (qXX + qYY);
return result;
}
/** Convert 3x3 matrix to 4x4 */
static inline constexpr Mat4X4 DimensionalShift(const Mat3X3& mat)
{
Mat4X4 result;
result.x = { mat.x.x, mat.x.y, 0, mat.x.z };
result.y = { mat.y.x, mat.y.y, 0, mat.y.z };
result.z = { 0, 0, 1, 0 };
result.w = { mat.z.x, mat.z.y, 0, mat.z.z };
return result;
}
/** Converts quaternion to matrix 4X4 */
static inline constexpr Mat4X4 Mat4Cast(const Quat& quaternion)
{
return Mat4X4(Mat3Cast(quaternion));
}
/** Translate by vector2 */
static inline constexpr Mat3X3 Translate(const Mat3X3& mat, const Vec2& vec)
{
Mat3X3 result(mat);
result.z = mat.x * vec.x + mat.y * vec.y + mat.z;
return result;
}
/** Translate by vector2 */
static inline constexpr Mat4X4 Translate(const Mat4X4& mat, const Vec2& vec)
{
Mat4X4 result(mat);
result.w = mat.x * vec.x + mat.y * vec.y + mat.w;
return result;
}
/** Translate by vector3 */
static inline constexpr Mat4X4 Translate(const Mat4X4& mat, const Vec3& vec)
{
Mat4X4 result(mat);
result.w = mat.x * vec.x + mat.y * vec.y + mat.z * vec.z + mat.w;
return result;
}
/** Skew angle in X and Y in radian */
static inline Mat4X4 SkewXY(const Mat4X4& mat, const Vec2& vec)
{
Mat4X4 result(mat);
result.x += mat.y * tanf(vec.y);
result.y += mat.x * tanf(vec.x);
return result;
}
/** Rotate on Z axis by angle in radian */
static inline Mat4X4 RotateZCWRadians(const Mat4X4& mat, float rot)
{
Mat4X4 result(mat);
float co = cosf(rot);
float si = sinf(rot);
result.x = mat.x * co + mat.y * si;
result.y = mat.y * co - mat.x * si;
return result;
}
/** Creates translation, rotation and scaling matrix from translation vector, rotation quaternion and scale vector */
static inline constexpr Mat4X4 Trs(
const Vec3& translationVector, const Quat& rotationQuaternion, const Vec3& scaleVector)
{
return Scale(Translate(Mat4X4(1.f), translationVector) * Mat4Cast(rotationQuaternion), scaleVector);
}
/** Transforms direction by this matrix */
static inline constexpr Vec3 MultiplyVector(const Mat4X4& mat, const Vec3& vec)
{
Vec3 res;
res.x = mat.x.x * vec.x + mat.y.x * vec.y + mat.z.x * vec.z;
res.y = mat.x.y * vec.x + mat.y.y * vec.y + mat.z.y * vec.z;
res.z = mat.x.z * vec.x + mat.y.z * vec.y + mat.z.z * vec.z;
return res;
}
/** Transforms position by this matrix without perspective divide */
static inline constexpr Vec2 MultiplyPoint2X4(const Mat4X4& mat4X4, const Vec2& point)
{
Vec2 result;
result.x = mat4X4.x.x * point.x + mat4X4.y.x * point.y + mat4X4.w.x;
result.y = mat4X4.x.y * point.x + mat4X4.y.y * point.y + mat4X4.w.y;
return result;
}
/** Transforms direction by this matrix without perspective divide */
static inline constexpr Vec2 MultiplyVector2X4(const Mat4X4& mat4X4, const Vec2& point)
{
Vec2 result;
result.x = mat4X4.x.x * point.x + mat4X4.y.x * point.y;
result.y = mat4X4.x.y * point.x + mat4X4.y.y * point.y;
return result;
}
/** Transforms position by this matrix without perspective divide */
static inline constexpr Vec2 MultiplyPoint2X3(const Mat3X3& mat3X3, const Vec2& point)
{
Vec2 result;
result.x = mat3X3.x.x * point.x + mat3X3.y.x * point.y + mat3X3.z.x;
result.y = mat3X3.x.y * point.x + mat3X3.y.y * point.y + mat3X3.z.y;
return result;
}
/** Transforms position by this matrix without perspective divide */
static inline constexpr Vec3 MultiplyPoint3X4(const Mat4X4& mat4X4, const Vec3& point)
{
Vec3 result;
result.x = mat4X4.x.x * point.x + mat4X4.y.x * point.y + mat4X4.z.x * point.z + mat4X4.w.x;
result.y = mat4X4.x.y * point.x + mat4X4.y.y * point.y + mat4X4.z.y * point.z + mat4X4.w.y;
result.z = mat4X4.x.z * point.x + mat4X4.y.z * point.y + mat4X4.z.z * point.z + mat4X4.w.z;
return result;
}
/** Inverse matrix with determinant output */
static inline constexpr Mat3X3 Inverse(Mat3X3 const& m, float& determinantOut)
{
determinantOut = m.x.x * (m.y.y * m.z.z - m.z.y * m.y.z) - m.x.y * (m.y.x * m.z.z - m.y.z * m.z.x) +
m.x.z * (m.y.x * m.z.y - m.y.y * m.z.x);
const float invdet = 1 / determinantOut;
Mat3X3 inverse;
inverse.x.x = (m.y.y * m.z.z - m.z.y * m.y.z) * invdet;
inverse.x.y = (m.x.z * m.z.y - m.x.y * m.z.z) * invdet;
inverse.x.z = (m.x.y * m.y.z - m.x.z * m.y.y) * invdet;
inverse.y.x = (m.y.z * m.z.x - m.y.x * m.z.z) * invdet;
inverse.y.y = (m.x.x * m.z.z - m.x.z * m.z.x) * invdet;
inverse.y.z = (m.y.x * m.x.z - m.x.x * m.y.z) * invdet;
inverse.z.x = (m.y.x * m.z.y - m.z.x * m.y.y) * invdet;
inverse.z.y = (m.z.x * m.x.y - m.x.x * m.z.y) * invdet;
inverse.z.z = (m.x.x * m.y.y - m.y.x * m.x.y) * invdet;
return inverse;
}
/** Inverse matrix */
static inline constexpr Mat3X3 Inverse(const Mat3X3& mat3X3)
{
float determinant = 0.0f;
return Inverse(mat3X3, determinant);
}
/** Inverse matrix with determinant output */
static inline constexpr Mat4X4 Inverse(Mat4X4 const& m, float& determinantOut)
{
const float coef00 = m.z.z * m.w.w - m.w.z * m.z.w;
const float coef02 = m.y.z * m.w.w - m.w.z * m.y.w;
const float coef03 = m.y.z * m.z.w - m.z.z * m.y.w;
const float coef04 = m.z.y * m.w.w - m.w.y * m.z.w;
const float coef06 = m.y.y * m.w.w - m.w.y * m.y.w;
const float coef07 = m.y.y * m.z.w - m.z.y * m.y.w;
const float coef08 = m.z.y * m.w.z - m.w.y * m.z.z;
const float coef10 = m.y.y * m.w.z - m.w.y * m.y.z;
const float coef11 = m.y.y * m.z.z - m.z.y * m.y.z;
const float coef12 = m.z.x * m.w.w - m.w.x * m.z.w;
const float coef14 = m.y.x * m.w.w - m.w.x * m.y.w;
const float coef15 = m.y.x * m.z.w - m.z.x * m.y.w;
const float coef16 = m.z.x * m.w.z - m.w.x * m.z.z;
const float coef18 = m.y.x * m.w.z - m.w.x * m.y.z;
const float coef19 = m.y.x * m.z.z - m.z.x * m.y.z;
const float coef20 = m.z.x * m.w.y - m.w.x * m.z.y;
const float coef22 = m.y.x * m.w.y - m.w.x * m.y.y;
const float coef23 = m.y.x * m.z.y - m.z.x * m.y.y;
const Vec4 fac0(coef00, coef00, coef02, coef03);
const Vec4 fac1(coef04, coef04, coef06, coef07);
const Vec4 fac2(coef08, coef08, coef10, coef11);
const Vec4 fac3(coef12, coef12, coef14, coef15);
const Vec4 fac4(coef16, coef16, coef18, coef19);
const Vec4 fac5(coef20, coef20, coef22, coef23);
const Vec4 vec0(m.y.x, m.x.x, m.x.x, m.x.x);
const Vec4 vec1(m.y.y, m.x.y, m.x.y, m.x.y);
const Vec4 vec2(m.y.z, m.x.z, m.x.z, m.x.z);
const Vec4 vec3(m.y.w, m.x.w, m.x.w, m.x.w);
const Vec4 inv0(vec1 * fac0 - vec2 * fac1 + vec3 * fac2);
const Vec4 inv1(vec0 * fac0 - vec2 * fac3 + vec3 * fac4);
const Vec4 inv2(vec0 * fac1 - vec1 * fac3 + vec3 * fac5);
const Vec4 inv3(vec0 * fac2 - vec1 * fac4 + vec2 * fac5);
constexpr Vec4 signA(+1.f, -1.f, +1.f, -1.f);
constexpr Vec4 signB(-1.f, +1.f, -1.f, +1.f);
const Mat4X4 inverse(inv0 * signA, inv1 * signB, inv2 * signA, inv3 * signB);
const Vec4 row0(inverse.x.x, inverse.y.x, inverse.z.x, inverse.w.x);
const Vec4 dot0(m.x * row0);
determinantOut = (dot0.x + dot0.y) + (dot0.z + dot0.w);
const float oneOverDeterminant = 1.0f / determinantOut;
return inverse * oneOverDeterminant;
}
/** Get determinant out from matrix 4X4 */
static inline constexpr float Determinant(const Mat4X4& mat4X4)
{
float determinant = 0.0f;
Inverse(mat4X4, determinant);
return determinant;
}
/** Inverse matrix */
static inline constexpr Mat4X4 Inverse(const Mat4X4& mat4X4)
{
float determinant = 0.0f;
return Inverse(mat4X4, determinant);
}
/** Inner product */
inline constexpr Vec4 operator*(const Mat4X4& m, const Vec4& v)
{
return { v.x * m.x.x + v.y * m.y.x + v.z * m.z.x + v.w * m.w.x,
v.x * m.x.y + v.y * m.y.y + v.z * m.z.y + v.w * m.w.y, v.x * m.x.z + v.y * m.y.z + v.z * m.z.z + v.w * m.w.z,
v.x * m.x.w + v.y * m.y.w + v.z * m.z.w + v.w * m.w.w };
}
inline constexpr Vec3 operator*(const Mat3X3& m, const Vec3& v)
{
return { v.x * m.x.x + v.y * m.y.x + v.z * m.z.x, v.x * m.x.y + v.y * m.y.y + v.z * m.z.y,
v.x * m.x.z + v.y * m.y.z + v.z * m.z.z };
}
/** Creates matrix for left handed symmetric perspective view frustum, near and far clip planes correspond to z
* normalized device coordinates of 0 and +1 */
static inline Mat4X4 PerspectiveLhZo(float fovy, float aspect, float zNear, float zFar)
{
float const tanHalfFovy = tan(fovy / 2.0f);
Mat4X4 result(0.f);
if (const auto div = (aspect * tanHalfFovy); abs(div) > EPSILON) {
result.x.x = 1.0f / div;
} else {
result.x.x = HUGE_VALF;
}
if (abs(tanHalfFovy) > EPSILON) {
result.y.y = 1.0f / (tanHalfFovy);
} else {
result.y.y = HUGE_VALF;
}
if (const auto div = (zFar - zNear); abs(div) > EPSILON) {
result.z.z = zFar / div;
result.w.z = -(zFar * zNear) / div;
} else {
result.z.z = HUGE_VALF;
result.w.z = HUGE_VALF;
}
result.z.w = 1.0f;
return result;
}
/** Creates matrix for right handed symmetric perspective view frustum, near and far clip planes correspond to z
* normalized device coordinates of 0 and +1 */
static inline Mat4X4 PerspectiveRhZo(float fovy, float aspect, float zNear, float zFar)
{
Mat4X4 result = PerspectiveLhZo(fovy, aspect, zNear, zFar);
result.z.z = -result.z.z;
result.z.w = -result.z.w;
return result;
}
/** Creates matrix for left handed symmetric perspective view frustum, near and far clip planes correspond to z
* normalized device coordinates of -1 and +1 */
static inline Mat4X4 PerspectiveLhNo(float fovy, float aspect, float zNear, float zFar)
{
float const tanHalfFovy = tan(fovy / 2.0f);
Mat4X4 result(0.f);
if (const auto div = (aspect * tanHalfFovy); abs(div) > EPSILON) {
result.x.x = 1.0f / div;
} else {
result.x.x = HUGE_VALF;
}
if (abs(tanHalfFovy) > EPSILON) {
result.y.y = 1.0f / (tanHalfFovy);
} else {
result.y.y = HUGE_VALF;
}
if (const auto div = (zFar - zNear); abs(div) > EPSILON) {
result.z.z = (zFar + zNear) / div;
result.w.z = -(2.0f * zFar * zNear) / div;
} else {
result.z.z = HUGE_VALF;
result.w.z = HUGE_VALF;
}
result.z.w = 1.0f;
return result;
}
/** Creates a matrix for right handed symmetric perspective view frustum, near and far clip planes correspond to z
* normalized device coordinates of -1 and +1 */
static inline Mat4X4 PerspectiveRhNo(float fovy, float aspect, float zNear, float zFar)
{
Mat4X4 result = PerspectiveLhNo(fovy, aspect, zNear, zFar);
result.z.z = -result.z.z;
result.z.w = -result.z.w;
return result;
}
/** Creates matrix for orthographic parallel viewing volume using left handed coordinates, near and far clip planes
* correspond to z normalized device coordinates of 0 and +1 */
static inline Mat4X4 OrthoLhZo(float left, float right, float bottom, float top, float zNear, float zFar)
{
Mat4X4 result(1.f);
result.x.x = 2.0f / (right - left);
result.y.y = 2.0f / (top - bottom);
result.z.z = 1.0f / (zFar - zNear);
result.w.x = -(right + left) / (right - left);
result.w.y = -(top + bottom) / (top - bottom);
result.w.z = -zNear / (zFar - zNear);
return result;
}
/** Creates matrix for orthographic parallel viewing volume using left handed coordinates, near and far clip planes
* correspond to z normalized device coordinates of -1 and +1 */
static inline Mat4X4 OrthoLhNo(float left, float right, float bottom, float top, float zNear, float zFar)
{
Mat4X4 result(1.f);
result.x.x = 2.0f / (right - left);
result.y.y = 2.0f / (top - bottom);
result.z.z = 2.0f / (zFar - zNear);
result.w.x = -(right + left) / (right - left);
result.w.y = -(top + bottom) / (top - bottom);
result.w.z = -(zFar + zNear) / (zFar - zNear);
return result;
}
/** Creates matrix for orthographic parallel viewing volume using right handed coordinates, near and far clip planes
* correspond to z normalized device coordinates of 0 and +1 */
static inline Mat4X4 OrthoRhZo(float left, float right, float bottom, float top, float zNear, float zFar)
{
Mat4X4 result = OrthoLhZo(left, right, bottom, top, zNear, zFar);
result.z.z = -result.z.z;
return result;
}
/** Creates matrix for orthographic parallel viewing volume using right handed coordinates, near and far clip planes
* correspond to z normalized device coordinates of -1 and +1 */
static inline Mat4X4 OrthoRhNo(float left, float right, float bottom, float top, float zNear, float zFar)
{
Mat4X4 result = OrthoLhNo(left, right, bottom, top, zNear, zFar);
result.z.z = -result.z.z;
return result;
}
/** Creates matrix for left handed perspective view frustum, near and far clip planes correspond to z normalized device
* coordinates of 0 and +1 */
static inline Mat4X4 PerspectiveLhZo(float left, float right, float bottom, float top, float zNear, float zFar)
{
Mat4X4 result(0.f);
if (const float width = right - left; abs(width) > EPSILON) {
result.x.x = 2.0f * zNear / width;
result.z.x = (left + right) / width;
} else {
result.x.x = HUGE_VALF;
result.z.x = HUGE_VALF;
}
if (const float height = top - bottom; abs(height) > EPSILON) {
result.y.y = 2.0f * zNear / height;
result.z.y = (top + bottom) / height;
} else {
result.y.y = HUGE_VALF;
result.z.y = HUGE_VALF;
}
if (const float depth = zFar - zNear; abs(depth) > EPSILON) {
result.z.z = zFar / depth;
result.w.z = -(zFar * zNear) / depth;
} else {
result.z.z = HUGE_VALF;
result.w.z = HUGE_VALF;
}
result.z.w = 1.0f;
return result;
}
/** Creates matrix for right handed perspective view frustum, near and far clip planes correspond to z normalized device
* coordinates of 0 and +1 */
static inline Mat4X4 PerspectiveRhZo(float left, float right, float bottom, float top, float zNear, float zFar)
{
Mat4X4 result = PerspectiveLhZo(left, right, bottom, top, zNear, zFar);
result.z.z = -result.z.z;
result.z.w = -result.z.w;
return result;
}
/** Creates matrix for left handed perspective view frustum, near and far clip planes correspond to z normalized device
* coordinates of -1 and +1 */
static inline Mat4X4 PerspectiveLhNo(float left, float right, float bottom, float top, float zNear, float zFar)
{
Mat4X4 result(0.f);
if (const float width = right - left; abs(width) > EPSILON) {
result.x.x = 2.0f * zNear / width;
result.z.x = (left + right) / width;
} else {
result.x.x = HUGE_VALF;
result.z.x = HUGE_VALF;
}
if (const float height = top - bottom; abs(height) > EPSILON) {
result.y.y = 2.0f * zNear / height;
result.z.y = (top + bottom) / height;
} else {
result.y.y = HUGE_VALF;
result.z.y = HUGE_VALF;
}
if (const float depth = zFar - zNear; abs(depth) > EPSILON) {
result.z.z = (zNear + zFar) / depth;
result.w.z = -(2.0f * zFar * zNear) / depth;
} else {
result.z.z = HUGE_VALF;
result.w.z = HUGE_VALF;
}
result.z.w = 1.0f;
return result;
}
/** Creates matrix for right handed perspective view frustum, near and far clip planes correspond to z normalized device
* coordinates of -1 and +1 */
static inline Mat4X4 PerspectiveRhNo(float left, float right, float bottom, float top, float zNear, float zFar)
{
Mat4X4 result = PerspectiveLhNo(left, right, bottom, top, zNear, zFar);
result.z.z = -result.z.z;
result.z.w = -result.z.w;
return result;
}
/** Right handed look at function */
static inline Mat4X4 LookAtRh(Vec3 const& eye, Vec3 const& center, Vec3 const& up)
{
Vec3 const f(Normalize(center - eye));
Vec3 const s(Normalize(Cross(f, up)));
Vec3 const u(Cross(s, f));
Mat4X4 result(1.f);
result.x.x = s.x;
result.y.x = s.y;
result.z.x = s.z;
result.x.y = u.x;
result.y.y = u.y;
result.z.y = u.z;
result.x.z = -f.x;
result.y.z = -f.y;
result.z.z = -f.z;
result.w.x = -Dot(s, eye);
result.w.y = -Dot(u, eye);
result.w.z = Dot(f, eye);
return result;
}
/** Left handed look at function */
static inline Mat4X4 LookAtLh(Vec3 const& eye, Vec3 const& center, Vec3 const& up)
{
Vec3 const f(Normalize(center - eye));
Vec3 const s(Normalize(Cross(up, f)));
Vec3 const u(Cross(f, s));
Mat4X4 result(1.f);
result.x.x = s.x;
result.y.x = s.y;
result.z.x = s.z;
result.x.y = u.x;
result.y.y = u.y;
result.z.y = u.z;
result.x.z = f.x;
result.y.z = f.y;
result.z.z = f.z;
result.w.x = -Dot(s, eye);
result.w.y = -Dot(u, eye);
result.w.z = -Dot(f, eye);
return result;
}
/** Decompose matrix */
static inline bool Decompose(
Mat4X4 const& modelMatrix, Vec3& scale, Quat& orientation, Vec3& translation, Vec3& skew, Vec4& perspective)
{
Mat4X4 localMatrix(modelMatrix);
if (localMatrix.w.w != 1.f) {
if (abs(localMatrix.w.w) < EPSILON) {
return false;
}
for (size_t i = 0; i < 4U; ++i) {
for (size_t j = 0; j < 4U; ++j) {
localMatrix[i][j] /= localMatrix.w.w;
}
}
}
if (abs(localMatrix.x.w) >= EPSILON || abs(localMatrix.y.w) >= EPSILON || abs(localMatrix.z.w) >= EPSILON) {
Vec4 rightHandSide;
rightHandSide.x = localMatrix.x.w;
rightHandSide.y = localMatrix.y.w;
rightHandSide.z = localMatrix.z.w;
rightHandSide.w = localMatrix.w.w;
Mat4X4 perspectiveMatrix(localMatrix);
for (size_t i = 0U; i < 3U; i++) {
perspectiveMatrix[i].w = 0.0f;
}
perspectiveMatrix.w.w = 1.0f;
float determinant;
const Mat4X4 inversePerspectiveMatrix = Inverse(perspectiveMatrix, determinant);
if (abs(determinant) < EPSILON) {
return false;
}
const Mat4X4 transposedInversePerspectiveMatrix = Transpose(inversePerspectiveMatrix);
perspective = transposedInversePerspectiveMatrix * rightHandSide;
localMatrix.x.w = localMatrix.y.w = localMatrix.z.w = 0.0f;
localMatrix.w.w = 1.0f;
} else {
perspective = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
}
translation = Vec3(localMatrix.w);
localMatrix.w = Vec4(0.0f, 0.0f, 0.0f, localMatrix.w.w);
Vec3 row[3];
for (size_t i = 0U; i < 3U; ++i) {
for (size_t j = 0U; j < 3U; ++j) {
row[i][j] = localMatrix[i][j];
}
}
scale.x = sqrt(Dot(row[0], row[0]));
row[0] = Math::Scale(row[0], 1.0f);
skew.z = Dot(row[0], row[1]);
row[1] = Combine(row[1], row[0], 1.0f, -skew.z);
scale.y = sqrt(Dot(row[1], row[1]));
row[1] = Math::Scale(row[1], 1.0f);
skew.z /= scale.y;
skew.y = Dot(row[0], row[2]);
row[2] = Combine(row[2], row[0], 1.0f, -skew.y);
skew.x = Dot(row[1], row[2]);
row[2] = Combine(row[2], row[1], 1.0f, -skew.x);
scale.z = sqrt(Dot(row[2], row[2]));
row[2] = Math::Scale(row[2], 1.0f);
skew.y /= scale.z;
skew.x /= scale.z;
const Vec3 pDum3 = Cross(row[1], row[2]);
if (Dot(row[0], pDum3) < 0) {
for (size_t i = 0U; i < 3U; i++) {
scale[i] *= -1.0f;
row[i] *= -1.0f;
}
}
unsigned i, j, k = 0U;
float root;
const float trace = row[0].x + row[1].y + row[2].z;
if (trace > 0.0f) {
root = sqrt(trace + 1.0f);
orientation.w = 0.5f * root;
root = 0.5f / root; // root cannot be zero as it's square root of at least 1
orientation.x = root * (row[1].z - row[2].y);
orientation.y = root * (row[2].x - row[0].z);
orientation.z = root * (row[0].y - row[1].x);
} else { // End if > 0
constexpr const unsigned next[3] = { 1U, 2U, 0U };
i = 0U;
if (row[1].y > row[0].x) {
i = 1U;
}
if (row[2].z > row[i][i]) {
i = 2U;
}
j = next[i];
k = next[j];
root = sqrt(row[i][i] - row[j][j] - row[k][k] + 1.0f);
orientation[i] = 0.5f * root;
root = (abs(root) > EPSILON) ? 0.5f / root : HUGE_VALF;
orientation[j] = root * (row[i][j] + row[j][i]);
orientation[k] = root * (row[i][k] + row[k][i]);
orientation.w = root * (row[j][k] - row[k][j]);
} // End if <= 0
return true;
}
/** Check if matrix has any rotation/shear components */
static inline bool HasRotation(Mat3X3 const& m)
{
return (m.x.y != 0.f || m.y.x != 0.f);
}
/** Check if matrix has any rotation/shear components */
static inline bool HasRotation(Mat4X4 const& m)
{
return (m.x.y != 0.f || m.x.z != 0.f || m.y.x != 0.f || m.y.z != 0.f || m.z.x != 0.f || m.z.y != 0.f);
}
/** @} */
} // namespace Math
BASE_END_NAMESPACE()
#endif // API_BASE_MATH_MATRIX_UTIL_H