psychopath/src/bbox4.rs

137 lines
4.0 KiB
Rust

#![allow(dead_code)]
use std;
use std::ops::{BitOr, BitOrAssign};
use crate::{
bbox::BBox,
lerp::{lerp, Lerp},
math::{Point, Vector},
};
use glam::{Vec4, Vec4Mask};
const BBOX_MAXT_ADJUST: f32 = 1.000_000_24;
/// A SIMD set of 4 3D axis-aligned bounding boxes.
#[derive(Debug, Copy, Clone)]
pub struct BBox4 {
pub x: (Vec4, Vec4), // (min, max)
pub y: (Vec4, Vec4), // (min, max)
pub z: (Vec4, Vec4), // (min, max)
}
impl BBox4 {
/// Creates a degenerate BBox with +infinity min and -infinity max.
pub fn new() -> BBox4 {
BBox4 {
x: (
Vec4::splat(std::f32::INFINITY),
Vec4::splat(std::f32::NEG_INFINITY),
),
y: (
Vec4::splat(std::f32::INFINITY),
Vec4::splat(std::f32::NEG_INFINITY),
),
z: (
Vec4::splat(std::f32::INFINITY),
Vec4::splat(std::f32::NEG_INFINITY),
),
}
}
/// Creates a BBox with min as the minimum extent and max as the maximum
/// extent.
pub fn from_bboxes(b1: BBox, b2: BBox, b3: BBox, b4: BBox) -> BBox4 {
BBox4 {
x: (
Vec4::new(b1.min.x(), b2.min.x(), b3.min.x(), b4.min.x()),
Vec4::new(b1.max.x(), b2.max.x(), b3.max.x(), b4.max.x()),
),
y: (
Vec4::new(b1.min.y(), b2.min.y(), b3.min.y(), b4.min.y()),
Vec4::new(b1.max.y(), b2.max.y(), b3.max.y(), b4.max.y()),
),
z: (
Vec4::new(b1.min.z(), b2.min.z(), b3.min.z(), b4.min.z()),
Vec4::new(b1.max.z(), b2.max.z(), b3.max.z(), b4.max.z()),
),
}
}
// Returns whether the given ray intersects with the bboxes.
pub fn intersect_ray(&self, orig: Point, dir_inv: Vector, max_t: f32) -> Vec4Mask {
// Get the ray data into SIMD format.
let ro_x = Vec4::splat(orig.co.x());
let ro_y = Vec4::splat(orig.co.y());
let ro_z = Vec4::splat(orig.co.z());
let rdi_x = Vec4::splat(dir_inv.co.x());
let rdi_y = Vec4::splat(dir_inv.co.y());
let rdi_z = Vec4::splat(dir_inv.co.z());
let max_t = Vec4::splat(max_t);
// Slab tests
let t1_x = (self.x.0 - ro_x) * rdi_x;
let t1_y = (self.y.0 - ro_y) * rdi_y;
let t1_z = (self.z.0 - ro_z) * rdi_z;
let t2_x = (self.x.1 - ro_x) * rdi_x;
let t2_y = (self.y.1 - ro_y) * rdi_y;
let t2_z = (self.z.1 - ro_z) * rdi_z;
// Get the far and near t hits for each axis.
let t_far_x = t1_x.max(t2_x);
let t_far_y = t1_y.max(t2_y);
let t_far_z = t1_z.max(t2_z);
let t_near_x = t1_x.min(t2_x);
let t_near_y = t1_y.min(t2_y);
let t_near_z = t1_z.min(t2_z);
// Calculate over-all far t hit.
let far_t = (t_far_x.min(t_far_y.min(t_far_z)) * Vec4::splat(BBOX_MAXT_ADJUST)).min(max_t);
// Calculate over-all near t hit.
let near_t = t_near_x.max(t_near_y).max(t_near_z.max(Vec4::splat(0.0)));
// Hit results
near_t.cmplt(far_t)
}
}
/// Union of two BBoxes.
impl BitOr for BBox4 {
type Output = BBox4;
fn bitor(self, rhs: BBox4) -> BBox4 {
BBox4 {
x: (self.x.0.min(rhs.x.0), self.x.1.max(rhs.x.1)),
y: (self.y.0.min(rhs.y.0), self.y.1.max(rhs.y.1)),
z: (self.z.0.min(rhs.z.0), self.z.1.max(rhs.z.1)),
}
}
}
impl BitOrAssign for BBox4 {
fn bitor_assign(&mut self, rhs: BBox4) {
*self = *self | rhs;
}
}
impl Lerp for BBox4 {
fn lerp(self, other: BBox4, alpha: f32) -> BBox4 {
BBox4 {
x: (
lerp(self.x.0, other.x.0, alpha),
lerp(self.x.1, other.x.1, alpha),
),
y: (
lerp(self.y.0, other.y.0, alpha),
lerp(self.y.1, other.y.1, alpha),
),
z: (
lerp(self.z.0, other.z.0, alpha),
lerp(self.z.1, other.z.1, alpha),
),
}
}
}