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 Meshable trait and implement meshing for 2D primitives #11431

Merged
merged 25 commits into from
Jan 29, 2024

Conversation

Jondolf
Copy link
Contributor

@Jondolf Jondolf commented Jan 20, 2024

Objective

The first part of #10569, split up from #11007.

The goal is to implement meshing support for Bevy's new geometric primitives, starting with 2D primitives. 3D meshing will be added in a follow-up, and we can consider removing the old mesh shapes completely.

Solution

Add a Meshable trait that primitives need to implement to support meshing, as suggested by the RFC.

/// A trait for shapes that can be turned into a [`Mesh`].
pub trait Meshable {
    /// The output of [`Self::mesh`]. This can either be a [`Mesh`]
    /// or a builder used for creating a [`Mesh`].
    type Output;

    /// Creates a [`Mesh`] for a shape.
    fn mesh(&self) -> Self::Output;
}

This PR implements it for the following primitives:

  • Circle
  • Ellipse
  • Rectangle
  • RegularPolygon
  • Triangle2d

The mesh method typically returns a builder-like struct such as CircleMeshBuilder. This is needed to support shape-specific configuration for things like mesh resolution or UV configuration:

meshes.add(Circle { radius: 0.5 }.mesh().resolution(64));

Note that if no configuration is needed, you can even skip calling mesh because From<MyPrimitive> is implemented for Mesh:

meshes.add(Circle { radius: 0.5 });

I also updated the 2d_shapes example to use primitives, and tweaked the colors to have better contrast against the dark background.

Before:

Old 2D shapes

After:

New 2D shapes

Here you can see the UVs and different facing directions: (taken from #11007, so excuse the 3D primitives at the bottom left)

UVs and facing directions


Changelog

  • Added bevy_render::mesh::primitives module
  • Added Meshable trait and implemented it for:
    • Circle
    • Ellipse
    • Rectangle
    • RegularPolygon
    • Triangle2d
  • Implemented Default and Copy for several 2D primitives
  • Updated 2d_shapes example to use primitives
  • Tweaked colors in 2d_shapes example to have better contrast against the (new-ish) dark background

@Jondolf Jondolf added C-Enhancement A new feature A-Rendering Drawing game state to the screen and removed A-Rendering Drawing game state to the screen labels Jan 20, 2024
@Jondolf
Copy link
Contributor Author

Jondolf commented Jan 20, 2024

I think we should also consider re-exporting the primitives so that you don't need primitives::foo everywhere. They're meant to be used all over the place, for e.g. meshes, bounding volumes, and eventually gizmos and colliders, so I think having them in the prelude is justified.

I'll probably make a separate PR for this now

Copy link
Contributor

The generated examples/README.md is out of sync with the example metadata in Cargo.toml or the example readme template. Please run cargo run -p build-templated-pages -- update examples to update it, and commit the file change.

examples/README.md Outdated Show resolved Hide resolved
@alice-i-cecile
Copy link
Member

@ManevilleF has done similar things in hexx for hexagonal meshes: could you give us a review?

Jondolf and others added 2 commits January 20, 2024 18:04
@Jondolf Jondolf mentioned this pull request Jan 20, 2024
46 tasks
github-merge-queue bot pushed a commit that referenced this pull request Jan 20, 2024
# Objective

Currently, the `primitives` module is inside of the prelude for
`bevy_math`, but the actual primitives are not. This requires either
importing the shapes everywhere that uses them, or adding the
`primitives::` prefix:

```rust
let rectangle = meshes.add(primitives::Rectangle::new(5.0, 2.5));
```

(Note: meshing isn't actually implemented yet, but it's in #11431)

The primitives are meant to be used for a variety of tasks across
several crates, like for meshing, bounding volumes, gizmos, colliders,
and so on, so I think having them in the prelude is justified. It would
make several common tasks a lot more ergonomic.

```rust
let rectangle = meshes.add(Rectangle::new(5.0, 2.5));
```

## Solution

Add `primitives::*` to `bevy_math::prelude`.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
@ManevilleF
Copy link
Contributor

ManevilleF commented Jan 21, 2024

Basically my suggestion is that those primitive mesh builders output a Mesh, which has generic methods like facing, rotated, offsetted, scaled, or whatever.

So the builder only has to think about building a Mesh, and not care about transformation specifics, as it will simply call the appropriate Mesh methods.

For example, all hexx mesh builders output a MeshInfo and their facing() or offset() method directly call the MeshInfo methods

@Jondolf
Copy link
Contributor Author

Jondolf commented Jan 21, 2024

Gotcha, makes sense. I already made PRs to add transformation methods (#11454) and merging support (#11456) for Mesh directly since they're useful to have anyway. It might be a bit expensive compared to a separate MeshInfo/MeshData struct though, so I'll probably add that too

@ManevilleF
Copy link
Contributor

I don't know if an extra struct is needed, I have one in hexx because bevy is not a dependency of the lib

@Jondolf
Copy link
Contributor Author

Jondolf commented Jan 22, 2024

I think it would actually make sense to just remove the stuff related to facing for now and to split it up into a future PR. It's a feature that doesn't exist for the old/current shapes, so it's probably not needed for initial primitive meshing support either.

Moving facing things to a follow-up PR might make the changes in this PR more trivial and easier to review, which would help us move forward with meshing support quicker. Some 3D shapes might end up havIng a bit more code duplication (because e.g. Circle can't be reused), but that's also the way the current shapes work, so it wouldn't be a regression in that sense.

@alice-i-cecile
Copy link
Member

Yep, let's split that out!

@Jondolf
Copy link
Contributor Author

Jondolf commented Jan 22, 2024

Alright, I've made some changes:

  • Removed Facing and other code related to transformations.
  • Removed builder structs from RegularPolygon, Rectangle, and Triangle2d; without facing they only store the shape, so it makes more sense to just return a Mesh directly.
  • Moved all 2D meshing to a single dim2 file for now. This is more consistent with primitive code in other crates (e.g. bevy_math::primitives::dim2) and some files were only about 30 lines of code when the impls were separate. We can always split this up as needed later on.

One extra thing to note is that Bevy's "old" Quad mesh has a flip property for flipping the UVs horizontally. I didn't add this to Rectangle yet because (1) I think you could just flip the texture in some other way, and (2) I'm not sure why only quads would support this and not other meshes. I could add it if it's actually valuable and I'm missing something though

Copy link
Contributor

@jdm jdm left a comment

Choose a reason for hiding this comment

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

This PR is now pretty straightforward, and I like the common interface that's exposed.

@alice-i-cecile alice-i-cecile added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Jan 28, 2024
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Jan 29, 2024
Merged via the queue into bevyengine:main with commit 2bf481c Jan 29, 2024
25 of 26 checks passed
@Jondolf Jondolf deleted the 2d-primitive-meshing branch January 29, 2024 17:33
github-merge-queue bot pushed a commit that referenced this pull request Jan 29, 2024
# Objective

Currently, the `Capsule` primitive is technically dimension-agnostic in
that it implements both `Primitive2d` and `Primitive3d`. This seems good
on paper, but it can often be useful to have separate 2D and 3D versions
of primitives.

For example, one might want a two-dimensional capsule mesh. We can't
really implement both 2D and 3D meshing for the same type using the
upcoming `Meshable` trait (see #11431). We also currently don't
implement `Bounded2d` for `Capsule`, see
#11336 (comment).

Having 2D and 3D separate at a type level is more explicit, and also
more consistent with the existing primitives, as there are no other
types that implement both `Primitive2d` and `Primitive3d` at the same
time.

## Solution

Rename `Capsule` to `Capsule3d` and add `Capsule2d`. `Capsule2d`
implements `Bounded2d`.

For now, I went for `Capsule2d` for the sake of consistency and clarity.
Mathematically the more accurate term would be `Stadium` or `Pill` (see
[Wikipedia](https://en.wikipedia.org/wiki/Stadium_(geometry))), but
those might be less obvious to game devs. For reference, Godot has
[`CapsuleShape2D`](https://docs.godotengine.org/en/stable/classes/class_capsuleshape2d.html).
I can rename it if others think the geometrically correct name is better
though.

---

## Changelog

- Renamed `Capsule` to `Capsule3d`
- Added `Capsule2d` with `Bounded2d` implemented

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
tjamaan pushed a commit to tjamaan/bevy that referenced this pull request Feb 6, 2024
…ine#11431)

# Objective

The first part of bevyengine#10569, split up from bevyengine#11007.

The goal is to implement meshing support for Bevy's new geometric
primitives, starting with 2D primitives. 3D meshing will be added in a
follow-up, and we can consider removing the old mesh shapes completely.

## Solution

Add a `Meshable` trait that primitives need to implement to support
meshing, as suggested by the
[RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/12-primitive-shapes.md#meshing).

```rust
/// A trait for shapes that can be turned into a [`Mesh`].
pub trait Meshable {
    /// The output of [`Self::mesh`]. This can either be a [`Mesh`]
    /// or a builder used for creating a [`Mesh`].
    type Output;

    /// Creates a [`Mesh`] for a shape.
    fn mesh(&self) -> Self::Output;
}
```

This PR implements it for the following primitives:

- `Circle`
- `Ellipse`
- `Rectangle`
- `RegularPolygon`
- `Triangle2d`

The `mesh` method typically returns a builder-like struct such as
`CircleMeshBuilder`. This is needed to support shape-specific
configuration for things like mesh resolution or UV configuration:

```rust
meshes.add(Circle { radius: 0.5 }.mesh().resolution(64));
```

Note that if no configuration is needed, you can even skip calling
`mesh` because `From<MyPrimitive>` is implemented for `Mesh`:

```rust
meshes.add(Circle { radius: 0.5 });
```

I also updated the `2d_shapes` example to use primitives, and tweaked
the colors to have better contrast against the dark background.

Before:

![Old 2D
shapes](https://github.com/bevyengine/bevy/assets/57632562/f1d8c2d5-55be-495f-8ed4-5890154b81ca)

After:

![New 2D
shapes](https://github.com/bevyengine/bevy/assets/57632562/f166c013-34b8-4752-800a-5517b284d978)

Here you can see the UVs and different facing directions: (taken from
bevyengine#11007, so excuse the 3D primitives at the bottom left)

![UVs and facing
directions](https://github.com/bevyengine/bevy/assets/57632562/eaf0be4e-187d-4b6d-8fb8-c996ba295a8a)

---

## Changelog

- Added `bevy_render::mesh::primitives` module
- Added `Meshable` trait and implemented it for:
  - `Circle`
  - `Ellipse`
  - `Rectangle`
  - `RegularPolygon`
  - `Triangle2d`
- Implemented `Default` and `Copy` for several 2D primitives
- Updated `2d_shapes` example to use primitives
- Tweaked colors in `2d_shapes` example to have better contrast against
the (new-ish) dark background

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
tjamaan pushed a commit to tjamaan/bevy that referenced this pull request Feb 6, 2024
# Objective

Currently, the `Capsule` primitive is technically dimension-agnostic in
that it implements both `Primitive2d` and `Primitive3d`. This seems good
on paper, but it can often be useful to have separate 2D and 3D versions
of primitives.

For example, one might want a two-dimensional capsule mesh. We can't
really implement both 2D and 3D meshing for the same type using the
upcoming `Meshable` trait (see bevyengine#11431). We also currently don't
implement `Bounded2d` for `Capsule`, see
bevyengine#11336 (comment).

Having 2D and 3D separate at a type level is more explicit, and also
more consistent with the existing primitives, as there are no other
types that implement both `Primitive2d` and `Primitive3d` at the same
time.

## Solution

Rename `Capsule` to `Capsule3d` and add `Capsule2d`. `Capsule2d`
implements `Bounded2d`.

For now, I went for `Capsule2d` for the sake of consistency and clarity.
Mathematically the more accurate term would be `Stadium` or `Pill` (see
[Wikipedia](https://en.wikipedia.org/wiki/Stadium_(geometry))), but
those might be less obvious to game devs. For reference, Godot has
[`CapsuleShape2D`](https://docs.godotengine.org/en/stable/classes/class_capsuleshape2d.html).
I can rename it if others think the geometrically correct name is better
though.

---

## Changelog

- Renamed `Capsule` to `Capsule3d`
- Added `Capsule2d` with `Bounded2d` implemented

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
github-merge-queue bot pushed a commit that referenced this pull request Feb 8, 2024
# Objective

#11431 and #11688 implemented meshing support for Bevy's new geometric
primitives. The next step is to deprecate the shapes in
`bevy_render::mesh::shape` and to later remove them completely for 0.14.

## Solution

Deprecate the shapes and reduce code duplication by utilizing the
primitive meshing API for the old shapes where possible.

Note that some shapes have behavior that can't be exactly reproduced
with the new primitives yet:

- `Box` is more of an AABB with min/max extents
- `Plane` supports a subdivision count
- `Quad` has a `flipped` property

These types have not been changed to utilize the new primitives yet.

---

## Changelog

- Deprecated all shapes in `bevy_render::mesh::shape`
- Changed all examples to use new primitives for meshing

## Migration Guide

Bevy has previously used rendering-specific types like `UVSphere` and
`Quad` for primitive mesh shapes. These have now been deprecated to use
the geometric primitives newly introduced in version 0.13.

Some examples:

```rust
let before = meshes.add(shape::Box::new(5.0, 0.15, 5.0));
let after = meshes.add(Cuboid::new(5.0, 0.15, 5.0));

let before = meshes.add(shape::Quad::default());
let after = meshes.add(Rectangle::default());

let before = meshes.add(shape::Plane::from_size(5.0));
// The surface normal can now also be specified when using `new`
let after = meshes.add(Plane3d::default().mesh().size(5.0, 5.0));

let before = meshes.add(
    Mesh::try_from(shape::Icosphere {
        radius: 0.5,
        subdivisions: 5,
    })
    .unwrap(),
);
let after = meshes.add(Sphere::new(0.5).mesh().ico(5).unwrap());
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Rendering Drawing game state to the screen C-Enhancement A new feature S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants