Skip to content

Commit

Permalink
Implement Meshable for some 3D primitives (#11688)
Browse files Browse the repository at this point in the history
# Objective

Split up from #11007, fixing most of the remaining work for #10569.

Implement `Meshable` for `Cuboid`, `Sphere`, `Cylinder`, `Capsule`,
`Torus`, and `Plane3d`. This covers all shapes that Bevy has mesh
structs for in `bevy_render::mesh::shapes`.

`Cone` and `ConicalFrustum` are new shapes, so I can add them in a
follow-up, or I could just add them here directly if that's preferrable.

## Solution

Implement `Meshable` for `Cuboid`, `Sphere`, `Cylinder`, `Capsule`,
`Torus`, and `Plane3d`.

The logic is mostly just a copy of the the existing `bevy_render`
shapes, but `Plane3d` has a configurable surface normal that affects the
orientation. Some property names have also been changed to be more
consistent.

The default values differ from the old shapes to make them a bit more
logical:

- Spheres now have a radius of 0.5 instead of 1.0. The default capsule
is equivalent to the default cylinder with the sphere's halves glued on.
- The inner and outer radius of the torus are now 0.5 and 1.0 instead of
0.5 and 1.5 (i.e. the new minor and major radii are 0.25 and 0.75). It's
double the width of the default cuboid, half of its height, and the
default sphere matches the size of the hole.
- `Cuboid` is 1x1x1 by default unlike the dreaded `Box` which is 2x1x1.

Before, with "old" shapes:


![old](https://github.com/bevyengine/bevy/assets/57632562/733f3dda-258c-4491-8152-9829e056a1a3)

Now, with primitive meshing:


![new](https://github.com/bevyengine/bevy/assets/57632562/5a1af14f-bb98-401d-82cf-de8072fea4ec)

I only changed the `3d_shapes` example to use primitives for now. I can
change them all in this PR or a follow-up though, whichever way is
preferrable.

### Sphere API

Spheres have had separate `Icosphere` and `UVSphere` structs, but with
primitives we only have one `Sphere`.

We need to handle this with builders:

```rust
// Existing structs
let ico = Mesh::try_from(Icophere::default()).unwrap();
let uv = Mesh::from(UVSphere::default());

// Primitives
let ico = Sphere::default().mesh().ico(5).unwrap();
let uv = Sphere::default().mesh().uv(32, 18);
```

We could add methods on `Sphere` directly to skip calling `.mesh()`.

I also added a `SphereKind` enum that can be used with the `kind`
method:

```rust
let ico = Sphere::default()
    .mesh()
    .kind(SphereKind::Ico { subdivisions: 8 })
    .build();
```

The default mesh for a `Sphere` is an icosphere with 5 subdivisions
(like the default `Icosphere`).

---

## Changelog

- Implement `Meshable` and `Default` for `Cuboid`, `Sphere`, `Cylinder`,
`Capsule`, `Torus`, and `Plane3d`
- Use primitives in `3d_shapes` example

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
  • Loading branch information
Jondolf and alice-i-cecile authored Feb 6, 2024
1 parent 9f2eabb commit cf15e6b
Show file tree
Hide file tree
Showing 10 changed files with 1,330 additions and 9 deletions.
56 changes: 56 additions & 0 deletions crates/bevy_math/src/primitives/dim3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,13 @@ pub struct Sphere {
}
impl Primitive3d for Sphere {}

impl Default for Sphere {
/// Returns the default [`Sphere`] with a radius of `0.5`.
fn default() -> Self {
Self { radius: 0.5 }
}
}

impl Sphere {
/// Create a new [`Sphere`] from a `radius`
#[inline(always)]
Expand Down Expand Up @@ -210,6 +217,15 @@ pub struct Plane3d {
}
impl Primitive3d for Plane3d {}

impl Default for Plane3d {
/// Returns the default [`Plane3d`] with a normal pointing in the `+Y` direction.
fn default() -> Self {
Self {
normal: Direction3d::Y,
}
}
}

impl Plane3d {
/// Create a new `Plane3d` from a normal
///
Expand Down Expand Up @@ -374,6 +390,15 @@ pub struct Cuboid {
}
impl Primitive3d for Cuboid {}

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

impl Cuboid {
/// Create a new `Cuboid` from a full x, y, and z length
#[inline(always)]
Expand Down Expand Up @@ -439,6 +464,16 @@ pub struct Cylinder {
}
impl Primitive3d for Cylinder {}

impl Default for Cylinder {
/// Returns the default [`Cylinder`] with a radius of `0.5` and a height of `1.0`.
fn default() -> Self {
Self {
radius: 0.5,
half_height: 0.5,
}
}
}

impl Cylinder {
/// Create a new `Cylinder` from a radius and full height
#[inline(always)]
Expand Down Expand Up @@ -496,6 +531,17 @@ pub struct Capsule3d {
}
impl Primitive3d for Capsule3d {}

impl Default for Capsule3d {
/// Returns the default [`Capsule3d`] with a radius of `0.5` and a segment length of `1.0`.
/// The total height is `2.0`.
fn default() -> Self {
Self {
radius: 0.5,
half_length: 0.5,
}
}
}

impl Capsule3d {
/// Create a new `Capsule3d` from a radius and length
pub fn new(radius: f32, length: f32) -> Self {
Expand Down Expand Up @@ -636,6 +682,16 @@ pub struct Torus {
}
impl Primitive3d for Torus {}

impl Default for Torus {
/// Returns the default [`Torus`] with a minor radius of `0.25` and a major radius of `0.75`.
fn default() -> Self {
Self {
minor_radius: 0.25,
major_radius: 0.75,
}
}
}

impl Torus {
/// Create a new `Torus` from an inner and outer radius.
///
Expand Down
Loading

0 comments on commit cf15e6b

Please sign in to comment.