Open DasLixou opened 1 month ago
Billboard Gizmo by definition is 2d so if we take depth into account and spawn multiple shapes, how we should sort and render them? No matter how many n-dimensional shape is, it still projected to 2d screen or 3d hologram, but illusion breaks when this objects interact and we can see depth.
What if camera is inside sphere ?
What if we have two spheres with same position, but different radius ?
IMO good way to represent 3d sphere is some kind of grid like globus.
Interesting idea. What I've thought about was something like this: so just outline them. the outline would also be approximately as far away in depth perspective as the sphere itself so a triangle facing towards the camera at the point of the sphere should be enough. Question is how to render the outline. As seen in the distorted fov thing, most of them are weird ellipses. No clue how to go from position and projection matrix to them.
Rendering of outlines have multiple solutions, but IMO its out of scope of this thread. If camera is close to sphere surface 2d billboard will be hidden by horizon.
Well.. it's more or less what I need, so not really that out of scope. I have a circle collider without a mesh and want to see where my mouse must be in order to interact with it, to share my usecase.
@DasLixou
I am a bit confused. You have a circle collider in a 3D world that collides with your mouse? The mouse position is inherently 2D (might be viewport or ndc though) and so are circles. Either way, I think that a billboard might be "overkill" (there are subtleties involved) here.
I suggest this
/// We'll need a custom Gizmo group for our 2D overlays
#[derive(Default, Reflect, GizmoConfigGroup)]
pub struct ScreenGizmoGroup;
fn spawn_screen_overlay_camera(mut commands: Commands, mut config_store: ResMut<GizmoConfigStore>) {
// This spawns a 2D Camera which by default renders to the primary window (there's a
// 'RenderTarget' Component in here somewhere). By using a 'Camera::order' higher than
// that of your 3D Camera this renders on top
commands.spawn((
Camera2dBundle {
camera: Camera {
// Make sure this is at least > 0 to render on top of 3D world.
// If you already use Camera Ordering, you probably already
// know how to adapt this for your project anyway.
order: 1,
..Default::default()
},
..Default::default()
},
// We don't want this to render 3D world objects, because that would be... janky at best. So we don't.
RenderLayers::layer(1),
));
let config = config_store.config_mut::<ScreenGizmoGroup>().0;
// This makes our Gizmo group render to the 2D camera we just spawned.
config.render_layers = RenderLayers::layer(1);
}
fn draw_screen_overlays(mut gizmos: Gizmos<ScreenGizmoGroup>) {
// Normally this doesn't work in 3D because the implementation is... odd.
// Using the 'ScreenGizmoGroup' we defined earlier makes this render to our
// 2D Camera which makes it work.
// Using 'Gizmos<ScreenGizmoGroup>' you can actually render all of the 2D gizmos.
gizmos.circle_2d(Vec2::ZERO, 25., bevy::color::palettes::basic::GREEN);
}
fn main() -> AppExit {
App::new()
.add_systems(Startup, spawn_screen_overlay_camera)
.add_systems(Update, draw_screen_overlays)
// --snip--
.run()
}
I intentionally omitted imports and probably everything that makes the code worthwhile (or even compile). I hope this demonstrates the technique enough for you to work with it.
I think the actual problem here is not the lack of billboard gizmo primitives, but the way the 2D gizmos are implemented.
As an example, Gizmos::circle_2d
is very roughly implemented like this
fn circle_2d(/* ... */) {
let positions = points_on_circle();
self.gizmos.linestrip_2d(positions, /* ... */);
}
fn linestrip_2d(/* ... */) {
// Behold! It's actually the 3D machinery!
self.linestrip(positions.into_iter().map(|vec2| vec2.extend(0.)), color);
}
This means that 2D Gizmos are actually rendered in a 3D world, as 3D Gizmos in the XY plane. This almost certainly violates the principle of least surprise and I can't find this behavior in documentation!
Oh, and if you have the coordinates of your mouse and still need world coordinates, or the opposite, or anything:
There's conversion methods on the Camera
type from bevy_render
(it's also included in the bevy::prelude
).
I know, I think you misunderstood my problem. Let me just.. quickly... yep, there it is: The problem is that with my mouse cursor being over the x, it is still over the 3d sphere (see the circle around the y axis underneath), but it isn't clearly visible where the exact border would be.
The outline would be something like this And that is what I want to visualize.
fn draw_sphere_outline(
mut gizmos: Gizmos,
camera: Query<&Transform, With<RelevantCamera>>
) {
let towards_camera = camera.single().translation - position_of_your_sphere;
gizmos.circle(
position_of_your_sphere,
Dir3::new(towards_camera).unwrap(),
// Making it a bit larger makes it easier to see
radius_of_your_sphere * 1.05,
RED,
);
}
It's not perfect but should be good enough
What problem does this solve or what need does it fill?
The current gizmos for circles/spheres are pretty hard to correctly see/interpret.
What solution would you like?
Add a billboard 3d sphere that is kind of like a 2d circle gizmo that looks at the camera but that it really outlines the sphere and respects fov distortion, scale, etc.
What alternative(s) have you considered?
Use the current 3d gizmo.
Additional context
Not sure how hard this would be to implement and what the best strategy would be. Maybe a real sphere with a custom shader that just renders at the "edges"?