use crate::map::DispInfo;
use crate::utils::Vec2d;
use super::*;
pub enum TriangleTag {
Unwalkable = 0,
Steep = 1,
Flat = 9,
}
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Displacement {
pub width: usize,
pub plane: Plane,
pub bottom_right: Vector3<f32>,
pub normals: Vec2d<Vector3<f32>>,
pub distances: Vec2d<f32>,
pub alphas: Vec2d<f32>,
}
impl Displacement {
pub const fn new(corners: [Vector3<f32>; 4], width: usize) -> Self {
let [bl, tl, tr, br] = corners;
Self {
width,
plane: Plane::new(bl, tl, tr),
bottom_right: br,
normals: Vec2d::new(Vec2d::strides(width)),
distances: Vec2d::new(Vec2d::strides(width)),
alphas: Vec2d::new(Vec2d::strides(width)),
}
}
pub fn new_plane(plane: Plane, width: usize) -> Self {
let bottom_right = plane.bottom_right();
Self {
width,
plane,
bottom_right,
normals: Vec2d::new(Vec2d::strides(width)),
distances: Vec2d::new(Vec2d::strides(width)),
alphas: Vec2d::new(Vec2d::strides(width)),
}
}
#[inline]
pub const fn corners(&self) -> [&Vector3<f32>; 4] {
let Plane { bottom_left, top_left, top_right } = &self.plane;
let bottom_right = &self.bottom_right;
[bottom_left, top_left, top_right, bottom_right]
}
#[inline]
pub(crate) const fn power_to_len(power: u32) -> usize {
2_usize.pow(power) + 1
}
#[inline]
pub(crate) const fn len_to_power(size: usize) -> u32 {
match size {
5 => 2,
9 => 3,
17 => 4,
x => (x - 1).ilog2(), }
}
pub fn ideal_points(&self) -> Vec2d<Vector3<f32>> {
Self::lerp4(self.corners(), self.width)
}
fn lerp4(points: [&Vector3<f32>; 4], num_points: usize) -> Vec2d<Vector3<f32>> {
let [bl, tl, tr, br] = points;
let mut points = Vec2d::with_capacity(num_points * num_points, Vec2d::strides(num_points));
for y in 0..num_points {
for x in 0..num_points {
let max = (num_points - 1) as f32;
let t_x = x as f32 / max;
let top = tl.lerp(tr, t_x);
let bottom = bl.lerp(br, t_x);
let (top, bottom) = (bottom, top);
let t_y = y as f32 / max;
let point = top.lerp(&bottom, t_y);
points.inner.push(point);
}
}
points
}
#[inline]
fn offsets(&self) -> Vec2d<Vector3<f32>> {
Vec2d::from_parts(vec![Vector3::origin(); self.width * self.width], [self.width, 1])
}
#[inline]
fn offset_normals(&self) -> Vec2d<Vector3<f32>> {
Vec2d::from_parts(vec![self.plane.normal(); self.width * self.width], [self.width, 1])
}
fn triangle_tags(&self) -> Vec2d<i32> {
let height = self.width - 1;
let width = height * 2;
let total = width * height;
let value = TriangleTag::Unwalkable as i32;
Vec2d::from_parts(vec![value; total], [width, 1])
}
#[inline]
const fn allowed_verts() -> [i32; 10] {
[-1; 10]
}
pub fn into_disp_info(self) -> DispInfo {
DispInfo {
offsets: self.offsets(),
offset_normals: self.offset_normals(),
triangle_tags: self.triangle_tags(),
allowed_verts: Self::allowed_verts(),
power: Displacement::len_to_power(self.width),
start_position: self.plane.bottom_left,
flags: DispInfo::NO_FLAGS,
elevation: 0.0,
is_subdiv: false, normals: self.normals,
distances: self.distances,
alphas: self.alphas,
}
}
pub fn from_disp_info(disp_info: DispInfo, plane: Plane) -> Self {
let len = Self::power_to_len(disp_info.power);
let bottom_right = plane.bottom_right();
let normals = disp_info.normals;
let distances = disp_info.distances;
let alphas = disp_info.alphas;
Self { width: len, plane, bottom_right, normals, distances, alphas }
}
}
pub(crate) fn lerp(a: f32, b: f32, t: f32) -> f32 {
const EPSILON: f32 = 1e-4;
if t < EPSILON {
return a;
}
if (t - 1.0).abs() < EPSILON {
return b;
}
(1.0 - t) * a + t * b
}
pub(crate) fn project_unit_cube_to_sphere(point: &Vector3<f32>) -> Vector3<f32> {
let Vector3 { x, y, z } = point;
let x_2 = x * x;
let y_2 = y * y;
let z_2 = z * z;
Vector3 {
x: x * f32::sqrt(1.0 - (y_2 / 2.0) - (z_2 / 2.0) + (y_2 * z_2) / 3.0),
y: y * f32::sqrt(1.0 - (z_2 / 2.0) - (x_2 / 2.0) + (z_2 * x_2) / 3.0),
z: z * f32::sqrt(1.0 - (x_2 / 2.0) - (y_2 / 2.0) + (x_2 * y_2) / 3.0),
}
}
impl Vector3<f32> {
pub fn lerp(&self, other: &Self, t: f32) -> Self {
Self {
x: lerp(self.x, other.x, t),
y: lerp(self.y, other.y, t),
z: lerp(self.z, other.z, t),
}
}
}
impl Vector3<f32> {
pub fn dir_and_dist(&self, other: &Self) -> (Vector3<f32>, f32) {
let mut dir = other.clone() - self;
let dist = dir.magnitude();
dir.normalize_mut();
(dir, dist)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ideal() {
let truth = Vec2d {
strides: [5, 1],
inner: vec![
Vector3::new(0.0, 0.0, 16.0),
Vector3::new(4.0, 0.0, 16.0),
Vector3::new(8.0, 0.0, 16.0),
Vector3::new(12.0, 0.0, 16.0),
Vector3::new(16.0, 0.0, 16.0),
Vector3::new(0.0, 0.0, 12.0),
Vector3::new(4.0, 0.0, 12.0),
Vector3::new(8.0, 0.0, 12.0),
Vector3::new(12.0, 0.0, 12.0),
Vector3::new(16.0, 0.0, 12.0),
Vector3::new(0.0, 0.0, 8.0),
Vector3::new(4.0, 0.0, 8.0),
Vector3::new(8.0, 0.0, 8.0),
Vector3::new(12.0, 0.0, 8.0),
Vector3::new(16.0, 0.0, 8.0),
Vector3::new(0.0, 0.0, 4.0),
Vector3::new(4.0, 0.0, 4.0),
Vector3::new(8.0, 0.0, 4.0),
Vector3::new(12.0, 0.0, 4.0),
Vector3::new(16.0, 0.0, 4.0),
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(4.0, 0.0, 0.0),
Vector3::new(8.0, 0.0, 0.0),
Vector3::new(12.0, 0.0, 0.0),
Vector3::new(16.0, 0.0, 0.0),
],
};
let input = Displacement {
width: 2,
plane: Plane::new(
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(0.0, 0.0, 16.0),
Vector3::new(16.0, 0.0, 16.0),
),
bottom_right: Vector3::new(16.0, 0.0, 0.0),
normals: Vec2d::new([0, 1]),
distances: Vec2d::new([0, 1]),
alphas: Vec2d::new([0, 1]),
};
let output = input.ideal_points();
assert_eq!(truth, output);
}
#[test]
fn normal_dist_from() {
let input1 = Vector3::new(1.0, 1.0, 1.0);
let input2 = Vector3::new(2.0, 3.0, 4.0);
let (dir, dist) = input1.dir_and_dist(&input2);
let delta = dir * dist;
assert_eq!(Vector3::new(1.0, 2.0, 3.0), delta);
let output2 = input1 + δ
assert_eq!(input2, output2);
}
}