feat: 添加Math库和Google Test测试框架

- 新增Math库: Vector2/3/4, Matrix3/4, Quaternion, Transform, Color等
- 新增测试框架: Google Test (gtest)
- 新增140个单元测试,覆盖Vector, Matrix, Quaternion, Geometry
- VolumeRenderer支持vcpkg的NanoVDB
- 添加TESTING.md测试文档
This commit is contained in:
2026-03-13 18:43:14 +08:00
parent 5efa171050
commit 7c54a62f9e
41 changed files with 8530 additions and 1845 deletions

49
engine/src/Math/Color.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include "Math/Color.h"
namespace XCEngine {
namespace Math {
Color Color::Lerp(const Color& a, const Color& b, float t) {
t = std::clamp(t, 0.0f, 1.0f);
return Color(
a.r + (b.r - a.r) * t,
a.g + (b.g - a.g) * t,
a.b + (b.b - a.b) * t,
a.a + (b.a - a.a) * t
);
}
uint32_t Color::ToRGBA() const {
uint32_t r = static_cast<uint32_t>(std::clamp(r * 255.0f, 0.0f, 255.0f));
uint32_t g = static_cast<uint32_t>(std::clamp(g * 255.0f, 0.0f, 255.0f));
uint32_t b = static_cast<uint32_t>(std::clamp(b * 255.0f, 0.0f, 255.0f));
uint32_t a = static_cast<uint32_t>(std::clamp(a * 255.0f, 0.0f, 255.0f));
return (r << 24) | (g << 16) | (b << 8) | a;
}
Vector3 Color::ToVector3() const {
return Vector3(r, g, b);
}
Vector4 Color::ToVector4() const {
return Vector4(r, g, b, a);
}
Color Color::operator+(const Color& other) const {
return Color(r + other.r, g + other.g, b + other.b, a + other.a);
}
Color Color::operator-(const Color& other) const {
return Color(r - other.r, g - other.g, b - other.b, a - other.a);
}
Color Color::operator*(float scalar) const {
return Color(r * scalar, g * scalar, b * scalar, a * scalar);
}
Color Color::operator/(float scalar) const {
return Color(r / scalar, g / scalar, b / scalar, a / scalar);
}
} // namespace Math
} // namespace XCEngine

View File

@@ -0,0 +1,159 @@
#include "Math/Frustum.h"
#include "Math/Bounds.h"
namespace XCEngine {
namespace Math {
bool Frustum::Contains(const Vector3& point) const {
for (int i = 0; i < 6; i++) {
if (planes[i].GetDistanceToPoint(point) < 0.0f) {
return false;
}
}
return true;
}
bool Frustum::Contains(const Sphere& sphere) const {
for (int i = 0; i < 6; i++) {
float distance = planes[i].GetDistanceToPoint(sphere.center);
if (distance < -sphere.radius) {
return false;
}
}
return true;
}
bool Frustum::Contains(const Bounds& bounds) const {
Vector3 corners[8] = {
bounds.GetMin(),
Vector3(bounds.GetMax().x, bounds.GetMin().y, bounds.GetMin().z),
Vector3(bounds.GetMin().x, bounds.GetMax().y, bounds.GetMin().z),
Vector3(bounds.GetMin().x, bounds.GetMin().y, bounds.GetMax().z),
bounds.GetMax(),
Vector3(bounds.GetMin().x, bounds.GetMax().y, bounds.GetMax().z),
Vector3(bounds.GetMax().x, bounds.GetMin().y, bounds.GetMax().z),
Vector3(bounds.GetMax().x, bounds.GetMax().y, bounds.GetMin().z)
};
for (int i = 0; i < 6; i++) {
bool allOutside = true;
for (int j = 0; j < 8; j++) {
if (planes[i].GetDistanceToPoint(corners[j]) >= 0.0f) {
allOutside = false;
break;
}
}
if (allOutside) {
return false;
}
}
return true;
}
bool Frustum::Intersects(const Bounds& bounds) const {
Vector3 corners[8] = {
bounds.GetMin(),
Vector3(bounds.GetMax().x, bounds.GetMin().y, bounds.GetMin().z),
Vector3(bounds.GetMin().x, bounds.GetMax().y, bounds.GetMin().z),
Vector3(bounds.GetMin().x, bounds.GetMin().y, bounds.GetMax().z),
bounds.GetMax(),
Vector3(bounds.GetMin().x, bounds.GetMax().y, bounds.GetMax().z),
Vector3(bounds.GetMax().x, bounds.GetMin().y, bounds.GetMax().z),
Vector3(bounds.GetMax().x, bounds.GetMax().y, bounds.GetMin().z)
};
for (int i = 0; i < 6; i++) {
bool allPositive = true;
bool allNegative = true;
for (int j = 0; j < 8; j++) {
float dist = planes[i].GetDistanceToPoint(corners[j]);
if (dist >= 0.0f) allNegative = false;
else allPositive = false;
}
if (allPositive || allNegative) {
return false;
}
}
return true;
}
bool Frustum::Intersects(const Sphere& sphere) const {
for (int i = 0; i < 6; i++) {
if (planes[i].GetDistanceToPoint(sphere.center) < -sphere.radius) {
return false;
}
}
return true;
}
Bounds::Bounds(const Vector3& center, const Vector3& size)
: center(center), extents(size * 0.5f) {}
Vector3 Bounds::GetMin() const {
return center - extents;
}
Vector3 Bounds::GetMax() const {
return center + extents;
}
void Bounds::SetMinMax(const Vector3& min, const Vector3& max) {
center = (min + max) * 0.5f;
extents = (max - min) * 0.5f;
}
bool Bounds::Intersects(const Bounds& other) const {
if (std::abs(center.x - other.center.x) > (extents.x + other.extents.x)) return false;
if (std::abs(center.y - other.center.y) > (extents.y + other.extents.y)) return false;
if (std::abs(center.z - other.center.z) > (extents.z + other.extents.z)) return false;
return true;
}
bool Bounds::Contains(const Vector3& point) const {
return point.x >= GetMin().x && point.x <= GetMax().x &&
point.y >= GetMin().y && point.y <= GetMax().y &&
point.z >= GetMin().z && point.z <= GetMax().z;
}
void Bounds::Encapsulate(const Vector3& point) {
Vector3 min = GetMin();
Vector3 max = GetMax();
min = Vector3(std::min(min.x, point.x), std::min(min.y, point.y), std::min(min.z, point.z));
max = Vector3(std::max(max.x, point.x), std::max(max.y, point.y), std::max(max.z, point.z));
SetMinMax(min, max);
}
void Bounds::Encapsulate(const Bounds& bounds) {
Vector3 min = GetMin();
Vector3 max = GetMax();
Vector3 boundsMin = bounds.GetMin();
Vector3 boundsMax = bounds.GetMax();
min = Vector3(std::min(min.x, boundsMin.x), std::min(min.y, boundsMin.y), std::min(min.z, boundsMin.z));
max = Vector3(std::max(max.x, boundsMax.x), std::max(max.y, boundsMax.y), std::max(max.z, boundsMax.z));
SetMinMax(min, max);
}
void Bounds::Expand(float amount) {
extents += Vector3(amount, amount, amount) * 0.5f;
}
void Bounds::Expand(const Vector3& amount) {
extents += amount * 0.5f;
}
Vector3 Bounds::GetClosestPoint(const Vector3& point) const {
Vector3 result = point;
Vector3 min = GetMin();
Vector3 max = GetMax();
result.x = std::clamp(result.x, min.x, max.x);
result.y = std::clamp(result.y, min.y, max.y);
result.z = std::clamp(result.z, min.z, max.z);
return result;
}
float Bounds::GetVolume() const {
return (extents.x * 2.0f) * (extents.y * 2.0f) * (extents.z * 2.0f);
}
} // namespace Math
} // namespace XCEngine

View File

@@ -0,0 +1,161 @@
#include "Math/Sphere.h"
#include "Math/Plane.h"
#include "Math/Box.h"
#include "Math/Ray.h"
namespace XCEngine {
namespace Math {
Ray::Ray(const Vector3& origin, const Vector3& direction)
: origin(origin), direction(Vector3::Normalize(direction)) {}
Vector3 Ray::GetPoint(float t) const {
return origin + direction * t;
}
bool Ray::Intersects(const Sphere& sphere, float& t) const {
Vector3 oc = origin - sphere.center;
float a = Vector3::Dot(direction, direction);
float b = 2.0f * Vector3::Dot(oc, direction);
float c = Vector3::Dot(oc, oc) - sphere.radius * sphere.radius;
float discriminant = b * b - 4 * a * c;
if (discriminant < 0) {
return false;
}
float sqrtDiscriminant = std::sqrt(discriminant);
float t0 = (-b - sqrtDiscriminant) / (2.0f * a);
float t1 = (-b + sqrtDiscriminant) / (2.0f * a);
if (t0 > 0) {
t = t0;
return true;
} else if (t1 > 0) {
t = t1;
return true;
}
return false;
}
bool Ray::Intersects(const Box& box, float& t) const {
float tmin = 0.0f;
float tmax = FLOAT_MAX;
Vector3 invDir = Vector3(1.0f / direction.x, 1.0f / direction.y, 1.0f / direction.z);
int sign[3] = { invDir.x < 0, invDir.y < 0, invDir.z < 0 };
Vector3 bounds[2] = { box.GetMin(), box.GetMax() };
for (int i = 0; i < 3; i++) {
float t0 = (bounds[sign[i]][i] - origin[i]) * invDir[i];
float t1 = (bounds[1 - sign[i]][i] - origin[i]) * invDir[i];
tmin = std::max(tmin, std::min(t0, t1));
tmax = std::min(tmax, std::max(t0, t1));
if (tmax < tmin) {
return false;
}
}
if (tmin > 0) {
t = tmin;
} else {
t = tmax;
}
return tmax >= 0;
}
bool Ray::Intersects(const Plane& plane, float& t) const {
float denom = Vector3::Dot(plane.normal, direction);
if (std::abs(denom) < EPSILON) {
return false;
}
t = -(Vector3::Dot(plane.normal, origin) + plane.distance) / denom;
return t >= 0;
}
Sphere::Sphere(const Vector3& center, float radius) : center(center), radius(radius) {}
bool Sphere::Contains(const Vector3& point) const {
return Vector3::SqrMagnitude(point - center) <= radius * radius;
}
bool Sphere::Intersects(const Sphere& other) const {
float sqrDist = Vector3::SqrMagnitude(center - other.center);
float sumRadii = radius + other.radius;
return sqrDist <= sumRadii * sumRadii;
}
Plane::Plane(const Vector3& normal, float distance) : normal(Vector3::Normalize(normal)), distance(distance) {}
Plane Plane::FromPoints(const Vector3& a, const Vector3& b, const Vector3& c) {
Vector3 normal = Vector3::Normalize(Vector3::Cross(b - a, c - a));
float distance = Vector3::Dot(normal, a);
return Plane(normal, distance);
}
float Plane::GetDistanceToPoint(const Vector3& point) const {
return Vector3::Dot(normal, point) + distance;
}
Vector3 Plane::GetClosestPoint(const Vector3& point) const {
float distance = GetDistanceToPoint(point);
return point - normal * distance;
}
bool Plane::GetSide(const Vector3& point) const {
return GetDistanceToPoint(point) > 0.0f;
}
bool Plane::Intersects(const Sphere& sphere) const {
return std::abs(GetDistanceToPoint(sphere.center)) <= sphere.radius;
}
Box::Box(const Vector3& center, const Vector3& extents) : center(center), extents(extents) {}
Vector3 Box::GetMin() const {
return center - extents;
}
Vector3 Box::GetMax() const {
return center + extents;
}
bool Box::Contains(const Vector3& point) const {
Vector3 localPoint = transform.Inverse().MultiplyPoint(point);
return std::abs(localPoint.x) <= extents.x &&
std::abs(localPoint.y) <= extents.y &&
std::abs(localPoint.z) <= extents.z;
}
bool Box::Intersects(const Sphere& sphere) const {
Vector3 localCenter = transform.Inverse().MultiplyPoint(sphere.center);
Vector3 closestPoint;
closestPoint.x = std::clamp(localCenter.x, -extents.x, extents.x);
closestPoint.y = std::clamp(localCenter.y, -extents.y, extents.y);
closestPoint.z = std::clamp(localCenter.z, -extents.z, extents.z);
float sqrDist = Vector3::SqrMagnitude(localCenter - closestPoint);
return sqrDist <= sphere.radius * sphere.radius;
}
bool Box::Intersects(const Box& other) const {
Vector3 aMin = GetMin();
Vector3 aMax = GetMax();
Vector3 bMin = other.GetMin();
Vector3 bMax = other.GetMax();
return (aMin.x <= bMax.x && aMax.x >= bMin.x) &&
(aMin.y <= bMax.y && aMax.y >= bMin.y) &&
(aMin.z <= bMax.z && aMax.z >= bMin.z);
}
bool Box::Intersects(const Ray& ray, float& t) const {
return ray.Intersects(*this, t);
}
} // namespace Math
} // namespace XCEngine

329
engine/src/Math/Matrix.cpp Normal file
View File

@@ -0,0 +1,329 @@
#include "Math/Matrix3.h"
#include "Math/Matrix4.h"
#include "Math/Quaternion.h"
#include "Math/Vector3.h"
namespace XCEngine {
namespace Math {
Matrix3 Matrix3::RotationX(float radians) {
float c = std::cos(radians);
float s = std::sin(radians);
Matrix3 result;
result.m[1][1] = c; result.m[1][2] = -s;
result.m[2][1] = s; result.m[2][2] = c;
return result;
}
Matrix3 Matrix3::RotationY(float radians) {
float c = std::cos(radians);
float s = std::sin(radians);
Matrix3 result;
result.m[0][0] = c; result.m[0][2] = s;
result.m[2][0] = -s; result.m[2][2] = c;
return result;
}
Matrix3 Matrix3::RotationZ(float radians) {
float c = std::cos(radians);
float s = std::sin(radians);
Matrix3 result;
result.m[0][0] = c; result.m[0][1] = -s;
result.m[1][0] = s; result.m[1][1] = c;
return result;
}
Matrix3 Matrix3::Scale(const Vector3& scale) {
Matrix3 result;
result.m[0][0] = scale.x;
result.m[1][1] = scale.y;
result.m[2][2] = scale.z;
return result;
}
Matrix3 Matrix3::operator*(const Matrix3& other) const {
Matrix3 result;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
result.m[i][j] = m[i][0] * other.m[0][j] +
m[i][1] * other.m[1][j] +
m[i][2] * other.m[2][j];
}
}
return result;
}
Vector3 Matrix3::operator*(const Vector3& v) const {
return Vector3(
m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z,
m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z,
m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z
);
}
Matrix3 Matrix3::Transpose() const {
Matrix3 result;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
result.m[i][j] = m[j][i];
return result;
}
float Matrix3::Determinant() const {
return m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) -
m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]) +
m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
}
Matrix3 Matrix3::Inverse() const {
float det = Determinant();
if (std::abs(det) < EPSILON) {
return Identity();
}
float invDet = 1.0f / det;
Matrix3 result;
result.m[0][0] = (m[1][1] * m[2][2] - m[1][2] * m[2][1]) * invDet;
result.m[0][1] = (m[0][2] * m[2][1] - m[0][1] * m[2][2]) * invDet;
result.m[0][2] = (m[0][1] * m[1][2] - m[0][2] * m[1][1]) * invDet;
result.m[1][0] = (m[1][2] * m[2][0] - m[1][0] * m[2][2]) * invDet;
result.m[1][1] = (m[0][0] * m[2][2] - m[0][2] * m[2][0]) * invDet;
result.m[1][2] = (m[1][0] * m[0][2] - m[0][0] * m[1][2]) * invDet;
result.m[2][0] = (m[1][0] * m[2][1] - m[1][1] * m[2][0]) * invDet;
result.m[2][1] = (m[2][0] * m[0][1] - m[0][0] * m[2][1]) * invDet;
result.m[2][2] = (m[0][0] * m[1][1] - m[1][0] * m[0][1]) * invDet;
return result;
}
Matrix4 Matrix4::Translation(const Vector3& v) {
Matrix4 result = Identity();
result.m[0][3] = v.x;
result.m[1][3] = v.y;
result.m[2][3] = v.z;
return result;
}
Matrix4 Matrix4::Rotation(const Quaternion& q) {
return q.ToMatrix4x4();
}
Matrix4 Matrix4::Scale(const Vector3& v) {
Matrix4 result;
result.m[0][0] = v.x;
result.m[1][1] = v.y;
result.m[2][2] = v.z;
result.m[3][3] = 1.0f;
return result;
}
Matrix4 Matrix4::TRS(const Vector3& translation, const Quaternion& rotation, const Vector3& scale) {
return Translation(translation) * Rotation(rotation) * Scale(scale);
}
Matrix4 Matrix4::LookAt(const Vector3& eye, const Vector3& target, const Vector3& up) {
Vector3 z = Vector3::Normalize(eye - target);
if (Vector3::SqrMagnitude(z) < EPSILON) {
z = Vector3::Forward();
}
Vector3 x = Vector3::Normalize(Vector3::Cross(up, z));
if (Vector3::SqrMagnitude(x) < EPSILON) {
x = Vector3::Right();
}
Vector3 y = Vector3::Cross(z, x);
Matrix4 result;
result.m[0][0] = x.x; result.m[0][1] = x.y; result.m[0][2] = x.z; result.m[0][3] = -Vector3::Dot(x, eye);
result.m[1][0] = y.x; result.m[1][1] = y.y; result.m[1][2] = y.z; result.m[1][3] = -Vector3::Dot(y, eye);
result.m[2][0] = z.x; result.m[2][1] = z.y; result.m[2][2] = z.z; result.m[2][3] = -Vector3::Dot(z, eye);
result.m[3][0] = 0.0f; result.m[3][1] = 0.0f; result.m[3][2] = 0.0f; result.m[3][3] = 1.0f;
return result;
}
Matrix4 Matrix4::Perspective(float fov, float aspect, float near, float far) {
float tanHalfFov = std::tan(fov * 0.5f);
Matrix4 result;
result.m[0][0] = 1.0f / (aspect * tanHalfFov);
result.m[1][1] = 1.0f / tanHalfFov;
result.m[2][2] = -(far + near) / (far - near);
result.m[2][3] = -(2.0f * far * near) / (far - near);
result.m[3][2] = -1.0f;
return result;
}
Matrix4 Matrix4::Orthographic(float left, float right, float bottom, float top, float near, float far) {
Matrix4 result = Identity();
result.m[0][0] = 2.0f / (right - left);
result.m[1][1] = 2.0f / (top - bottom);
result.m[2][2] = -2.0f / (far - near);
result.m[0][3] = -(right + left) / (right - left);
result.m[1][3] = -(top + bottom) / (top - bottom);
result.m[2][3] = -(far + near) / (far - near);
return result;
}
Matrix4 Matrix4::RotationX(float radians) {
float c = std::cos(radians);
float s = std::sin(radians);
Matrix4 result = Identity();
result.m[1][1] = c; result.m[1][2] = -s;
result.m[2][1] = s; result.m[2][2] = c;
return result;
}
Matrix4 Matrix4::RotationY(float radians) {
float c = std::cos(radians);
float s = std::sin(radians);
Matrix4 result = Identity();
result.m[0][0] = c; result.m[0][2] = s;
result.m[2][0] = -s; result.m[2][2] = c;
return result;
}
Matrix4 Matrix4::RotationZ(float radians) {
float c = std::cos(radians);
float s = std::sin(radians);
Matrix4 result = Identity();
result.m[0][0] = c; result.m[0][1] = -s;
result.m[1][0] = s; result.m[1][1] = c;
return result;
}
Matrix4 Matrix4::operator*(const Matrix4& other) const {
Matrix4 result;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
result.m[i][j] = m[i][0] * other.m[0][j] +
m[i][1] * other.m[1][j] +
m[i][2] * other.m[2][j] +
m[i][3] * other.m[3][j];
}
}
return result;
}
Vector4 Matrix4::operator*(const Vector4& v) const {
return Vector4(
m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z + m[0][3] * v.w,
m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z + m[1][3] * v.w,
m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z + m[2][3] * v.w,
m[3][0] * v.x + m[3][1] * v.y + m[3][2] * v.z + m[3][3] * v.w
);
}
Vector3 Matrix4::MultiplyPoint(const Vector3& v) const {
Vector4 v4(v.x, v.y, v.z, 1.0f);
Vector4 result = (*this) * v4;
return Vector3(result.x, result.y, result.z);
}
Vector3 Matrix4::MultiplyVector(const Vector3& v) const {
Vector4 v4(v.x, v.y, v.z, 0.0f);
Vector4 result = (*this) * v4;
return Vector3(result.x, result.y, result.z);
}
Matrix4 Matrix4::Transpose() const {
Matrix4 result;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
result.m[i][j] = m[j][i];
}
}
return result;
}
float Matrix4::Determinant() const {
float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2], a03 = m[0][3];
float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2], a13 = m[1][3];
float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2], a23 = m[2][3];
float a30 = m[3][0], a31 = m[3][1], a32 = m[3][2], a33 = m[3][3];
float b00 = a00 * a11 - a01 * a10;
float b01 = a00 * a12 - a02 * a10;
float b02 = a00 * a13 - a03 * a10;
float b03 = a01 * a12 - a02 * a11;
float b04 = a01 * a13 - a03 * a11;
float b05 = a02 * a13 - a03 * a12;
float b06 = a20 * a31 - a21 * a30;
float b07 = a20 * a32 - a22 * a30;
float b08 = a20 * a33 - a23 * a30;
float b09 = a21 * a32 - a22 * a31;
float b10 = a21 * a33 - a23 * a31;
float b11 = a22 * a33 - a23 * a32;
return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
}
Matrix4 Matrix4::Inverse() const {
float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2], a03 = m[0][3];
float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2], a13 = m[1][3];
float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2], a23 = m[2][3];
float a30 = m[3][0], a31 = m[3][1], a32 = m[3][2], a33 = m[3][3];
float b00 = a00 * a11 - a01 * a10;
float b01 = a00 * a12 - a02 * a10;
float b02 = a00 * a13 - a03 * a10;
float b03 = a01 * a12 - a02 * a11;
float b04 = a01 * a13 - a03 * a11;
float b05 = a02 * a13 - a03 * a12;
float b06 = a20 * a31 - a21 * a30;
float b07 = a20 * a32 - a22 * a30;
float b08 = a20 * a33 - a23 * a30;
float b09 = a21 * a32 - a22 * a31;
float b10 = a21 * a33 - a23 * a31;
float b11 = a22 * a33 - a23 * a32;
float det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
if (std::abs(det) < EPSILON) {
return Identity();
}
float invDet = 1.0f / det;
Matrix4 result;
result.m[0][0] = (a11 * b11 - a12 * b10 + a13 * b09) * invDet;
result.m[0][1] = (a02 * b10 - a01 * b11 - a03 * b09) * invDet;
result.m[0][2] = (a31 * b05 - a32 * b04 + a33 * b03) * invDet;
result.m[0][3] = (a22 * b04 - a21 * b05 - a23 * b03) * invDet;
result.m[1][0] = (a12 * b08 - a10 * b11 - a13 * b07) * invDet;
result.m[1][1] = (a00 * b11 - a02 * b08 + a03 * b07) * invDet;
result.m[1][2] = (a32 * b02 - a30 * b05 - a33 * b01) * invDet;
result.m[1][3] = (a20 * b05 - a22 * b02 + a23 * b01) * invDet;
result.m[2][0] = (a10 * b10 - a11 * b08 + a13 * b06) * invDet;
result.m[2][1] = (a01 * b08 - a00 * b10 - a03 * b06) * invDet;
result.m[2][2] = (a30 * b04 - a31 * b02 + a33 * b00) * invDet;
result.m[2][3] = (a21 * b02 - a20 * b04 - a23 * b00) * invDet;
result.m[3][0] = (a11 * b07 - a10 * b09 - a12 * b06) * invDet;
result.m[3][1] = (a00 * b09 - a01 * b07 + a02 * b06) * invDet;
result.m[3][2] = (a31 * b01 - a30 * b03 - a32 * b00) * invDet;
result.m[3][3] = (a20 * b03 - a21 * b01 + a22 * b00) * invDet;
return result;
}
Vector3 Matrix4::GetTranslation() const {
return Vector3(m[0][3], m[1][3], m[2][3]);
}
Quaternion Matrix4::GetRotation() const {
return Quaternion::FromRotationMatrix(*this);
}
Vector3 Matrix4::GetScale() const {
return Vector3(
std::sqrt(m[0][0] * m[0][0] + m[1][0] * m[1][0] + m[2][0] * m[2][0]),
std::sqrt(m[0][1] * m[0][1] + m[1][1] * m[1][1] + m[2][1] * m[2][1]),
std::sqrt(m[0][2] * m[0][2] + m[1][2] * m[1][2] + m[2][2] * m[2][2])
);
}
void Matrix4::Decompose(Vector3& translation, Quaternion& rotation, Vector3& scale) const {
translation = GetTranslation();
scale = GetScale();
rotation = GetRotation();
}
} // namespace Math
} // namespace XCEngine

View File

@@ -0,0 +1,223 @@
#include "Math/Quaternion.h"
#include "Math/Matrix4.h"
#include "Math/Vector3.h"
namespace XCEngine {
namespace Math {
Quaternion Quaternion::FromAxisAngle(const Vector3& axis, float radians) {
float halfAngle = radians * 0.5f;
float s = std::sin(halfAngle);
return Quaternion(
axis.x * s,
axis.y * s,
axis.z * s,
std::cos(halfAngle)
);
}
Quaternion Quaternion::FromEulerAngles(float pitch, float yaw, float roll) {
float halfPitch = pitch * 0.5f;
float halfYaw = yaw * 0.5f;
float halfRoll = roll * 0.5f;
float sinPitch = std::sin(halfPitch);
float cosPitch = std::cos(halfPitch);
float sinYaw = std::sin(halfYaw);
float cosYaw = std::cos(halfYaw);
float sinRoll = std::sin(halfRoll);
float cosRoll = std::cos(halfRoll);
return Quaternion(
cosYaw * sinPitch * cosRoll + sinYaw * cosPitch * sinRoll,
sinYaw * cosPitch * cosRoll - cosYaw * sinPitch * sinRoll,
cosYaw * cosPitch * sinRoll - sinYaw * sinPitch * cosRoll,
cosYaw * cosPitch * cosRoll + sinYaw * sinPitch * sinRoll
);
}
Quaternion Quaternion::FromEulerAngles(const Vector3& euler) {
return FromEulerAngles(euler.x, euler.y, euler.z);
}
Quaternion Quaternion::FromRotationMatrix(const Matrix4& matrix) {
float trace = matrix.m[0][0] + matrix.m[1][1] + matrix.m[2][2];
Quaternion q;
if (trace > 0.0f) {
float s = std::sqrt(trace + 1.0f) * 2.0f;
q.w = 0.25f * s;
q.x = (matrix.m[2][1] - matrix.m[1][2]) / s;
q.y = (matrix.m[0][2] - matrix.m[2][0]) / s;
q.z = (matrix.m[1][0] - matrix.m[0][1]) / s;
} else if (matrix.m[0][0] > matrix.m[1][1] && matrix.m[0][0] > matrix.m[2][2]) {
float s = std::sqrt(1.0f + matrix.m[0][0] - matrix.m[1][1] - matrix.m[2][2]) * 2.0f;
q.w = (matrix.m[2][1] - matrix.m[1][2]) / s;
q.x = 0.25f * s;
q.y = (matrix.m[0][1] + matrix.m[1][0]) / s;
q.z = (matrix.m[0][2] + matrix.m[2][0]) / s;
} else if (matrix.m[1][1] > matrix.m[2][2]) {
float s = std::sqrt(1.0f + matrix.m[1][1] - matrix.m[0][0] - matrix.m[2][2]) * 2.0f;
q.w = (matrix.m[0][2] - matrix.m[2][0]) / s;
q.x = (matrix.m[0][1] + matrix.m[1][0]) / s;
q.y = 0.25f * s;
q.z = (matrix.m[1][2] + matrix.m[2][1]) / s;
} else {
float s = std::sqrt(1.0f + matrix.m[2][2] - matrix.m[0][0] - matrix.m[1][1]) * 2.0f;
q.w = (matrix.m[1][0] - matrix.m[0][1]) / s;
q.x = (matrix.m[0][2] + matrix.m[2][0]) / s;
q.y = (matrix.m[1][2] + matrix.m[2][1]) / s;
q.z = 0.25f * s;
}
return q.Normalized();
}
Quaternion Quaternion::Slerp(const Quaternion& a, const Quaternion& b, float t) {
t = std::clamp(t, 0.0f, 1.0f);
float cosHalfTheta = a.w * b.w + a.x * b.x + a.y * b.y + a.z * b.z;
if (cosHalfTheta < 0.0f) {
return Slerp(Quaternion(-b.x, -b.y, -b.z, -b.w), Quaternion(a.x, a.y, a.z, a.w), 1.0f - t);
}
if (std::abs(cosHalfTheta) >= 1.0f) {
return a;
}
float halfTheta = std::acos(cosHalfTheta);
float sinHalfTheta = std::sqrt(1.0f - cosHalfTheta * cosHalfTheta);
if (std::abs(sinHalfTheta) < EPSILON) {
return Quaternion(
a.w * 0.5f + b.w * 0.5f,
a.x * 0.5f + b.x * 0.5f,
a.y * 0.5f + b.y * 0.5f,
a.z * 0.5f + b.z * 0.5f
);
}
float ratioA = std::sin((1.0f - t) * halfTheta) / sinHalfTheta;
float ratioB = std::sin(t * halfTheta) / sinHalfTheta;
return Quaternion(
a.x * ratioA + b.x * ratioB,
a.y * ratioA + b.y * ratioB,
a.z * ratioA + b.z * ratioB,
a.w * ratioA + b.w * ratioB
);
}
Quaternion Quaternion::LookRotation(const Vector3& forward, const Vector3& up) {
Vector3 f = Vector3::Normalize(forward);
Vector3 r = Vector3::Normalize(Vector3::Cross(up, f));
Vector3 u = Vector3::Cross(f, r);
Matrix4 m;
m.m[0][0] = r.x; m.m[0][1] = r.y; m.m[0][2] = r.z;
m.m[1][0] = u.x; m.m[1][1] = u.y; m.m[1][2] = u.z;
m.m[2][0] = f.x; m.m[2][1] = f.y; m.m[2][2] = f.z;
m.m[0][3] = m[1][3] = m[2][3] = 0.0f;
m.m[3][0] = m[3][1] = m[3][2] = 0.0f;
m.m[3][3] = 1.0f;
return FromRotationMatrix(m);
}
Vector3 Quaternion::ToEulerAngles() const {
float sinrCosp = 2.0f * (w * x + y * z);
float cosrCosp = 1.0f - 2.0f * (x * x + y * y);
float roll = std::atan2(sinrCosp, cosrCosp);
float sinp = 2.0f * (w * y - z * x);
float pitch;
if (std::abs(sinp) >= 1.0f) {
pitch = std::copysign(PI * 0.5f, sinp);
} else {
pitch = std::asin(sinp);
}
float sinyCosp = 2.0f * (w * z + x * y);
float cosyCosp = 1.0f - 2.0f * (y * y + z * z);
float yaw = std::atan2(sinyCosp, cosyCosp);
return Vector3(pitch, yaw, roll);
}
Matrix4 Quaternion::ToMatrix4x4() const {
float xx = x * x;
float yy = y * y;
float zz = z * z;
float xy = x * y;
float xz = x * z;
float yz = y * z;
float wx = w * x;
float wy = w * y;
float wz = w * z;
Matrix4 result;
result.m[0][0] = 1.0f - 2.0f * (yy + zz);
result.m[0][1] = 2.0f * (xy - wz);
result.m[0][2] = 2.0f * (xz + wy);
result.m[1][0] = 2.0f * (xy + wz);
result.m[1][1] = 1.0f - 2.0f * (xx + zz);
result.m[1][2] = 2.0f * (yz - wx);
result.m[2][0] = 2.0f * (xz - wy);
result.m[2][1] = 2.0f * (yz + wx);
result.m[2][2] = 1.0f - 2.0f * (xx + yy);
result.m[3][3] = 1.0f;
return result;
}
Quaternion Quaternion::operator*(const Quaternion& other) const {
return Quaternion(
w * other.x + x * other.w + y * other.z - z * other.y,
w * other.y - x * other.z + y * other.w + z * other.x,
w * other.z + x * other.y - y * other.x + z * other.w,
w * other.w - x * other.x - y * other.y - z * other.z
);
}
Quaternion Quaternion::Inverse() const {
float norm = x * x + y * y + z * z + w * w;
if (norm > 0.0f) {
float invNorm = 1.0f / norm;
return Quaternion(-x * invNorm, -y * invNorm, -z * invNorm, w * invNorm);
}
return Identity();
}
float Quaternion::Dot(const Quaternion& other) const {
return x * other.x + y * other.y + z * other.z + w * other.w;
}
float Quaternion::Magnitude() const {
return std::sqrt(x * x + y * y + z * z + w * w);
}
Quaternion Quaternion::Normalized() const {
float mag = Magnitude();
if (mag > EPSILON) {
return Quaternion(x / mag, y / mag, z / mag, w / mag);
}
return Identity();
}
Quaternion Quaternion::Normalize(const Quaternion& q) {
return q.Normalized();
}
Vector3 operator*(const Quaternion& q, const Vector3& v) {
Vector3 qv(q.x, q.y, q.z);
Vector3 uv = Vector3::Cross(qv, v);
Vector3 uuv = Vector3::Cross(qv, uv);
return v + (uv * q.w + uuv) * 2.0f;
}
} // namespace Math
} // namespace XCEngine

View File

@@ -0,0 +1,44 @@
#include "Math/Transform.h"
#include "Math/Matrix4.h"
namespace XCEngine {
namespace Math {
Matrix4 Transform::ToMatrix() const {
return Matrix4x4::TRS(position, rotation, scale);
}
Transform Transform::Inverse() const {
Transform result;
result.rotation = rotation.Inverse();
result.scale = Vector3::One() / scale;
result.position = result.rotation * (Vector3::Zero() - position) * result.scale;
return result;
}
Transform Transform::operator*(const Transform& other) const {
Transform result;
result.position = position + rotation * (scale * other.position);
result.rotation = rotation * other.rotation;
result.scale = scale * other.scale;
return result;
}
Vector3 Transform::TransformPoint(const Vector3& point) const {
return position + rotation * (scale * point);
}
Vector3 Transform::TransformDirection(const Vector3& direction) const {
return rotation * (scale * direction);
}
Vector3 Transform::InverseTransformPoint(const Vector3& point) const {
return (rotation.Inverse() * (point - position)) / scale;
}
Vector3 Transform::InverseTransformDirection(const Vector3& direction) const {
return rotation.Inverse() * direction / scale;
}
} // namespace Math
} // namespace XCEngine