pub use crate::ffi::{Matrix, Quaternion, Vector2, Vector3, Vector4};
use crate::ffi;
use crate::misc::AsF32;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
macro_rules! optional_serde_struct {
($def:item) => {
#[repr(C)]
#[derive(Default, Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
$def
};
}
#[inline]
#[must_use]
pub const fn lerp(v0: f32, v1: f32, amount: f32) -> f32 {
v0 + amount * (v1 - v0)
}
#[inline]
#[must_use]
pub fn rquat<T1: AsF32, T2: AsF32, T3: AsF32, T4: AsF32>(x: T1, y: T2, z: T3, w: T4) -> Quaternion {
Quaternion::new(x.as_f32(), y.as_f32(), z.as_f32(), w.as_f32())
}
optional_serde_struct! {
pub struct Ray {
pub position: Vector3,
pub direction: Vector3,
}
}
impl From<ffi::Ray> for Ray {
fn from(r: ffi::Ray) -> Ray {
unsafe { std::mem::transmute(r) }
}
}
impl From<Ray> for ffi::Ray {
fn from(v: Ray) -> ffi::Ray {
unsafe { std::mem::transmute(v) }
}
}
impl From<&Ray> for ffi::Ray {
fn from(v: &Ray) -> ffi::Ray {
unsafe { std::mem::transmute(*v) }
}
}
impl Ray {
#[must_use]
#[inline]
pub const fn new(position: Vector3, direction: Vector3) -> Self {
Self {
position,
direction,
}
}
}
pub type Rectangle = ffi::Rectangle;
optional_serde_struct! {
pub struct BoundingBox {
pub min: Vector3,
pub max: Vector3,
}
}
impl BoundingBox {
#[must_use]
#[inline]
pub fn new(min: Vector3, max: Vector3) -> BoundingBox {
BoundingBox { min, max }
}
}
impl From<ffi::BoundingBox> for BoundingBox {
fn from(r: ffi::BoundingBox) -> BoundingBox {
unsafe { std::mem::transmute(r) }
}
}
impl From<BoundingBox> for ffi::BoundingBox {
fn from(v: BoundingBox) -> ffi::BoundingBox {
unsafe { std::mem::transmute(v) }
}
}
impl From<&BoundingBox> for ffi::BoundingBox {
fn from(v: &BoundingBox) -> ffi::BoundingBox {
unsafe { std::mem::transmute(*v) }
}
}
impl BoundingBox {
#[inline]
#[must_use]
pub fn check_collision_boxes(&self, box2: BoundingBox) -> bool {
unsafe { ffi::CheckCollisionBoxes(self.into(), box2.into()) }
}
#[inline]
#[must_use]
pub fn check_collision_box_sphere(
&self,
center_sphere: impl Into<Vector3>,
radius_sphere: f32,
) -> bool {
unsafe { ffi::CheckCollisionBoxSphere(self.into(), center_sphere.into(), radius_sphere) }
}
#[inline]
#[must_use]
pub fn get_ray_collision_box(&self, ray: Ray) -> RayCollision {
unsafe { ffi::GetRayCollisionBox(ray.into(), self.into()).into() }
}
}
optional_serde_struct! {
pub struct RayCollision {
pub hit: bool,
pub distance: f32,
pub point: Vector3,
pub normal: Vector3,
}
}
impl From<ffi::RayCollision> for RayCollision {
fn from(r: ffi::RayCollision) -> RayCollision {
unsafe { std::mem::transmute(r) }
}
}
impl From<RayCollision> for ffi::RayCollision {
fn from(v: RayCollision) -> ffi::RayCollision {
unsafe { std::mem::transmute(v) }
}
}
impl From<&RayCollision> for ffi::RayCollision {
fn from(v: &RayCollision) -> ffi::RayCollision {
unsafe { std::mem::transmute(*v) }
}
}
optional_serde_struct! {
pub struct Transform {
pub translation: Vector3,
pub rotation: Quaternion,
pub scale: Vector3,
}
}
impl From<ffi::Transform> for Transform {
fn from(r: ffi::Transform) -> Transform {
unsafe { std::mem::transmute(r) }
}
}
impl From<Transform> for ffi::Transform {
fn from(v: Transform) -> ffi::Transform {
unsafe { std::mem::transmute(v) }
}
}
impl From<&Transform> for ffi::Transform {
fn from(v: &Transform) -> ffi::Transform {
unsafe { std::mem::transmute(*v) }
}
}
#[cfg(test)]
mod math_test {
use super::{Ray, Vector2, Vector3, Vector4};
use crate::{ffi, math::Matrix};
#[test]
fn test_into() {
let v2: ffi::Vector2 = Vector2 { x: 1.0, y: 2.0 };
assert!(v2.x == 1.0 && v2.y == 2.0, "bad memory transmutation");
let v3: ffi::Vector3 = Vector3 {
x: 1.0,
y: 2.0,
z: 3.0,
};
assert!(
v3.x == 1.0 && v3.y == 2.0 && v3.z == 3.0,
"bad memory transmutation"
);
let v4: ffi::Vector4 = Vector4::new(1.0, 2.0, 3.0, 4.0);
assert!(
v4.x == 1.0 && v4.y == 2.0 && v4.z == 3.0 && v4.w == 4.0,
"bad memory transmutation"
);
let r: ffi::Ray = (Ray {
position: v3,
direction: Vector3 {
x: 3.0,
y: 2.0,
z: 1.0,
},
})
.into();
assert!(
r.position.x == 1.0
&& r.position.y == 2.0
&& r.position.z == 3.0
&& r.direction.x == 3.0
&& r.direction.y == 2.0
&& r.direction.z == 1.0,
"bad memory transmutation"
);
let identity_mat: ffi::Matrix = Matrix::identity();
assert!(
identity_mat.m0 == 1.0
&& identity_mat.m5 == 1.0
&& identity_mat.m10 == 1.0
&& identity_mat.m15 == 1.0
&& identity_mat.m1 == 0.0
&& identity_mat.m4 == 0.0,
"identity matrix should be 1.0 on the diagonal, 0.0 elsewhere"
);
}
}