Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ path = "examples/3d/texture.rs"
name = "z_sort_debug"
path = "examples/3d/z_sort_debug.rs"

[[example]]
name = "raycast"
path = "examples/physics/raycast.rs"

[[example]]
name = "custom_loop"
path = "examples/app/custom_loop.rs"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_internal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ bevy_input = { path = "../bevy_input", version = "0.3.0" }
bevy_log = { path = "../bevy_log", version = "0.3.0" }
bevy_math = { path = "../bevy_math", version = "0.3.0" }
bevy_reflect = { path = "../bevy_reflect", version = "0.3.0", features = ["bevy"] }
bevy_physics = { path = "../bevy_physics", version = "0.3.0" }
bevy_scene = { path = "../bevy_scene", version = "0.3.0" }
bevy_transform = { path = "../bevy_transform", version = "0.3.0" }
bevy_utils = { path = "../bevy_utils", version = "0.3.0" }
Expand Down
5 changes: 5 additions & 0 deletions crates/bevy_internal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ pub mod math {
pub use bevy_math::*;
}

pub mod physics {
//! colliders and raycasting
pub use bevy_physics::*;
}

pub mod reflect {
// TODO: remove these renames once TypeRegistryArc is no longer required
//! Type reflection used for dynamically interacting with rust types.
Expand Down
23 changes: 23 additions & 0 deletions crates/bevy_physics/Cargo.toml
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 <[email protected]>",
"Carter Anderson <[email protected]>",
]
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"] }

55 changes: 55 additions & 0 deletions crates/bevy_physics/src/d3/geometry.rs
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);
105 changes: 105 additions & 0 deletions crates/bevy_physics/src/d3/intersectors.rs
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)]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this allow acceptable?

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
}
}
}
7 changes: 7 additions & 0 deletions crates/bevy_physics/src/d3/mod.rs
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::*};
}
73 changes: 73 additions & 0 deletions crates/bevy_physics/src/d3/ray.rs
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,
Copy link
Member

@smokku smokku Dec 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Window now has .cursor_position() method. We could add:

pub fn from_window(
    window: &Window,
    camera: &Camera,
    camera_transform: &GlobalTransform,
) -> Self {
    Self::from_mouse_position(window.cursor_position(), camera, camera_transform)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct me if i'm wrong, this can terminate the program?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

Choose a reason for hiding this comment

The 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;
}
}
1 change: 1 addition & 0 deletions crates/bevy_physics/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod d3;
Loading