Orange/include/Orange/Math/Transformation.h

158 lines
4.1 KiB
C++

#pragma once
#include <Orange/Math/Common.h>
#include <Orange/Math/Vector.h>
#include <Orange/Math/Quaternion.h>
namespace orange::Math
{
mat4 translate(const vec3& v)
{
mat4 result;
result[3] = vec4{ v[0], v[1], v[2], 1.0f };
return result;
}
mat4 rotate(const Radian& angle, const vec3& v)
{
const float c = Math::cos(angle);
const float s = Math::sin(angle);
const vec3 axis = normalize(v);
const vec3 t = (1.0f - c) * axis;
mat4 rot;
rot[0][0] = c + t[0] * axis[0];
rot[0][1] = 0 + t[0] * axis[1] + s * axis[2];
rot[0][2] = 0 + t[0] * axis[2] - s * axis[1];
rot[0][3] = 0;
rot[1][0] = 0 + t[1] * axis[0] - s * axis[2];
rot[1][1] = c + t[1] * axis[1];
rot[1][2] = 0 + t[1] * axis[2] + s * axis[0];
rot[1][3] = 0;
rot[2][0] = 0 + t[2] * axis[0] + s * axis[1];
rot[2][1] = 0 + t[2] * axis[1] - s * axis[0];
rot[2][2] = c + t[2] * axis[2];
rot[2][3] = 0;
return rot;
}
mat4 scale(const vec3& v)
{
return mat4{vec4{v[0], v[1], v[2], 1.0f}};
}
mat4 ortho(float left, float right, float bottom, float top)
{
mat4 result;
result[0][0] = 2.0f / (right - left);
result[1][1] = 2.0f / (top - bottom);
result[2][2] = -1.0f;
result[3][0] = -(right + left) / (right - left);
result[3][1] = -(top + bottom) / (top - bottom);
return result;
}
mat4 ortho(float left, float right, float bottom, float top, float zNear, float zFar)
{
mat4 result;
result[0][0] = 2.0f / (right - left);
result[1][1] = 2.0f / (top - bottom);
result[2][2] = -2.0f / (zFar - zNear);
result[3][0] = -(right + left) / (right - left);
result[3][1] = -(top + bottom) / (top - bottom);
result[3][2] = -(zFar + zNear) / (zFar - zNear);
return result;
}
mat4 perspective(const Radian& fovy, float aspect, float zNear, float zFar)
{
Assert(fabsf(aspect - Math::Epsilon) > 0.0f && "Math::perspective `fovy` is 0/inf.");
const float tanHalfFovy = Math::tan(0.5f * fovy);
mat4 result{0.0f};
result[0][0] = 1.0f / (aspect * tanHalfFovy);
result[1][1] = 1.0f / (tanHalfFovy);
result[2][2] = -(zFar + zNear) / (zFar - zNear);
result[2][3] = -1.0f;
result[3][2] = -2.0f * zFar * zNear / (zFar - zNear);
return result;
}
mat4 infinitePerspective(const Radian& fovy, float aspect, float zNear)
{
const float range = Math::tan(0.5f * fovy) * zNear;
const float left = -range * aspect;
const float right = range * aspect;
const float bottom = -range;
const float top = range;
mat4 result{0.0f};
result[0][0] = (2.0f * zNear) / (right - left);
result[1][1] = (2.0f * zNear) / (top - bottom);
result[2][2] = -1.0f;
result[2][3] = -1.0f;
result[3][2] = -2.0f * zNear;
return result;
}
template <typename T>
T lookAt(const vec3& eye, const vec3& center, const vec3& up);
template <>
mat4 lookAt<mat4>(
const vec3& eye,
const vec3& center,
const vec3& up)
{
const vec3 f = normalize(center - eye);
const vec3 s = normalize(cross(f, up));
const vec3 u = cross(s, f);
mat4 result;
result[0][0] = +s.x;
result[1][0] = +s.y;
result[2][0] = +s.z;
result[0][1] = +u.x;
result[1][1] = +u.y;
result[2][1] = +u.z;
result[0][2] = -f.x;
result[1][2] = -f.y;
result[2][2] = -f.z;
result[3][0] = -dot(s, eye);
result[3][1] = -dot(u, eye);
result[3][2] = +dot(f, eye);
return result;
}
template <>
quat lookAt<quat>(
const vec3& eye,
const vec3& center,
const vec3& up)
{
const float similar = 0.001f;
if (length(center - eye) < similar)
return quat{}; // You cannot look at where you are!
return matrix4ToQuaternion(lookAt<mat4>(eye, center, up));
}
}