-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Drawing Primitives with Gizmos #11072
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
Drawing Primitives with Gizmos #11072
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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! |
2ebe4d1
to
de7aa29
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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! 🥲 |
Authored-by: RobWalt <[email protected]>
Authored-by: RobWalt <[email protected]>
Fix code after rebasing on main. The capsule primitive was split for 2d and 3d cases. Authored-by: RobWalt <[email protected]>
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 <[email protected]>
Authored-by: RobWalt <[email protected]>
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 <[email protected]>
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 <[email protected]> Authored-by: RobWalt <[email protected]>
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 <[email protected]>
Authored-by: RobWalt <[email protected]>
Authored-by: RobWalt <[email protected]>
Authored-by: RobWalt <[email protected]>
Authored-by: RobWalt <[email protected]>
Authored-by: RobWalt <[email protected]>
9822690
to
d3df91f
Compare
|
||
// helpers - affine transform | ||
|
||
pub fn rotate_then_translate_2d(angle: f32, position: Vec2) -> impl Fn(Vec2) -> Vec2 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would really like docs for these public methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 <[email protected]>
# 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 <[email protected]>
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:
Default
and derive other useful traits for the new structsObjective
bevy_math
in immediate mode with gizmosSolution
Caveats
Primitive(2/3)d
impls forDirection(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
Primitive2d
forDirection2d
and implPrimitive3d
forDirection3d
GizmoPrimitive2d
for all the 2D primitivesGizmoPrimitive3d
for all the 3D primitives