Drawing Primitives with Gizmos#11072
Drawing Primitives with Gizmos#11072alice-i-cecile merged 38 commits intobevyengine:mainfrom JasmineLowen:feat/primitive-gizmos
Conversation
|
This PR currently includes some other general changes to Gizmos which could be factored out. They are:
The prior was pretty straight-forward. For the latter, here are some references how other game engines implement Arc3d, which I used for orientation:
|
|
Some general notes for reviewers:
@Jondolf Can you take a look over this and review it if you find some time? |
|
I think the 3D arc should be in a separate PR since it's new functionality that isn't related to the primitives, which are the core focus of this PR. But the ellipse might be fine since it's needed for the primitives too. I responded about the API in the issue and added a proposal for an alternative: #10571 (comment). I felt like it'd be too long and slightly off-topic to post here. |
atlv24
left a comment
There was a problem hiding this comment.
please remove flake.lock, flake.nix, and template.nix
|
@rodolphito I will when this branch is ready. Atm I'm facing the problem described in #10571 (comment) which blocks this here from a merge anyways. |
|
Resolved the last issue with this and also removed the nix files |
|
I'm wondering if Self::Output<'_> is 2018 idioms.. |
|
Just so I don't forget: This needs a rebase on this #11540 and some adjustments because of it. I also noticed that some general Gizmo things changed. So this also needs a rebase on main first! |
Jondolf
left a comment
There was a problem hiding this comment.
Looks pretty good overall, but I'm not a huge fan of having the position, rotation, and especially color configured via builders. In my opinion, the API should be:
gizmos.primitive_2d(primitive, translation, rotation, color).builder_stuff();I can't think of many cases where I'd want to always draw something at the origin with gizmos, and primitives by themselves don't have any position or rotation associated with them. The color should also be given explicitly, as assuming a default color for gizmos is not a good idea in my opinion.
Using a builder pattern for the positioning and color is also inconsistent: all existing gizmos have color as the last argument(s), and they all have some kind of position as an argument.
The current names for the builders are also inconsistent: there's center, start_position, normal_position, translation, position... The user basically needs to guess what the name is for each primitive. I would unify them all to be just translation (as an argument to the gizmo method), as it simply translates the primitive relative to the origin.
One argument against this is that we might want to define e.g. line segments with one endpoint at the start position instead of having the center of the line segment be at the origin, but... do we? If you need that representation, you could just use gizmos.line_2d(...). All primitives are currently assumed to be centered at the origin, and I'm not sure if we want to break that assumption for gizmos.
The primitives.rs gizmo file is also getting really big now; I would split it into primitives2d and primitives3d modules, or a primitives module with dim2 and dim3 modules.
crates/bevy_gizmos/src/primitives.rs
Outdated
| /// Builder for configuring the drawing options of [`Ellipse`]. | ||
| pub struct Ellipse2dBuilder<'a, 'w, 's, T: GizmoConfigGroup> { | ||
| gizmos: &'a mut Gizmos<'w, 's, T>, | ||
|
|
||
| half_width: f32, // Half-width of the ellipse | ||
| half_height: f32, // Half-height of the ellipse | ||
|
|
||
| center: Vec2, // Position of the center of the ellipse | ||
| color: Color, // Color of the ellipse | ||
| } |
There was a problem hiding this comment.
Ellipses should support rotation like other primitives. Imo every primitive (even the ones with a direction or normal) should be rotatable, as they don't have an inherent rotation associated with them
|
Another reason why having Example use case: bevy_xpbd has a physics debug rendering pipeline. I could convert each collider to a primitive if possible, and call the primitive gizmo method for collider visualization. If I can't do this with generics, each primitive type must be handled individually. Same for the color, it must be an argument, otherwise every type needs special casing. |
|
TLDR of what I'd personally change:
If you want to keep support for e.g. segments starting from a point instead of being centered at the origin, you could add that as a configuration option. But imo all primitives should be centered at the origin by default. Sorry if this is a lot 😅 and feel free to disagree! This is just what I would find to be the most useful and consistent API |
# Objective - Implement an arc3d API for gizmos - Solves #11536 ## Solution ### `arc_3d` - The current `arc3d` method on gizmos only takes an angle - It draws an "standard arc" by default, this is an arc starting at `Vec3::X`, in the XZ plane, in counter clockwise direction with a normal that is facing up - The "standard arc" can be customized with the usual gizmo builder pattern. This way you'll be able to draw arbitrary arcs ### `short/long_arc_3d_between` - Given `center`, `from`, `to` draws an arc between `from` and `to` --- ## Changelog > This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section. - Added: `Gizmos::arc3d(&mut self, angle)` method - Added: `Gizmos::long_arc_3d_between(&mut self, center, from, to)` method - Added: `Gizmos::short_arc_3d_between(&mut self, center, from, to)` method --- This PR factors out an orthogonal part of another PR as mentioned in [this comment](#11072 (comment))
|
Makes sense! I implemented everything as requested. Should be more in line with the rest of the gizmo code. Another nice side effect of these changes is that the code is much "simpler". Less code = easier to maintain, so yay 🎉 The only thing that I didn't use from the review is the Thanks for the review 😁 👍🏼 |
|
I suggested I made a slightly stripped down version of the 2024-01-28.14-36-56.mp4The primitives look good, but I see two things that I consider to be issues. Firstly, I think the primitives (capsule, cylinder, cone, and conical frustum) should be upright instead of facing Z. I asked about this on the Discord, and heard that gizmos should typically face negative Z, but I think we agreed that it's reasonable for these primitives to be upright. The primitive meshes and colliders would most certainly be upright, so making gizmos differ from this is probably not a good idea. It's also just intuitive in my opinion; if I draw a cone with no rotation, I'd expect it to be upright like a traffic cone, not pointing towards Z or negative Z. The names of the properties of the primitives also suggest this: e.g. cylinders and cones currently have a Secondly, in my opinion, cones and conical frusta should not start at the base, but instead be centered on the origin like other primitives (i.e. the center of the cone is at the geometric center, half-way between the tip and base). Cylinders, conical frusta, and cones can be treated as different versions of the same shape: many engines only have a conical frustum (but call it a cylinder), and cylinders and cones are just a special case of this. They should all be positioned similarly, centered at the origin, like other primitives. For reference, Godot has conical frusta (called cylinder) centered like this for both the mesh and collider, Parry has cone and cylinder colliders centered, and Blender also centers cylinders (but doesn't seem to have two radii for them). Side note: The gizmo examples cycle through primitives with the Up/Down arrow keys, but the text overlays don't say this. |
|
Good points 👍🏼 I'll tackle these asap. The upright thing makes total sense and I already thought about if I should make that change. I used Z up mainly for my mental model while developing since I'm used to it from other applications using mainly 2D structures with an optional Z coordinate in specific cases. As for the centering: That's also reasonable to further unify implicit assumptions, so cool. I'll also add UI text telling the example users how to navigate the 3D gizmos. |
Jondolf
left a comment
There was a problem hiding this comment.
Looking pretty good now! I have some final nits and fixes, mainly around lines and some comments.
You should probably also add the primitive gizmo traits to the bevy_gizmos prelude so that primitive_2d and primitive_3d work without having to explicitly import traits:
/// The `bevy_gizmos` prelude.
pub mod prelude {
#[doc(hidden)]
pub use crate::{
aabb::{AabbGizmoConfigGroup, ShowAabbGizmo},
config::{DefaultGizmoConfigGroup, GizmoConfig, GizmoConfigGroup, GizmoConfigStore},
gizmos::Gizmos,
// This
primitives::{dim2::GizmoPrimitive2d, dim3::GizmoPrimitive3d},
AppGizmoBuilder,
};
}|
FYI, It should probably be added as a 2D gizmo too, it'd just be two semicircles and lines to connect them |
Yeah I saw that. Solid addition btw! Really cool seeing the primitives evolve. They grow up so fast! 🥲 |
|
@Jondolf Examples are updated now and you can take a look at most primitives (except for the polygon ones) |
Authored-by: RobWalt <robwalter96@gmail.com>
Authored-by: RobWalt <robwalter96@gmail.com>
Authored-by: RobWalt <robwalter96@gmail.com>
Fix code after rebasing on main. The capsule primitive was split for 2d and 3d cases. Authored-by: RobWalt <robwalter96@gmail.com>
This includes: - just drawing one line for line segements (2D/3D) - use minimum length for directions in 2D to prevent 1 pixel lines - now: 50 pixel - changing the infinit-ness of lines by adding big constant scale factors now: - 2D infinite length: 100_000 - 3D infinite length: 10_000 - refactor plane (2D/3D) to use other primitives for simplicity and less repetition (they now use a combination of Direction + Segement rendering) - add configurable options to Plane3d (number + detail + size of hinting axis) - adjust examples in 2D/3D cases - use consistent variable names - clean up docs
Authored-by: RobWalt <robwalter96@gmail.com>
Authored-by: RobWalt <robwalter96@gmail.com>
The examples should probably be cleaner than the previous state. After all people are learning with these so we should implement best practices and keep systems small and decoupled. Authored-by: RobWalt <robwalter96@gmail.com>
This commit implements a loose bunch of small polishing fixes: - use angles in radians instead of `Mat2` for rotation in 2D primitive case and ellipse case - use constants on `Direction2D` struct as much as possible - update key bindings in examples to be consistent - update user facing text in examples to reflect real key bindings - fix naming on configurating builder for `Plane3D` sorry should have added you earlier below 🙈 Reviewed-by: Joona Aalto <jondolf.dev@gmail.com> Authored-by: RobWalt <robwalter96@gmail.com>
Since a `half_size` `Vec2` was used on a lot of primitives lately I thought that we should be consistent here with ellipses as well. Authored-by: RobWalt <robwalter96@gmail.com>
Authored-by: RobWalt <robwalter96@gmail.com>
Authored-by: RobWalt <robwalter96@gmail.com>
Authored-by: RobWalt <robwalter96@gmail.com>
Authored-by: RobWalt <robwalter96@gmail.com>
Authored-by: RobWalt <robwalter96@gmail.com>
|
|
||
| // helpers - affine transform | ||
|
|
||
| pub fn rotate_then_translate_2d(angle: f32, position: Vec2) -> impl Fn(Vec2) -> Vec2 { |
There was a problem hiding this comment.
I would really like docs for these public methods.
There was a problem hiding this comment.
Good catch 🎯 They are exposed by the pub(crate) helper module but I guess I should've made them pub(crate) themselves as well. That's easier to read for the bevy devs which just jump there and are confused why they can't access it elsewhere.
I did some documentation there as well anyways
alice-i-cecile
left a comment
There was a problem hiding this comment.
A ton of code, but very straightforward. A nice demonstration of the utility of our new primitives.
I would like aggressive documentation, but it's not so bad that it can't be fixed in follow-up.
|
Leaving this until Monday's merge train: if you have time to write docs this weekend, great! But I'm comfortable merging this either way. |
Authored-by: Aviac <aviac@mailbox.org>
# Objective - Implement an arc3d API for gizmos - Solves bevyengine#11536 ## Solution ### `arc_3d` - The current `arc3d` method on gizmos only takes an angle - It draws an "standard arc" by default, this is an arc starting at `Vec3::X`, in the XZ plane, in counter clockwise direction with a normal that is facing up - The "standard arc" can be customized with the usual gizmo builder pattern. This way you'll be able to draw arbitrary arcs ### `short/long_arc_3d_between` - Given `center`, `from`, `to` draws an arc between `from` and `to` --- ## Changelog > This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section. - Added: `Gizmos::arc3d(&mut self, angle)` method - Added: `Gizmos::long_arc_3d_between(&mut self, center, from, to)` method - Added: `Gizmos::short_arc_3d_between(&mut self, center, from, to)` method --- This PR factors out an orthogonal part of another PR as mentioned in [this comment](bevyengine#11072 (comment))
The PR is in a reviewable state now in the sense that the basic implementations are there. There are still some ToDos that I'm aware of: - [x] docs for all the new structs and traits - [x] implement `Default` and derive other useful traits for the new structs - [x] Take a look at the notes again (Do this after a first round of reviews) - [x] Take care of the repetition in the circle drawing functions --- # Objective - TLDR: This PR enables us to quickly draw all the newly added primitives from `bevy_math` in immediate mode with gizmos - Addresses bevyengine#10571 ## Solution - This implements the first design idea I had that covered everything that was mentioned in the Issue bevyengine#10571 (comment) --- ## Caveats - I added the `Primitive(2/3)d` impls for `Direction(2/3)d` to make them work with the current solution. We could impose less strict requirements for the gizmoable objects and remove the impls afterwards if the community doesn't like the current approach. --- ## Changelog - implement capabilities to draw ellipses on the gizmo in general (this was required to have some code which is able to draw the ellipse primitive) - refactored circle drawing code to use the more general ellipse drawing code to keep code duplication low - implement `Primitive2d` for `Direction2d` and impl `Primitive3d` for `Direction3d` - implement trait to draw primitives with specialized details with gizmos - `GizmoPrimitive2d` for all the 2D primitives - `GizmoPrimitive3d` for all the 3D primitives - (question while writing this: Does it actually matter if we split this in 2D and 3D? I guess it could be useful in the future if we do something based on the main rendering mode even though atm it's kinda useless) --- --------- Co-authored-by: nothendev <borodinov.ilya@gmail.com>
The PR is in a reviewable state now in the sense that the basic implementations are there. There are still some ToDos that I'm aware of:
Defaultand derive other useful traits for the new structsObjective
bevy_mathin immediate mode with gizmosSolution
Caveats
Primitive(2/3)dimpls forDirection(2/3)dto make them work with the current solution. We could impose less strict requirements for the gizmoable objects and remove the impls afterwards if the community doesn't like the current approach.Changelog
Primitive2dforDirection2dand implPrimitive3dforDirection3dGizmoPrimitive2dfor all the 2D primitivesGizmoPrimitive3dfor all the 3D primitives