-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Define a basic set of Primitives (#10466)
# Objective - Implement a subset of https://github.com/bevyengine/rfcs/blob/main/rfcs/12-primitive-shapes.md#feature-name-primitive-shapes ## Solution - Define a very basic set of primitives in bevy_math - Assume a 0,0,0 origin for most shapes - Use radius and half extents to avoid unnecessary computational overhead wherever they get used - Provide both Boxed and const generics variants for shapes with variable sizes - Boxed is useful if a 3rd party crate wants to use something like enum-dispatch for all supported primitives - Const generics is useful when just working on a single primitive, as it causes no allocs #### Some discrepancies from the RFC: - Box was changed to Cuboid, because Box is already used for an alloc type - Skipped Cone because it's unclear where the origin should be for different uses - Skipped Wedge because it's too niche for an initial PR (we also don't implement Torus, Pyramid or a Death Star (there's an SDF for that!)) - Skipped Frustum because while it would be a useful math type, it's not really a common primitive - Skipped Triangle3d and Quad3d because those are just rotated 2D shapes ## Future steps - Add more primitives - Add helper methods to make primitives easier to construct (especially when half extents are involved) - Add methods to calculate AABBs for primitives (useful for physics, BVH construction, for the mesh AABBs, etc) - Add wrappers for common and cheap operations, like extruding 2D shapes and translating them - Use the primitives to generate meshes - Provide signed distance functions and gradients for primitives (maybe) --- ## Changelog - Added a collection of primitives to the bevy_math crate --------- Co-authored-by: Joona Aalto <[email protected]>
- Loading branch information
Showing
4 changed files
with
375 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
use super::Primitive2d; | ||
use crate::Vec2; | ||
|
||
/// A normalized vector pointing in a direction in 2D space | ||
#[derive(Clone, Copy, Debug)] | ||
pub struct Direction2d(Vec2); | ||
|
||
impl From<Vec2> for Direction2d { | ||
fn from(value: Vec2) -> Self { | ||
Self(value.normalize()) | ||
} | ||
} | ||
|
||
impl Direction2d { | ||
/// Create a direction from a [`Vec2`] that is already normalized | ||
pub fn from_normalized(value: Vec2) -> Self { | ||
debug_assert!(value.is_normalized()); | ||
Self(value) | ||
} | ||
} | ||
|
||
impl std::ops::Deref for Direction2d { | ||
type Target = Vec2; | ||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
|
||
/// A circle primitive | ||
#[derive(Clone, Copy, Debug)] | ||
pub struct Circle { | ||
/// The radius of the circle | ||
pub radius: f32, | ||
} | ||
impl Primitive2d for Circle {} | ||
|
||
/// An unbounded plane in 2D space. It forms a separating surface through the origin, | ||
/// stretching infinitely far | ||
#[derive(Clone, Copy, Debug)] | ||
pub struct Plane2d { | ||
/// The normal of the plane. The plane will be placed perpendicular to this direction | ||
pub normal: Direction2d, | ||
} | ||
impl Primitive2d for Plane2d {} | ||
|
||
/// An infinite line along a direction in 2D space. | ||
/// | ||
/// For a finite line: [`Segment2d`] | ||
#[derive(Clone, Copy, Debug)] | ||
pub struct Line2d { | ||
/// The direction of the line. The line extends infinitely in both the given direction | ||
/// and its opposite direction | ||
pub direction: Direction2d, | ||
} | ||
impl Primitive2d for Line2d {} | ||
|
||
/// A segment of a line along a direction in 2D space. | ||
#[doc(alias = "LineSegment2d")] | ||
#[derive(Clone, Debug)] | ||
pub struct Segment2d { | ||
/// The direction of the line segment | ||
pub direction: Direction2d, | ||
/// Half the length of the line segment. The segment extends by this amount in both | ||
/// the given direction and its opposite direction | ||
pub half_length: f32, | ||
} | ||
impl Primitive2d for Segment2d {} | ||
|
||
impl Segment2d { | ||
/// Create a line segment from a direction and full length of the segment | ||
pub fn new(direction: Direction2d, length: f32) -> Self { | ||
Self { | ||
direction, | ||
half_length: length / 2., | ||
} | ||
} | ||
|
||
/// Get a line segment and translation from two points at each end of a line segment | ||
/// | ||
/// Panics if point1 == point2 | ||
pub fn from_points(point1: Vec2, point2: Vec2) -> (Self, Vec2) { | ||
let diff = point2 - point1; | ||
let length = diff.length(); | ||
( | ||
Self::new(Direction2d::from_normalized(diff / length), length), | ||
(point1 + point2) / 2., | ||
) | ||
} | ||
|
||
/// Get the position of the first point on the line segment | ||
pub fn point1(&self) -> Vec2 { | ||
*self.direction * -self.half_length | ||
} | ||
|
||
/// Get the position of the second point on the line segment | ||
pub fn point2(&self) -> Vec2 { | ||
*self.direction * self.half_length | ||
} | ||
} | ||
|
||
/// A series of connected line segments in 2D space. | ||
/// | ||
/// For a version without generics: [`BoxedPolyline2d`] | ||
#[derive(Clone, Debug)] | ||
pub struct Polyline2d<const N: usize> { | ||
/// The vertices of the polyline | ||
pub vertices: [Vec2; N], | ||
} | ||
impl<const N: usize> Primitive2d for Polyline2d<N> {} | ||
|
||
/// A series of connected line segments in 2D space, allocated on the heap | ||
/// in a `Box<[Vec2]>`. | ||
/// | ||
/// For a version without alloc: [`Polyline2d`] | ||
#[derive(Clone, Debug)] | ||
pub struct BoxedPolyline2d { | ||
/// The vertices of the polyline | ||
pub vertices: Box<[Vec2]>, | ||
} | ||
impl Primitive2d for BoxedPolyline2d {} | ||
|
||
/// A triangle in 2D space | ||
#[derive(Clone, Debug)] | ||
pub struct Triangle2d { | ||
/// The vertices of the triangle | ||
pub vertices: [Vec2; 3], | ||
} | ||
impl Primitive2d for Triangle2d {} | ||
|
||
/// A rectangle primitive | ||
#[doc(alias = "Quad")] | ||
#[derive(Clone, Copy, Debug)] | ||
pub struct Rectangle { | ||
/// The half width of the rectangle | ||
pub half_width: f32, | ||
/// The half height of the rectangle | ||
pub half_height: f32, | ||
} | ||
impl Primitive2d for Rectangle {} | ||
|
||
impl Rectangle { | ||
/// Create a rectangle from a full width and height | ||
pub fn new(width: f32, height: f32) -> Self { | ||
Self::from_size(Vec2::new(width, height)) | ||
} | ||
|
||
/// Create a rectangle from a given full size | ||
pub fn from_size(size: Vec2) -> Self { | ||
Self { | ||
half_width: size.x / 2., | ||
half_height: size.y / 2., | ||
} | ||
} | ||
} | ||
|
||
/// A polygon with N vertices. | ||
/// | ||
/// For a version without generics: [`BoxedPolygon`] | ||
#[derive(Clone, Debug)] | ||
pub struct Polygon<const N: usize> { | ||
/// The vertices of the polygon | ||
pub vertices: [Vec2; N], | ||
} | ||
impl<const N: usize> Primitive2d for Polygon<N> {} | ||
|
||
/// A polygon with a variable number of vertices, allocated on the heap | ||
/// in a `Box<[Vec2]>`. | ||
/// | ||
/// For a version without alloc: [`Polygon`] | ||
#[derive(Clone, Debug)] | ||
pub struct BoxedPolygon { | ||
/// The vertices of the polygon | ||
pub vertices: Box<[Vec2]>, | ||
} | ||
impl Primitive2d for BoxedPolygon {} | ||
|
||
/// A polygon where all vertices lie on a circle, equally far apart | ||
#[derive(Clone, Copy, Debug)] | ||
pub struct RegularPolygon { | ||
/// The circumcircle on which all vertices lie | ||
pub circumcircle: Circle, | ||
/// The number of sides | ||
pub sides: usize, | ||
} | ||
impl Primitive2d for RegularPolygon {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
use super::Primitive3d; | ||
use crate::Vec3; | ||
|
||
/// A normalized vector pointing in a direction in 3D space | ||
#[derive(Clone, Copy, Debug)] | ||
pub struct Direction3d(Vec3); | ||
|
||
impl From<Vec3> for Direction3d { | ||
fn from(value: Vec3) -> Self { | ||
Self(value.normalize()) | ||
} | ||
} | ||
|
||
impl Direction3d { | ||
/// Create a direction from a [`Vec3`] that is already normalized | ||
pub fn from_normalized(value: Vec3) -> Self { | ||
debug_assert!(value.is_normalized()); | ||
Self(value) | ||
} | ||
} | ||
|
||
impl std::ops::Deref for Direction3d { | ||
type Target = Vec3; | ||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
|
||
/// A sphere primitive | ||
#[derive(Clone, Copy, Debug)] | ||
pub struct Sphere { | ||
/// The radius of the sphere | ||
pub radius: f32, | ||
} | ||
impl Primitive3d for Sphere {} | ||
|
||
/// An unbounded plane in 3D space. It forms a separating surface through the origin, | ||
/// stretching infinitely far | ||
#[derive(Clone, Copy, Debug)] | ||
pub struct Plane3d { | ||
/// The normal of the plane. The plane will be placed perpendicular to this direction | ||
pub normal: Direction3d, | ||
} | ||
impl Primitive3d for Plane3d {} | ||
|
||
/// An infinite line along a direction in 3D space. | ||
/// | ||
/// For a finite line: [`Segment3d`] | ||
#[derive(Clone, Copy, Debug)] | ||
pub struct Line3d { | ||
/// The direction of the line | ||
pub direction: Direction3d, | ||
} | ||
impl Primitive3d for Line3d {} | ||
|
||
/// A segment of a line along a direction in 3D space. | ||
#[doc(alias = "LineSegment3d")] | ||
#[derive(Clone, Debug)] | ||
pub struct Segment3d { | ||
/// The direction of the line | ||
pub direction: Direction3d, | ||
/// Half the length of the line segment. The segment extends by this amount in both | ||
/// the given direction and its opposite direction | ||
pub half_length: f32, | ||
} | ||
impl Primitive3d for Segment3d {} | ||
|
||
impl Segment3d { | ||
/// Create a line segment from a direction and full length of the segment | ||
pub fn new(direction: Direction3d, length: f32) -> Self { | ||
Self { | ||
direction, | ||
half_length: length / 2., | ||
} | ||
} | ||
|
||
/// Get a line segment and translation from two points at each end of a line segment | ||
/// | ||
/// Panics if point1 == point2 | ||
pub fn from_points(point1: Vec3, point2: Vec3) -> (Self, Vec3) { | ||
let diff = point2 - point1; | ||
let length = diff.length(); | ||
( | ||
Self::new(Direction3d::from_normalized(diff / length), length), | ||
(point1 + point2) / 2., | ||
) | ||
} | ||
|
||
/// Get the position of the first point on the line segment | ||
pub fn point1(&self) -> Vec3 { | ||
*self.direction * -self.half_length | ||
} | ||
|
||
/// Get the position of the second point on the line segment | ||
pub fn point2(&self) -> Vec3 { | ||
*self.direction * self.half_length | ||
} | ||
} | ||
|
||
/// A series of connected line segments in 3D space. | ||
/// | ||
/// For a version without generics: [`BoxedPolyline3d`] | ||
#[derive(Clone, Debug)] | ||
pub struct Polyline3d<const N: usize> { | ||
/// The vertices of the polyline | ||
pub vertices: [Vec3; N], | ||
} | ||
impl<const N: usize> Primitive3d for Polyline3d<N> {} | ||
|
||
/// A series of connected line segments in 3D space, allocated on the heap | ||
/// in a `Box<[Vec3]>`. | ||
/// | ||
/// For a version without alloc: [`Polyline3d`] | ||
#[derive(Clone, Debug)] | ||
pub struct BoxedPolyline3d { | ||
/// The vertices of the polyline | ||
pub vertices: Box<[Vec3]>, | ||
} | ||
impl Primitive3d for BoxedPolyline3d {} | ||
|
||
/// A cuboid primitive, more commonly known as a box. | ||
#[derive(Clone, Copy, Debug)] | ||
pub struct Cuboid { | ||
/// Half of the width, height and depth of the cuboid | ||
pub half_extents: Vec3, | ||
} | ||
impl Primitive3d for Cuboid {} | ||
|
||
impl Cuboid { | ||
/// Create a cuboid from a full x, y and z length | ||
pub fn new(x_length: f32, y_length: f32, z_length: f32) -> Self { | ||
Self::from_size(Vec3::new(x_length, y_length, z_length)) | ||
} | ||
|
||
/// Create a cuboid from a given full size | ||
pub fn from_size(size: Vec3) -> Self { | ||
Self { | ||
half_extents: size / 2., | ||
} | ||
} | ||
} | ||
|
||
/// A cylinder primitive | ||
#[derive(Clone, Copy, Debug)] | ||
pub struct Cylinder { | ||
/// The radius of the cylinder | ||
pub radius: f32, | ||
/// The half height of the cylinder | ||
pub half_height: f32, | ||
} | ||
impl Primitive3d for Cylinder {} | ||
|
||
impl Cylinder { | ||
/// Create a cylinder from a radius and full height | ||
pub fn new(radius: f32, height: f32) -> Self { | ||
Self { | ||
radius, | ||
half_height: height / 2., | ||
} | ||
} | ||
} | ||
|
||
/// A capsule primitive. | ||
/// A capsule is defined as a surface at a distance (radius) from a line | ||
#[derive(Clone, Copy, Debug)] | ||
pub struct Capsule { | ||
/// The radius of the capsule | ||
pub radius: f32, | ||
/// Half the height of the capsule, excluding the hemispheres | ||
pub half_length: f32, | ||
} | ||
impl super::Primitive2d for Capsule {} | ||
impl Primitive3d for Capsule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
//! This module defines primitive shapes. | ||
//! The origin is (0, 0) for 2D primitives and (0, 0, 0) for 3D primitives, | ||
//! unless stated otherwise. | ||
|
||
mod dim2; | ||
pub use dim2::*; | ||
mod dim3; | ||
pub use dim3::*; | ||
|
||
/// A marker trait for 2D primitives | ||
pub trait Primitive2d {} | ||
|
||
/// A marker trait for 3D primitives | ||
pub trait Primitive3d {} |