use serde::{Deserialize, Serialize};
pub use glam;
pub use glam::{DMat3, DMat4, DQuat, DVec2, DVec3, DVec4};
pub use glam::{Mat3, Mat4, Quat, Vec2, Vec3, Vec4};
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Transform2D {
pub position: Vec2,
pub rotation: f32,
pub scale: Vec2,
}
impl Transform2D {
pub const IDENTITY: Self = Self {
position: Vec2::ZERO,
rotation: 0.0,
scale: Vec2::ONE,
};
#[must_use]
pub fn new(position: Vec2, rotation: f32, scale: Vec2) -> Self {
Self {
position,
rotation,
scale,
}
}
#[must_use]
#[inline]
pub fn to_matrix(&self) -> Mat3 {
let (sin, cos) = self.rotation.sin_cos();
Mat3::from_cols(
Vec3::new(cos * self.scale.x, sin * self.scale.x, 0.0),
Vec3::new(-sin * self.scale.y, cos * self.scale.y, 0.0),
Vec3::new(self.position.x, self.position.y, 1.0),
)
}
#[must_use]
#[inline]
pub fn apply_to_point(&self, point: Vec2) -> Vec3 {
let (sin, cos) = self.rotation.sin_cos();
let x = cos * self.scale.x * point.x - sin * self.scale.y * point.y + self.position.x;
let y = sin * self.scale.x * point.x + cos * self.scale.y * point.y + self.position.y;
Vec3::new(x, y, 1.0)
}
#[must_use]
#[inline]
pub fn inverse_matrix(&self) -> Mat3 {
self.to_matrix().inverse()
}
#[must_use]
#[inline]
pub fn compose(&self, other: &Transform2D) -> Transform2D {
let (sin, cos) = other.rotation.sin_cos();
let rotated_pos = Vec2::new(
cos * self.position.x * other.scale.x - sin * self.position.y * other.scale.y,
sin * self.position.x * other.scale.x + cos * self.position.y * other.scale.y,
);
Transform2D {
position: rotated_pos + other.position,
rotation: self.rotation + other.rotation,
scale: self.scale * other.scale,
}
}
}
impl Default for Transform2D {
fn default() -> Self {
Self::IDENTITY
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Transform3D {
pub position: Vec3,
pub rotation: Quat,
pub scale: Vec3,
}
impl Transform3D {
pub const IDENTITY: Self = Self {
position: Vec3::ZERO,
rotation: Quat::IDENTITY,
scale: Vec3::ONE,
};
#[must_use]
pub fn new(position: Vec3, rotation: Quat, scale: Vec3) -> Self {
Self {
position,
rotation,
scale,
}
}
#[must_use]
#[inline]
pub fn to_matrix(&self) -> Mat4 {
Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.position)
}
#[must_use]
#[inline]
pub fn apply_to_point(&self, point: Vec3) -> Vec3 {
self.rotation * (self.scale * point) + self.position
}
#[must_use]
#[inline]
pub fn inverse_matrix(&self) -> Mat4 {
self.to_matrix().inverse()
}
#[must_use]
#[inline]
pub fn compose(&self, other: &Transform3D) -> Transform3D {
Transform3D {
position: other.rotation * (other.scale * self.position) + other.position,
rotation: other.rotation * self.rotation,
scale: other.scale * self.scale,
}
}
}
impl Default for Transform3D {
fn default() -> Self {
Self::IDENTITY
}
}
#[must_use]
#[inline]
pub fn slerp(a: Quat, b: Quat, t: f32) -> Quat {
a.slerp(b, t)
}
#[must_use]
#[inline]
pub fn transform3d_lerp(a: &Transform3D, b: &Transform3D, t: f32) -> Transform3D {
Transform3D {
position: lerp_vec3(a.position, b.position, t),
rotation: a.rotation.slerp(b.rotation, t),
scale: lerp_vec3(a.scale, b.scale, t),
}
}
#[must_use]
#[inline]
pub fn flip_handedness_z(mat: Mat4) -> Mat4 {
let cols = mat.to_cols_array_2d();
Mat4::from_cols_array_2d(&[
cols[0],
cols[1],
[-cols[2][0], -cols[2][1], -cols[2][2], -cols[2][3]],
cols[3],
])
}
#[must_use]
#[inline]
pub fn projection_orthographic(
left: f32,
right: f32,
bottom: f32,
top: f32,
near: f32,
far: f32,
) -> Mat4 {
Mat4::orthographic_rh_gl(left, right, bottom, top, near, far)
}
#[must_use]
#[inline]
pub fn projection_perspective(fov_y_radians: f32, aspect: f32, near: f32, far: f32) -> Mat4 {
Mat4::perspective_rh_gl(fov_y_radians, aspect, near, far)
}
#[must_use]
#[inline]
pub fn lerp_f32(a: f32, b: f32, t: f32) -> f32 {
a + (b - a) * t
}
#[must_use]
#[inline]
pub fn lerp_vec3(a: Vec3, b: Vec3, t: f32) -> Vec3 {
a + (b - a) * t
}
#[must_use]
#[inline]
pub fn inverse_lerp(a: f32, b: f32, value: f32) -> f32 {
let denom = b - a;
if denom.abs() < crate::EPSILON_F32 {
0.0
} else {
(value - a) / denom
}
}
#[must_use]
#[inline]
pub fn remap(value: f32, in_lo: f32, in_hi: f32, out_lo: f32, out_hi: f32) -> f32 {
let t = inverse_lerp(in_lo, in_hi, value);
lerp_f32(out_lo, out_hi, t)
}
#[must_use]
#[inline]
pub fn projection_perspective_reverse_z(fov_y_radians: f32, aspect: f32, near: f32) -> Mat4 {
let f = 1.0 / (fov_y_radians * 0.5).tan();
Mat4::from_cols(
Vec4::new(f / aspect, 0.0, 0.0, 0.0),
Vec4::new(0.0, f, 0.0, 0.0),
Vec4::new(0.0, 0.0, 0.0, -1.0),
Vec4::new(0.0, 0.0, near, 0.0),
)
}