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 12 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
50 changes: 49 additions & 1 deletion crates/bevy_gizmos/src/primitives/dim3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::f32::consts::TAU;

use bevy_math::primitives::{
BoxedPolyline3d, Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Direction3d, Line3d,
Plane3d, Polyline3d, Primitive3d, Segment3d, Sphere, Torus,
Plane3d, Polyline3d, Primitive3d, Prism, Segment3d, Sphere, Torus,
};
use bevy_math::{Quat, Vec3};
use bevy_render::color::Color;
Expand Down Expand Up @@ -917,3 +917,51 @@ impl<T: GizmoConfigGroup> Drop for Torus3dBuilder<'_, '_, '_, T> {
});
}
}

// prism

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

fn primitive_3d(
&mut self,
primitive: Prism,
position: Vec3,
rotation: Quat,
color: 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 prism to the actual prism 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, primitive.apex_displacement],
[-1.0, 1.0, primitive.apex_displacement],
]
.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),
];

lines.into_iter().for_each(|(start, end)| {
self.line(start, end, color);
});
}
}
61 changes: 58 additions & 3 deletions crates/bevy_math/src/bounding/bounded3d/primitive_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
use glam::{Mat3, Quat, Vec2, Vec3};

use crate::{
bounding::{Bounded2d, BoundingCircle},
bounding::{Bounded2d, BoundingCircle, BoundingVolume},
primitives::{
BoxedPolyline3d, Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Direction3d, Line3d,
Plane3d, Polyline3d, Segment3d, Sphere, Torus, Triangle2d,
Plane3d, Polyline3d, Prism, Segment3d, Sphere, Torus, Triangle2d,
},
};

Expand Down Expand Up @@ -304,6 +304,44 @@ impl Bounded3d for Torus {
}
}

impl Bounded3d for Prism {
fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d {
let line = Segment3d {
direction: Direction3d::new_unchecked(Vec3::X),
half_length: self.half_size.x,
};
let apex_aabb = line.aabb_3d(
translation
+ Vec3::new(
0.0,
self.half_size.y,
self.half_size.z * self.apex_displacement,
),
rotation,
);
let front_aabb = line.aabb_3d(
translation + (Vec3::NEG_Z * self.half_size.z + Vec3::NEG_Y * self.half_size.y),
rotation,
);
let back_aabb = line.aabb_3d(
translation + rotation * (Vec3::Z * self.half_size.z + Vec3::NEG_Y * self.half_size.y),
rotation,
);

apex_aabb.merge(&front_aabb).merge(&back_aabb)
}

fn bounding_sphere(&self, translation: Vec3, rotation: Quat) -> BoundingSphere {
let furthest = self.half_size * Vec3::new(1.0, 1.0, self.apex_displacement.abs());
let local_center = Vec3::new(0.0, 0.0, self.half_size.z * self.apex_displacement * 0.5);

let radius = (furthest - local_center).length();
let center = translation + rotation * local_center;

BoundingSphere::new(center, radius)
}
}

#[cfg(test)]
mod tests {
use glam::{Quat, Vec3};
Expand All @@ -312,7 +350,7 @@ mod tests {
bounding::Bounded3d,
primitives::{
Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Direction3d, Line3d, Plane3d,
Polyline3d, Segment3d, Sphere, Torus,
Polyline3d, Prism, Segment3d, Sphere, Torus,
},
};

Expand Down Expand Up @@ -546,4 +584,21 @@ mod tests {
assert_eq!(bounding_sphere.center, translation);
assert_eq!(bounding_sphere.radius(), 1.5);
}

#[test]
fn prism() {
let prism = Prism {
half_size: Vec3::new(1.0, 0.8, 2.5),
apex_displacement: 1.0,
};
let translation = Vec3::new(3.5, -1.25, 2.0);

let aabb = prism.aabb_3d(translation, Quat::IDENTITY);
assert_eq!(aabb.min, Vec3::new(2.5, -2.05, -0.5));
assert_eq!(aabb.max, Vec3::new(4.5, -0.45, 4.5));

let bounding_sphere = prism.bounding_sphere(translation, Quat::IDENTITY);
assert_eq!(bounding_sphere.center, Vec3::new(3.5, -1.25, 3.25));
assert_eq!(bounding_sphere.radius(), 1.789553);
}
}
47 changes: 47 additions & 0 deletions crates/bevy_math/src/primitives/dim3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,43 @@ impl Torus {
}
}

/// A triangular prism primitive, often representing a ramp.
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Prism {
/// Half of the width, height and depth of the prism
pub half_size: Vec3,
/// Displacement of the apex edge along the Z-axis. -1.0 positions the apex straight above the bottom-front edge.
pub apex_displacement: f32,
}
lynn-lumen marked this conversation as resolved.
Show resolved Hide resolved
impl Primitive3d for Prism {}

impl Default for Prism {
fn default() -> Self {
Self {
half_size: Vec3::splat(0.5),
apex_displacement: 0.0,
}
}
}

impl Prism {
/// Get the surface area of the prism
#[inline(always)]
pub fn area(&self) -> f32 {
let [x, y, z] = self.half_size.to_array();
let edge1 = (z * (self.apex_displacement + 1.0)).hypot(2.0 * y);
let edge2 = (z * (self.apex_displacement - 1.0)).hypot(2.0 * y);
4.0 * z * y + 2.0 * x * (edge1 + edge2 + 2.0 * z)
}

/// Get the volume of the prism
#[inline(always)]
pub fn volume(&self) -> f32 {
self.half_size.x * self.half_size.y * self.half_size.z * 4.0
}
}

#[cfg(test)]
mod tests {
// Reference values were computed by hand and/or with external tools
Expand Down Expand Up @@ -930,4 +967,14 @@ mod tests {
assert_relative_eq!(torus.area(), 33.16187);
assert_relative_eq!(torus.volume(), 4.97428, epsilon = 0.00001);
}

#[test]
fn prism_math() {
let prism = Prism {
half_size: Vec3::splat(0.75),
apex_displacement: 1.0,
};
assert_eq!(prism.volume(), 1.6875, "incorrect prism volume");
assert_eq!(prism.area(), 9.93198, "incorrect prism area");
}
}
9 changes: 9 additions & 0 deletions crates/bevy_reflect/src/impls/math/primitives3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,12 @@ impl_reflect!(
major_radius: f32,
}
);

impl_reflect!(
#[reflect(Debug, PartialEq, Serialize, Deserialize)]
#[type_path = "bevy_math::primitives"]
struct Prism {
half_size: Vec3,
apex_displacement: f32,
}
);
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 prism;
mod sphere;
mod torus;

Expand Down
79 changes: 79 additions & 0 deletions crates/bevy_render/src/mesh/primitives/dim3/prism.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use bevy_math::{primitives::Prism, Vec3};
use wgpu::PrimitiveTopology;

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

impl Meshable for Prism {
type Output = Mesh;

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

let front_normal = Vec3::new(0.0, max.z * (self.apex_displacement + 1.0), max.y * 2.0)
.normalize_or_zero()
.to_array();
let back_normal = Vec3::new(0.0, max.z * (self.apex_displacement - 1.0), max.y * 2.0)
.normalize_or_zero()
.to_array();

let apex_z = self.apex_displacement * max.z;

// Suppose Y-up right hand, and camera look from +Z to -Z
let vertices = &[
// Back
([min.x, max.y, apex_z], back_normal, [1.0, 0.0]),
([max.x, max.y, apex_z], back_normal, [0.0, 0.0]),
([max.x, min.y, min.z], back_normal, [0.0, 1.0]),
([min.x, min.y, min.z], back_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, apex_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, apex_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, apex_z], front_normal, [0.0, 0.0]),
([max.x, max.y, apex_z], front_normal, [0.0, 1.0]),
([max.x, min.y, max.z], front_normal, [1.0, 0.0]),
([min.x, min.y, max.z], front_normal, [1.0, 1.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, // back
4, 5, 6, // right
7, 8, 9, // left
10, 11, 12, 12, 13, 10, // bottom
14, 16, 15, 16, 14, 17, // Slope
]);

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<Prism> for Mesh {
fn from(prism: Prism) -> Self {
prism.mesh()
}
}
15 changes: 14 additions & 1 deletion examples/3d/3d_gizmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ enum PrimitiveState {
Cone,
ConicalFrustum,
Torus,
Ramp,
}

impl PrimitiveState {
const ALL: [Self; 11] = [
const ALL: [Self; 12] = [
Self::Sphere,
Self::Plane,
Self::Line,
Expand All @@ -59,6 +60,7 @@ impl PrimitiveState {
Self::Cone,
Self::ConicalFrustum,
Self::Torus,
Self::Ramp,
Self::Nothing,
];
fn next(self) -> Self {
Expand Down Expand Up @@ -329,6 +331,17 @@ fn draw_primitives(
.major_segments(segments)
.minor_segments((segments / 4).max(1));
}
PrimitiveState::Ramp => {
gizmos.primitive_3d(
Prism {
half_size: Vec3::new(1.0, 0.5, 2.0),
apex_displacement: 0.5,
},
center,
rotation,
Color::default(),
);
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions examples/3d/3d_shapes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ fn setup(
meshes.add(Cylinder::default()),
meshes.add(Sphere::default().mesh().ico(5).unwrap()),
meshes.add(Sphere::default().mesh().uv(32, 18)),
meshes.add(Prism {
apex_displacement: 4.0,
half_size: Vec3::ONE * 0.5,
}),
];

let num_shapes = shapes.len();
Expand Down