Skip to content
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

Add ramp primitive #11790

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3cca061
Added ramp and tests for ramp
lynn-lumen Feb 9, 2024
1639d0d
Implement `Meshable` and `Default` for `Cone`
lynn-lumen Feb 9, 2024
8a732f9
Implement `Bounded3d` for `Ramp` and added Ramp primitive gizmo
lynn-lumen Feb 9, 2024
3b9047f
Implement reflect for Ramp
lynn-lumen Feb 9, 2024
0be1b66
Update 3d_gizmos example to include ramp
lynn-lumen Feb 9, 2024
93dcc64
Merge branch 'main' into add_ramp_and_cone
lynn-lumen Feb 9, 2024
13d9e67
Ran cargo fmt
lynn-lumen Feb 9, 2024
61b4273
Merge branch 'add_ramp_and_cone' of https://github.com/solis-lumine-v…
lynn-lumen Feb 9, 2024
85fb64c
Truncated floats in tests
lynn-lumen Feb 9, 2024
505cf05
Removed `Meshable` for `Cone``
lynn-lumen Feb 9, 2024
c8a642e
Generalize `Ramp` to `Prism`
lynn-lumen Feb 12, 2024
10cf433
Fix prism bounding test
lynn-lumen Feb 12, 2024
3dbe528
Merge remote-tracking branch 'upstream/main' into add_ramp_and_cone
lynn-lumen Apr 9, 2024
748d65d
Fix merge errors
lynn-lumen Apr 11, 2024
d8a1078
Revert to ramp
lynn-lumen Apr 11, 2024
8ae6ba5
Fix example
lynn-lumen Apr 11, 2024
9124845
Merge branch 'main' into add_ramp_and_cone
lynn-lumen Apr 12, 2024
5a91157
Fix merge errors
lynn-lumen Apr 12, 2024
3d43bdb
fix CI errors
lynn-lumen Apr 12, 2024
76969df
Merge branch 'main' into add_ramp_and_cone
lynn-lumen Apr 13, 2024
2482ded
Merge branch 'main' into add_ramp_and_cone
lynn-lumen May 3, 2024
94fc290
Use `element_product`
lynn-lumen May 3, 2024
b100632
Use `Vec3A` for bounding
lynn-lumen May 3, 2024
b8b92af
Don't split halfsize in gizmos
lynn-lumen May 3, 2024
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
51 changes: 50 additions & 1 deletion crates/bevy_gizmos/src/primitives/dim3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::f32::consts::TAU;
use bevy_color::Color;
use bevy_math::primitives::{
BoxedPolyline3d, Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Line3d, Plane3d,
Polyline3d, Primitive3d, Segment3d, Sphere, Tetrahedron, Torus,
Polyline3d, Primitive3d, Ramp, Segment3d, Sphere, Tetrahedron, Torus,
};
use bevy_math::{Dir3, Quat, Vec3};

Expand Down Expand Up @@ -944,6 +944,55 @@ impl<T: GizmoConfigGroup> Drop for Torus3dBuilder<'_, '_, '_, T> {
}
}

// ramp

impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive3d<Ramp> for Gizmos<'w, 's, T> {
type Output<'a> = () where Self: 'a;

fn primitive_3d(
&mut self,
primitive: Ramp,
position: Vec3,
rotation: Quat,
color: impl Into<Color>,
) -> Self::Output<'_> {
if !self.enabled {
return;
}

let [half_extend_x, half_extend_y, half_extend_z] = primitive.half_size.to_array();
lynn-lumen marked this conversation as resolved.
Show resolved Hide resolved

// transform the points from the reference unit cube-like ramp to the actual ramp coords
let [a, b, c, d, e, f] = [
[-1.0, -1.0, -1.0],
[-1.0, -1.0, 1.0],
[1.0, -1.0, -1.0],
[1.0, -1.0, 1.0],
[1.0, 1.0, 1.0],
[-1.0, 1.0, 1.0],
]
.map(|[sx, sy, sz]| Vec3::new(sx * half_extend_x, sy * half_extend_y, sz * half_extend_z))
.map(rotate_then_translate_3d(rotation, position));

let lines = vec![
(a, b),
(b, d),
(d, c),
(c, a),
(b, f),
(d, e),
(e, f),
(a, f),
(c, e),
];

let color = color.into();
lines.into_iter().for_each(|(start, end)| {
self.line(start, end, color);
});
}
}

// tetrahedron

impl<'w, 's, T: GizmoConfigGroup> GizmoPrimitive3d<Tetrahedron> for Gizmos<'w, 's, T> {
Expand Down
36 changes: 34 additions & 2 deletions crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
bounding::{Bounded2d, BoundingCircle},
primitives::{
BoxedPolyline3d, Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Line3d, Plane3d,
Polyline3d, Segment3d, Sphere, Torus, Triangle2d,
Polyline3d, Ramp, Segment3d, Sphere, Torus, Triangle2d,
},
Dir3, Mat3, Quat, Vec2, Vec3,
};
Expand Down Expand Up @@ -303,14 +303,32 @@ impl Bounded3d for Torus {
}
}

impl Bounded3d for Ramp {
fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d {
let points = [
lynn-lumen marked this conversation as resolved.
Show resolved Hide resolved
self.half_size,
self.half_size * Vec3::new(1.0, -1.0, 1.0),
self.half_size * Vec3::new(1.0, -1.0, -1.0),
self.half_size * Vec3::new(-1.0, 1.0, 1.0),
self.half_size * Vec3::new(-1.0, -1.0, 1.0),
self.half_size * Vec3::NEG_ONE,
];
Aabb3d::from_point_cloud(translation, rotation, &points)
}

fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere {
BoundingSphere::new(translation, self.half_size.length())
lynn-lumen marked this conversation as resolved.
Show resolved Hide resolved
}
}

#[cfg(test)]
mod tests {
use glam::{Quat, Vec3};

use crate::{
bounding::Bounded3d,
primitives::{
Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Line3d, Plane3d, Polyline3d,
Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Line3d, Plane3d, Polyline3d, Ramp,
Segment3d, Sphere, Torus,
},
Dir3,
Expand Down Expand Up @@ -535,4 +553,18 @@ mod tests {
assert_eq!(bounding_sphere.center, translation);
assert_eq!(bounding_sphere.radius(), 1.5);
}

#[test]
fn ramp() {
let ramp = Ramp::new(1.0, 3.0, 4.0);
let translation = Vec3::new(-3.0, 1.75, 0.0);

let aabb = ramp.aabb_3d(translation, Quat::IDENTITY);
assert_eq!(aabb.min, Vec3::new(-3.5, 0.25, -2.0));
assert_eq!(aabb.max, Vec3::new(-2.5, 3.25, 2.0));
lynn-lumen marked this conversation as resolved.
Show resolved Hide resolved

let bounding_sphere = ramp.bounding_sphere(translation, Quat::IDENTITY);
assert_eq!(bounding_sphere.center, translation);
assert_eq!(bounding_sphere.radius(), 2.5495098);
}
}
92 changes: 92 additions & 0 deletions crates/bevy_math/src/primitives/dim3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,75 @@ impl Torus {
}
}

/// A Ramp primitive.
/// The ramp will slant down along the Y axis towards the negative-Z axis.
lynn-lumen marked this conversation as resolved.
Show resolved Hide resolved
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Ramp {
/// Half of the width, height and depth of the ramp
pub half_size: Vec3,
}
impl Primitive3d for Ramp {}

impl Default for Ramp {
/// Returns the default [`Ramp`] with a width, height, and depth of `1.0`.
fn default() -> Self {
Self {
half_size: Vec3::splat(0.5),
}
}
}

impl Ramp {
/// Create a new `Ramp` from a full x, y, and z length
#[inline(always)]
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 new `Ramp` from a given full size
#[inline(always)]
pub fn from_size(size: Vec3) -> Self {
Self {
half_size: size / 2.0,
}
}

/// Create a new `Ramp` from a given full base size and an `incline`.
/// The `incline` is the angle between the Z axis and the slanted face of the ramp.
/// The `incline` is in radians.
pub fn from_incline(x_length: f32, z_length: f32, incline: f32) -> Self {
let y_length = incline.tan() * z_length;
Self::from_size(Vec3::new(x_length, y_length, z_length))
}

/// Get the surface area of the ramp.
#[inline(always)]
pub fn area(&self) -> f32 {
(self.half_size.x
* (self.half_size.z
+ self.half_size.y
+ (self.half_size.z.powi(2) + self.half_size.y.powi(2)).sqrt())
+ self.half_size.z * self.half_size.y)
* 4.
}

/// Get the volume of the ramp.
#[inline(always)]
pub fn volume(&self) -> f32 {
4.0 * self.half_size.x * self.half_size.y * self.half_size.z
lynn-lumen marked this conversation as resolved.
Show resolved Hide resolved
}

/// Get the incline of the ramp.
///
/// The `incline` is the angle between the Z axis and the slanted face of the ramp.
/// The `incline` is in radians.
#[inline(always)]
pub fn incline(&self) -> f32 {
self.half_size.y.atan2(self.half_size.z)
}
}

/// A 3D triangle primitive.
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
Expand Down Expand Up @@ -1098,4 +1167,27 @@ mod tests {
);
assert_relative_eq!(Tetrahedron::default().centroid(), Vec3::ZERO);
}

#[test]
fn ramp_math() {
use std::f32::consts::FRAC_PI_4;
let ramp = Ramp {
half_size: Vec3::new(1.9, 3.2, 7.45),
};

assert_eq!(ramp.area(), 237.92213, "incorrect area");
assert_eq!(ramp.volume(), 181.18399, "incorrect volume");
assert_eq!(ramp.incline(), 0.40570152, "incorrect incline");

assert_eq!(Ramp::default().area(), 4.4142136, "incorrect area");
assert_eq!(Ramp::default().volume(), 0.5, "incorrect volume");
assert_eq!(Ramp::default().incline(), FRAC_PI_4, "incorrect incline");

let ramp = Ramp::from_incline(2.4, 3.8, 0.12);
assert_eq!(
ramp.half_size,
Vec3::new(1.2, 0.22910073, 1.9),
"incorrect from_incline"
);
}
}
8 changes: 8 additions & 0 deletions crates/bevy_reflect/src/impls/math/primitives3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,11 @@ impl_reflect!(
vertices: [Vec3; 4],
}
);

impl_reflect!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Ramp {
half_size: Vec3,
}
);
1 change: 1 addition & 0 deletions crates/bevy_render/src/mesh/primitives/dim3/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod capsule;
mod cuboid;
mod cylinder;
mod plane;
mod ramp;
mod sphere;
mod torus;
pub(crate) mod triangle3d;
Expand Down
72 changes: 72 additions & 0 deletions crates/bevy_render/src/mesh/primitives/dim3/ramp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use bevy_math::{primitives::Ramp, Vec3};
use wgpu::PrimitiveTopology;

use crate::{
mesh::{Indices, Mesh, Meshable},
render_asset::RenderAssetUsages,
};

impl Meshable for Ramp {
type Output = Mesh;

fn mesh(&self) -> Self::Output {
let min = -self.half_size;
let max = self.half_size;

let top_normal = Vec3::new(0.0, min.z, max.y).normalize_or_zero().to_array();

// Suppose Y-up right hand, and camera look from +Z to -Z
let vertices = &[
// Slope
([min.x, max.y, max.z], top_normal, [1.0, 0.0]),
([max.x, max.y, max.z], top_normal, [0.0, 0.0]),
([max.x, min.y, min.z], top_normal, [0.0, 1.0]),
([min.x, min.y, min.z], top_normal, [1.0, 1.0]),
// Right
([max.x, min.y, min.z], [1.0, 0.0, 0.0], [0.0, 0.0]),
([max.x, max.y, max.z], [1.0, 0.0, 0.0], [1.0, 1.0]),
([max.x, min.y, max.z], [1.0, 0.0, 0.0], [0.0, 1.0]),
// Left
([min.x, min.y, max.z], [-1.0, 0.0, 0.0], [1.0, 0.0]),
([min.x, max.y, max.z], [-1.0, 0.0, 0.0], [0.0, 0.0]),
([min.x, min.y, min.z], [-1.0, 0.0, 0.0], [1.0, 1.0]),
// Bottom
([max.x, min.y, max.z], [0.0, -1.0, 0.0], [0.0, 0.0]),
([min.x, min.y, max.z], [0.0, -1.0, 0.0], [1.0, 0.0]),
([min.x, min.y, min.z], [0.0, -1.0, 0.0], [1.0, 1.0]),
([max.x, min.y, min.z], [0.0, -1.0, 0.0], [0.0, 1.0]),
// Front
([min.x, max.y, max.z], [0.0, 0.0, 1.0], [0.0, 1.0]),
([max.x, max.y, max.z], [0.0, 0.0, 1.0], [1.0, 1.0]),
([max.x, min.y, max.z], [0.0, 0.0, 1.0], [1.0, 0.0]),
([min.x, min.y, max.z], [0.0, 0.0, 1.0], [0.0, 0.0]),
];

let positions: Vec<_> = vertices.iter().map(|(p, _, _)| *p).collect();
let normals: Vec<_> = vertices.iter().map(|(_, n, _)| *n).collect();
let uvs: Vec<_> = vertices.iter().map(|(_, _, uv)| *uv).collect();

let indices = Indices::U32(vec![
0, 1, 2, 2, 3, 0, // slope
4, 5, 6, // right
7, 8, 9, // left
10, 11, 12, 12, 13, 10, // bottom
14, 16, 15, 16, 14, 17, // front
]);

Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, uvs)
.with_inserted_indices(indices)
}
}

impl From<Ramp> for Mesh {
fn from(ramp: Ramp) -> Self {
ramp.mesh()
}
}
1 change: 1 addition & 0 deletions examples/3d/3d_shapes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ fn setup(
meshes.add(Capsule3d::default()),
meshes.add(Torus::default()),
meshes.add(Cylinder::default()),
meshes.add(Ramp::default()),
meshes.add(Sphere::default().mesh().ico(5).unwrap()),
meshes.add(Sphere::default().mesh().uv(32, 18)),
];
Expand Down