use bevy_math::{Vec3, Vec3A};
use bevy_reflect::Reflect;
pub use rays::*;
#[derive(Debug, Clone, Reflect)]
pub struct IntersectionData {
position: Vec3,
normal: Vec3,
barycentric_coord: Vec3,
distance: f32,
triangle: Option<[Vec3A; 3]>,
triangle_index: Option<usize>,
}
impl From<rays::PrimitiveIntersection> for IntersectionData {
fn from(data: rays::PrimitiveIntersection) -> Self {
Self {
position: data.position(),
normal: data.normal(),
distance: data.distance(),
barycentric_coord: Vec3::ZERO,
triangle: None,
triangle_index: None,
}
}
}
impl IntersectionData {
pub fn new(
position: Vec3,
normal: Vec3,
barycentric: Vec3,
distance: f32,
triangle: Option<[Vec3A; 3]>,
triangle_index: Option<usize>,
) -> Self {
Self {
position,
normal,
barycentric_coord: barycentric,
distance,
triangle,
triangle_index,
}
}
#[must_use]
pub fn position(&self) -> Vec3 {
self.position
}
#[must_use]
pub fn normal(&self) -> Vec3 {
self.normal
}
#[must_use]
pub fn barycentric_coord(&self) -> Vec3 {
self.barycentric_coord
}
#[must_use]
pub fn distance(&self) -> f32 {
self.distance
}
#[must_use]
pub fn triangle(&self) -> Option<[Vec3A; 3]> {
self.triangle
}
#[must_use]
pub fn triangle_index(&self) -> Option<usize> {
self.triangle_index
}
}
pub mod rays {
use bevy_math::{prelude::*, Ray3d, Vec3A};
use bevy_render::{camera::Camera, primitives::Aabb};
use bevy_transform::components::GlobalTransform;
use bevy_window::Window;
pub struct PrimitiveIntersection {
position: Vec3,
normal: Vec3,
distance: f32,
}
impl PrimitiveIntersection {
pub fn new(position: Vec3, normal: Vec3, distance: f32) -> Self {
Self {
position,
normal,
distance,
}
}
#[must_use]
pub fn position(&self) -> Vec3 {
self.position
}
#[must_use]
pub fn normal(&self) -> Vec3 {
self.normal
}
#[must_use]
pub fn distance(&self) -> f32 {
self.distance
}
}
pub fn to_transform(ray: Ray3d) -> Mat4 {
to_aligned_transform(ray, [0., 1., 0.].into())
}
pub fn to_aligned_transform(ray: Ray3d, up: Vec3) -> Mat4 {
let position = ray.origin;
let normal = ray.direction;
let new_rotation = Quat::from_rotation_arc(up, *normal);
Mat4::from_rotation_translation(new_rotation, position)
}
pub fn ray_from_transform(transform: Mat4) -> Ray3d {
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 ray_from_screenspace(
cursor_pos_screen: Vec2,
camera: &Camera,
camera_transform: &GlobalTransform,
window: &Window,
) -> Option<Ray3d> {
let mut viewport_pos = cursor_pos_screen;
if let Some(viewport) = &camera.viewport {
viewport_pos -= viewport.physical_position.as_vec2() / window.scale_factor();
}
camera
.viewport_to_world(camera_transform, viewport_pos)
.map(Ray3d::from)
}
pub fn intersects_aabb(ray: Ray3d, 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(*ray.direction).into();
let ray_origin: Vec3A = world_to_model.transform_point3(ray.origin).into();
let t_0: Vec3A = (aabb.min() - ray_origin) / ray_dir;
let t_1: Vec3A = (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])
}
}