137 lines
4.0 KiB
Rust
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::{BVec4A, Vec4};
|
|
|
|
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) -> BVec4A {
|
|
// Get the ray data into SIMD format.
|
|
let ro_x = Vec4::splat(orig.co[0]);
|
|
let ro_y = Vec4::splat(orig.co[1]);
|
|
let ro_z = Vec4::splat(orig.co[2]);
|
|
let rdi_x = Vec4::splat(dir_inv.co[0]);
|
|
let rdi_y = Vec4::splat(dir_inv.co[1]);
|
|
let rdi_z = Vec4::splat(dir_inv.co[2]);
|
|
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),
|
|
),
|
|
}
|
|
}
|
|
}
|