-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Raycasting #615
Raycasting #615
Changes from all commits
0f02b01
dd99c79
1f31d00
dcce5e7
be23d74
5fb710a
29f5d50
0b93d4b
bfe5690
f771a90
22e62f7
3a0b376
7bed280
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
[package] | ||
name = "bevy_physics" | ||
version = "0.3.0" | ||
edition = "2018" | ||
authors = [ | ||
"Bevy Contributors <bevyengine@gmail.com>", | ||
"Carter Anderson <mcanders1@gmail.com>", | ||
] | ||
description = "Provides physics functionality for Bevy Engine" | ||
homepage = "https://bevyengine.org" | ||
repository = "https://github.com/bevyengine/bevy" | ||
license = "MIT" | ||
keywords = ["bevy"] | ||
|
||
[dependencies] | ||
# bevy | ||
bevy_transform = { path = "../bevy_transform", version = "0.3.0" } | ||
bevy_render = { path = "../bevy_render", version = "0.3.0" } | ||
bevy_window = { path = "../bevy_window", version = "0.3.0" } | ||
|
||
# linear algebra | ||
glam = { version = "0.11.2", features = ["serde"] } | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
use glam::Vec3; | ||
|
||
pub struct Plane { | ||
center: Vec3, | ||
normal: Vec3, | ||
} | ||
|
||
impl Plane { | ||
pub fn new(center: Vec3, normal: Vec3) -> Self { | ||
normal.normalize(); | ||
Self { center, normal } | ||
} | ||
|
||
pub fn center(&self) -> &Vec3 { | ||
&self.center | ||
} | ||
|
||
pub fn center_mut(&mut self) -> &mut Vec3 { | ||
&mut self.center | ||
} | ||
|
||
pub fn normal(&self) -> &Vec3 { | ||
&self.normal | ||
} | ||
|
||
pub fn set_normal(&mut self, normal: Vec3) { | ||
normal.normalize(); | ||
self.normal = normal; | ||
} | ||
} | ||
|
||
impl Default for Plane { | ||
fn default() -> Self { | ||
Plane { | ||
center: Vec3::zero(), | ||
normal: Vec3::unit_y(), | ||
} | ||
} | ||
} | ||
|
||
pub struct Sphere { | ||
pub center: Vec3, | ||
pub radius: f32, | ||
} | ||
|
||
impl Default for Sphere { | ||
fn default() -> Self { | ||
Sphere { | ||
center: Vec3::zero(), | ||
radius: 1.0, | ||
} | ||
} | ||
} | ||
|
||
pub struct Triangle(pub Vec3, pub Vec3, pub Vec3); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
use super::{geometry::*, ray::*}; | ||
use glam::Vec3; | ||
|
||
pub struct RayHit { | ||
distance: f32, | ||
point: Vec3, | ||
} | ||
|
||
impl RayHit { | ||
pub fn new(distance: f32, point: Vec3) -> Self { | ||
Self { distance, point } | ||
} | ||
|
||
pub fn distance(&self) -> &f32 { | ||
&self.distance | ||
} | ||
|
||
pub fn point(&self) -> &Vec3 { | ||
&self.point | ||
} | ||
} | ||
|
||
pub trait RayIntersector { | ||
fn intersect_ray(&self, ray: &Ray) -> Option<RayHit>; | ||
} | ||
|
||
impl RayIntersector for Plane { | ||
fn intersect_ray(&self, ray: &Ray) -> Option<RayHit> { | ||
let denominator = self.normal().dot(*ray.direction()); | ||
if denominator.abs() > f32::EPSILON { | ||
let distance = (*self.center() - *ray.origin()).dot(*self.normal()) / denominator; | ||
if distance > 0.0 { | ||
return Some(RayHit::new( | ||
distance, | ||
*ray.origin() + *ray.direction() * distance, | ||
)); | ||
} | ||
} | ||
|
||
None | ||
} | ||
} | ||
|
||
impl RayIntersector for Sphere { | ||
fn intersect_ray(&self, ray: &Ray) -> Option<RayHit> { | ||
let oc = *ray.origin() - self.center; | ||
let a = ray.direction().length_squared(); | ||
let b = 2.0 * oc.dot(*ray.direction()); | ||
let c = oc.length_squared() - self.radius.powi(2); | ||
|
||
let d = b.powi(2) - 4.0 * a * c; | ||
|
||
if d < 0.0 { | ||
None | ||
} else { | ||
let distance = (-b - d.sqrt()) / (2.0 * a); | ||
|
||
Some(RayHit::new( | ||
distance, | ||
*ray.origin() + *ray.direction() * distance, | ||
)) | ||
} | ||
} | ||
} | ||
|
||
impl RayIntersector for Triangle { | ||
// using the Moeller-Trumbore intersection algorithm | ||
// Can anyone think of sensible names for theese? | ||
#[allow(clippy::many_single_char_names)] | ||
fn intersect_ray(&self, ray: &Ray) -> Option<RayHit> { | ||
let edges = (self.1 - self.0, self.2 - self.0); | ||
let h = ray.direction().cross(edges.1); | ||
let a = edges.0.dot(h); | ||
|
||
if a > -f32::EPSILON && a < f32::EPSILON { | ||
return None; | ||
} | ||
|
||
let f = 1.0 / a; | ||
let s = *ray.origin() - self.0; | ||
let u = f * s.dot(h); | ||
|
||
if !(0.0..=1.0).contains(&u) { | ||
return None; | ||
} | ||
|
||
let q = s.cross(edges.0); | ||
let v = f * ray.direction().dot(q); | ||
|
||
if v < 0.0 || u + v > 1.0 { | ||
return None; | ||
} | ||
|
||
let distance = f * edges.1.dot(q); | ||
|
||
if distance > f32::EPSILON { | ||
Some(RayHit::new( | ||
distance, | ||
*ray.origin() + *ray.direction() * distance, | ||
)) | ||
} else { | ||
None | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
pub mod geometry; | ||
pub mod intersectors; | ||
pub mod ray; | ||
|
||
pub mod prelude { | ||
pub use super::{geometry::*, intersectors::*, ray::*}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
use bevy_render::camera::Camera; | ||
use bevy_transform::components::GlobalTransform; | ||
use bevy_window::Window; | ||
use glam::{Vec2, Vec3}; | ||
|
||
pub struct Ray { | ||
origin: Vec3, | ||
direction: Vec3, | ||
} | ||
|
||
impl Ray { | ||
pub fn new(origin: Vec3, direction: Vec3) -> Self { | ||
direction.normalize(); | ||
Self { origin, direction } | ||
} | ||
|
||
pub fn from_window( | ||
window: &Window, | ||
camera: &Camera, | ||
camera_transform: &GlobalTransform, | ||
) -> Self { | ||
Self::from_mouse_position( | ||
&window.cursor_position().unwrap(), | ||
window, | ||
camera, | ||
camera_transform, | ||
) | ||
} | ||
|
||
pub fn from_mouse_position( | ||
mouse_position: &Vec2, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Window now has pub fn from_window(
window: &Window,
camera: &Camera,
camera_transform: &GlobalTransform,
) -> Self {
Self::from_mouse_position(window.cursor_position(), camera, camera_transform)
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice. I'll add it. |
||
window: &Window, | ||
camera: &Camera, | ||
camera_transform: &GlobalTransform, | ||
) -> Self { | ||
if window.id() != camera.window { | ||
panic!("Generating Ray from Camera with wrong Window"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct me if i'm wrong, this can terminate the program? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It can, you're right. This is by design. Passing a non-matching window and camera to from_mouse_position(..) would result in undefined behavior and should be checked before calling the method. Do you think a Log Error would be more sensible? I didn't familiarize myself very much with the Diagnostics Plugin, so maybe that could help. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TBH I don't know the right answer, I am learning here 👍 |
||
} | ||
|
||
let x = 2.0 * (mouse_position.x / window.width() as f32) - 1.0; | ||
let y = 2.0 * (mouse_position.y / window.height() as f32) - 1.0; | ||
|
||
let camera_inverse_matrix = | ||
camera_transform.compute_matrix() * camera.projection_matrix.inverse(); | ||
let near = camera_inverse_matrix * Vec3::new(x, y, 0.0).extend(1.0); | ||
let far = camera_inverse_matrix * Vec3::new(x, y, 1.0).extend(1.0); | ||
|
||
let near = near.truncate() / near.w; | ||
let far = far.truncate() / far.w; | ||
|
||
let direction: Vec3 = far - near; | ||
let origin: Vec3 = near; | ||
|
||
Self { origin, direction } | ||
} | ||
|
||
pub fn origin(&self) -> &Vec3 { | ||
&self.origin | ||
} | ||
|
||
pub fn origin_mut(&mut self) -> &mut Vec3 { | ||
&mut self.origin | ||
} | ||
|
||
pub fn direction(&self) -> &Vec3 { | ||
&self.direction | ||
} | ||
|
||
pub fn set_direction(&mut self, direction: Vec3) { | ||
direction.normalize(); | ||
self.direction = direction; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub mod d3; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this allow acceptable?