use super::{Plane, Projection};
use rand::Rng;
use serde::{Deserialize, Serialize};
use tracing::warn;
use vek::*;
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(into = "SerdeDir")]
#[serde(from = "SerdeDir")]
pub struct Dir(Vec3<f32>);
impl Default for Dir {
fn default() -> Self { Self::forward() }
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
struct SerdeDir(Vec3<f32>);
impl From<SerdeDir> for Dir {
fn from(dir: SerdeDir) -> Self {
let dir = dir.0;
if dir.map(f32::is_nan).reduce_or() {
warn!(
?dir,
"Deserialized dir containing NaNs, replacing with default"
);
Default::default()
} else if !dir.is_normalized() {
warn!(
?dir,
"Deserialized unnormalized dir, replacing with default"
);
Default::default()
} else {
Self(dir)
}
}
}
impl From<Dir> for SerdeDir {
fn from(other: Dir) -> SerdeDir { SerdeDir(*other) }
}
impl Dir {
pub fn new(dir: Vec3<f32>) -> Self {
debug_assert!(!dir.map(f32::is_nan).reduce_or());
debug_assert!(dir.is_normalized());
Self(dir)
}
pub fn from_unnormalized(dirs: Vec3<f32>) -> Option<Self> {
dirs.try_normalized().map(|dir| {
#[cfg(debug_assertions)]
{
if dir.map(f32::is_nan).reduce_or() {
panic!("{} => {}", dirs, dir);
}
}
Self(dir)
})
}
pub fn random_2d(rng: &mut impl Rng) -> Self {
Self::from_unnormalized(Vec3::new(
rng.gen_range(-1.0..=1.0),
rng.gen_range(-1.0..=1.0),
0.0,
))
.unwrap_or(Self::new(Vec3::unit_x()))
}
pub fn slerp(from: Self, to: Self, factor: f32) -> Self {
Self(slerp_normalized(from.0, to.0, factor))
}
#[must_use]
pub fn slerped_to(self, to: Self, factor: f32) -> Self {
Self(slerp_normalized(self.0, to.0, factor))
}
pub fn slerp_to_vec3(from: Self, to: Vec3<f32>, factor: f32) -> Self {
Self(slerp_to_unnormalized(from.0, to, factor).unwrap_or_else(|e| e))
}
pub fn rotation_between(&self, to: Self) -> Quaternion<f32> {
Quaternion::<f32>::rotation_from_to_3d(self.0, to.0)
}
pub fn rotation(&self) -> Quaternion<f32> { Self::default().rotation_between(*self) }
pub fn is_valid(&self) -> bool { !self.0.map(f32::is_nan).reduce_or() && self.is_normalized() }
pub fn up() -> Self { Dir::new(Vec3::<f32>::unit_z()) }
pub fn down() -> Self { -Dir::new(Vec3::<f32>::unit_z()) }
pub fn left() -> Self { -Dir::new(Vec3::<f32>::unit_x()) }
pub fn right() -> Self { Dir::new(Vec3::<f32>::unit_x()) }
pub fn forward() -> Self { Dir::new(Vec3::<f32>::unit_y()) }
pub fn back() -> Self { -Dir::new(Vec3::<f32>::unit_y()) }
pub fn to_horizontal(self) -> Option<Self> { Self::from_unnormalized(self.xy().into()) }
pub fn vec(&self) -> &Vec3<f32> { &self.0 }
pub fn to_vec(self) -> Vec3<f32> { self.0 }
}
impl std::ops::Deref for Dir {
type Target = Vec3<f32>;
fn deref(&self) -> &Vec3<f32> { &self.0 }
}
impl From<Dir> for Vec3<f32> {
fn from(dir: Dir) -> Self { *dir }
}
impl Projection<Plane> for Dir {
type Output = Option<Self>;
fn projected(self, plane: &Plane) -> Self::Output {
Dir::from_unnormalized(plane.projection(*self))
}
}
impl Projection<Dir> for Vec3<f32> {
type Output = Vec3<f32>;
fn projected(self, dir: &Dir) -> Self::Output {
let dir = **dir;
self.dot(dir) * dir
}
}
impl std::ops::Mul<Dir> for Quaternion<f32> {
type Output = Dir;
fn mul(self, dir: Dir) -> Self::Output { Dir((self * *dir).normalized()) }
}
impl std::ops::Neg for Dir {
type Output = Dir;
fn neg(self) -> Dir { Dir::new(-self.0) }
}
#[inline(always)]
fn slerp_normalized(from: Vec3<f32>, to: Vec3<f32>, factor: f32) -> Vec3<f32> {
debug_assert!(!to.map(f32::is_nan).reduce_or());
debug_assert!(!from.map(f32::is_nan).reduce_or());
#[cfg(debug_assertions)]
{
let unnormalized = {
let len_sq = from.magnitude_squared();
!(0.999..=1.001).contains(&len_sq)
};
if unnormalized {
panic!("Called slerp_normalized with unnormalized `from`: {}", from);
}
}
#[cfg(debug_assertions)]
{
let unnormalized = {
let len_sq = from.magnitude_squared();
!(0.999..=1.001).contains(&len_sq)
};
if unnormalized {
panic!("Called slerp_normalized with unnormalized `to`: {}", to);
}
}
let dot = from.dot(to);
if dot >= 1.0 - 1E-6 {
return to;
}
let (from, to, factor) = if dot < -0.999 {
let mid_dir = if from.z.abs() > 0.999 {
Vec3::unit_x()
} else {
Vec3::new(from.y, -from.x, 0.0).normalized()
};
if factor > 0.5 {
(mid_dir, to, factor * 2.0 - 1.0)
} else {
(from, mid_dir, factor * 2.0)
}
} else {
(from, to, factor)
};
let slerped = Vec3::slerp(from, to, factor);
let slerped_normalized = slerped.normalized();
#[cfg(debug_assertions)]
{
if !slerped_normalized.is_normalized() || slerped_normalized.map(f32::is_nan).reduce_or() {
panic!(
"Failed to normalize {:?} produced from:\nslerp(\n {:?},\n {:?},\n \
{:?},\n)\nWith result: {:?})",
slerped, from, to, factor, slerped_normalized
);
}
}
slerped_normalized
}
fn slerp_to_unnormalized(
from: Vec3<f32>,
to: Vec3<f32>,
factor: f32,
) -> Result<Vec3<f32>, Vec3<f32>> {
to.try_normalized()
.map(|to| slerp_normalized(from, to, factor))
.ok_or(from)
}