use bevy::{math::Vec3A, prelude::*};
pub use rays::*;
#[non_exhaustive]
pub enum Primitive3d {
Plane { point: Vec3, normal: Vec3 },
}
#[derive(Debug, PartialEq, Copy, Clone, Component)]
pub struct Intersection {
position: Vec3,
normal: Vec3,
distance: f32,
triangle: Option<Triangle>,
}
impl Intersection {
pub fn new(
position: Vec3,
normal: Vec3,
pick_distance: f32,
triangle: Option<Triangle>,
) -> Self {
Intersection {
position,
normal,
distance: pick_distance,
triangle,
}
}
pub fn position(&self) -> Vec3 {
self.position
}
pub fn normal(&self) -> Vec3 {
self.normal
}
pub fn normal_ray(&self) -> Ray3d {
Ray3d::new(self.position, self.normal)
}
pub fn distance(&self) -> f32 {
self.distance
}
pub fn world_triangle(&self) -> Option<Triangle> {
self.triangle
}
}
pub mod rays {
use bevy::{
math::Vec3A,
prelude::*,
render::{camera::Camera, primitives::Aabb},
};
#[derive(Debug, PartialEq, Copy, Clone, Default)]
pub struct Ray3d {
pub(crate) origin: Vec3A,
pub(crate) direction: Vec3A,
}
impl Ray3d {
pub fn new(origin: Vec3, direction: Vec3) -> Self {
Ray3d {
origin: origin.into(),
direction: direction.normalize().into(),
}
}
pub fn origin(&self) -> Vec3 {
self.origin.into()
}
pub fn direction(&self) -> Vec3 {
self.direction.into()
}
pub fn position(&self, distance: f32) -> Vec3 {
(self.origin + self.direction * distance).into()
}
pub fn to_transform(self) -> Mat4 {
let position = self.origin();
let normal = self.direction();
let up = Vec3::from([0.0, 1.0, 0.0]);
let axis = up.cross(normal).normalize();
let angle = up.dot(normal).acos();
let epsilon = f32::EPSILON;
let new_rotation = if angle.abs() > epsilon {
Quat::from_axis_angle(axis, angle)
} else {
Quat::default()
};
Mat4::from_rotation_translation(new_rotation, position)
}
pub fn from_transform(transform: Mat4) -> Self {
let pick_position_ndc = Vec3::from([0.0, 0.0, -1.0]);
let pick_position = transform.project_point3(pick_position_ndc);
let (_, _, source_origin) = transform.to_scale_rotation_translation();
let ray_direction = pick_position - source_origin;
Ray3d::new(source_origin, ray_direction)
}
pub fn from_screenspace(
cursor_pos_screen: Vec2,
windows: &Res<Windows>,
camera: &Camera,
camera_transform: &GlobalTransform,
) -> Option<Self> {
let camera_position = camera_transform.compute_matrix();
let window = match windows.get(camera.window) {
Some(window) => window,
None => {
error!("WindowId {} does not exist", camera.window);
return None;
}
};
let screen_size = Vec2::from([window.width() as f32, window.height() as f32]);
let projection_matrix = camera.projection_matrix;
let cursor_ndc = (cursor_pos_screen / screen_size) * 2.0 - Vec2::from([1.0, 1.0]);
let cursor_pos_ndc_near: Vec3 = cursor_ndc.extend(-1.0);
let cursor_pos_ndc_far: Vec3 = cursor_ndc.extend(1.0);
let ndc_to_world: Mat4 = camera_position * projection_matrix.inverse();
let cursor_pos_near: Vec3 = ndc_to_world.project_point3(cursor_pos_ndc_near);
let cursor_pos_far: Vec3 = ndc_to_world.project_point3(cursor_pos_ndc_far);
let ray_direction = cursor_pos_far - cursor_pos_near;
Some(Ray3d::new(cursor_pos_near, ray_direction))
}
pub fn intersects_aabb(&self, aabb: &Aabb, model_to_world: &Mat4) -> Option<[f32; 2]> {
let world_to_model = model_to_world.inverse();
let ray_dir: Vec3A = world_to_model.transform_vector3(self.direction()).into();
let ray_origin: Vec3A = world_to_model.transform_point3(self.origin()).into();
let t_0: Vec3A = (Vec3A::from(aabb.min()) - ray_origin) / ray_dir;
let t_1: Vec3A = (Vec3A::from(aabb.max()) - ray_origin) / ray_dir;
let t_min: Vec3A = t_0.min(t_1);
let t_max: Vec3A = t_0.max(t_1);
let mut hit_near = t_min.x;
let mut hit_far = t_max.x;
if hit_near > t_max.y || t_min.y > hit_far {
return None;
}
if t_min.y > hit_near {
hit_near = t_min.y;
}
if t_max.y < hit_far {
hit_far = t_max.y;
}
if (hit_near > t_max.z) || (t_min.z > hit_far) {
return None;
}
if t_min.z > hit_near {
hit_near = t_min.z;
}
if t_max.z < hit_far {
hit_far = t_max.z;
}
Some([hit_near, hit_far])
}
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct Triangle {
pub v0: Vec3A,
pub v1: Vec3A,
pub v2: Vec3A,
}
impl From<(Vec3A, Vec3A, Vec3A)> for Triangle {
fn from(vertices: (Vec3A, Vec3A, Vec3A)) -> Self {
Triangle {
v0: vertices.0,
v1: vertices.1,
v2: vertices.2,
}
}
}
impl From<Vec<Vec3A>> for Triangle {
fn from(vertices: Vec<Vec3A>) -> Self {
Triangle {
v0: *vertices.get(0).unwrap(),
v1: *vertices.get(1).unwrap(),
v2: *vertices.get(2).unwrap(),
}
}
}
impl From<[Vec3A; 3]> for Triangle {
fn from(vertices: [Vec3A; 3]) -> Self {
Triangle {
v0: vertices[0],
v1: vertices[1],
v2: vertices[2],
}
}
}