Retained `Gizmo`s (#15473)
# Objective
Add a way to use the gizmo API in a retained manner, for increased
performance.
## Solution
- Move gizmo API from `Gizmos` to `GizmoBuffer`, ~ab~using `Deref` to
keep usage the same as before.
- Merge non-strip and strip variant of `LineGizmo` into one, storing the
data in a `GizmoBuffer` to have the same API for retained `LineGizmo`s.
### Review guide
- The meat of the changes are in `lib.rs`, `retained.rs`, `gizmos.rs`,
`pipeline_3d.rs` and `pipeline_2d.rs`
- The other files contain almost exclusively the churn from moving the
gizmo API from `Gizmos` to `GizmoBuffer`
## Testing
### Performance
Performance compared to the immediate mode API is from 65 to 80 times
better for static lines.
```
7900 XTX, 3700X
1707.9k lines/ms: gizmos_retained (21.3ms)
3488.5k lines/ms: gizmos_retained_continuous_polyline (31.3ms)
0.5k lines/ms: gizmos_retained_separate (97.7ms)
3054.9k lines/ms: bevy_polyline_retained_nan (16.8ms)
3596.3k lines/ms: bevy_polyline_retained_continuous_polyline (14.2ms)
0.6k lines/ms: bevy_polyline_retained_separate (78.9ms)
26.9k lines/ms: gizmos_immediate (14.9ms)
43.8k lines/ms: gizmos_immediate_continuous_polyline (18.3ms)
```
Looks like performance is good enough, being close to par with
`bevy_polyline`.
Benchmarks can be found here:
This branch:
https://github.com/tim-blackbird/line_racing/tree/retained-gizmos
Bevy 0.14: https://github.com/DGriffin91/line_racing
## Showcase
```rust
fn setup(
mut commands: Commands,
mut gizmo_assets: ResMut<Assets<GizmoAsset>>
) {
let mut gizmo = GizmoAsset::default();
// A sphere made out of one million lines!
gizmo
.sphere(default(), 1., CRIMSON)
.resolution(1_000_000 / 3);
commands.spawn(Gizmo {
handle: gizmo_assets.add(gizmo),
..default()
});
}
```
## Follow-up work
- Port over to the retained rendering world proper
- Calculate visibility and cull `Gizmo`s
2024-12-04 21:21:06 +00:00
|
|
|
#import bevy_render::{view::View, maths::affine3_to_square}
|
Gizmo line joints (#12252)
# Objective
- Adds gizmo line joints, suggestion of #9400
## Solution
- Adds `line_joints: GizmoLineJoint` to `GizmoConfig`. Currently the
following values are supported:
- `GizmoLineJoint::None`: does not draw line joints, same behaviour as
previously
- `GizmoLineJoint::Bevel`: draws a single triangle between the lines
- `GizmoLineJoint::Miter` / 'spiky joints': draws two triangles between
the lines extending them until they meet at a (miter) point.
- NOTE: for very small angles between the lines, which happens
frequently in 3d, the miter point will be very far away from the point
at which the lines meet.
- `GizmoLineJoint::Round(resolution)`: Draw a circle arc between the
lines. The circle is a triangle fan of `resolution` triangles.
---
## Changelog
- Added `GizmoLineJoint`, use that in `GizmoConfig` and added necessary
pipelines and draw commands.
- Added a new `line_joints.wgsl` shader containing three vertex shaders
`vertex_bevel`, `vertex_miter` and `vertex_round` as well as a basic
`fragment` shader.
## Migration Guide
Any manually created `GizmoConfig`s must now set the `.line_joints`
field.
## Known issues
- The way we currently create basic closed shapes like rectangles,
circles, triangles or really any closed 2d shape means that one of the
corners will not be drawn with joints, although that would probably be
expected. (see the triangle in the 2d image)
- This could be somewhat mitigated by introducing line caps or fixed by
adding another segment overlapping the first of the strip. (Maybe in a
followup PR?)
- 3d shapes can look 'off' with line joints (especially bevel) because
wherever 3 or more lines meet one of them may stick out beyond the joint
drawn between the other 2.
- Adding additional lines so that there is a joint between every line at
a corner would fix this but would probably be too computationally
expensive.
- Miter joints are 'unreasonably long' for very small angles between the
lines (the angle is the angle between the lines in screen space). This
is technically correct but distracting and does not feel right,
especially in 3d contexts. I think limiting the length of the miter to
the point at which the lines meet might be a good idea.
- The joints may be drawn with a different gizmo in-between them and
their corresponding lines in 2d. Some sort of z-ordering would probably
be good here, but I believe this may be out of scope for this PR.
## Additional information
Some pretty images :)
<img width="1175" alt="Screenshot 2024-03-02 at 04 53 50"
src="https://github.com/bevyengine/bevy/assets/62256001/58df7e63-9376-4430-8871-32adba0cb53b">
- Note that the top vertex does not have a joint drawn.
<img width="1440" alt="Screenshot 2024-03-02 at 05 03 55"
src="https://github.com/bevyengine/bevy/assets/62256001/137a00cf-cbd4-48c2-a46f-4b47492d4fd9">
Now for a weird video:
https://github.com/bevyengine/bevy/assets/62256001/93026f48-f1d6-46fe-9163-5ab548a3fce4
- The black lines shooting out from the cube are miter joints that get
very long because the lines between which they are drawn are (almost)
collinear in screen space.
---------
Co-authored-by: Pablo Reinhardt <126117294+pablo-lua@users.noreply.github.com>
2024-03-11 19:21:32 +00:00
|
|
|
|
|
|
|
@group(0) @binding(0) var<uniform> view: View;
|
|
|
|
|
|
|
|
|
|
|
|
struct LineGizmoUniform {
|
Retained `Gizmo`s (#15473)
# Objective
Add a way to use the gizmo API in a retained manner, for increased
performance.
## Solution
- Move gizmo API from `Gizmos` to `GizmoBuffer`, ~ab~using `Deref` to
keep usage the same as before.
- Merge non-strip and strip variant of `LineGizmo` into one, storing the
data in a `GizmoBuffer` to have the same API for retained `LineGizmo`s.
### Review guide
- The meat of the changes are in `lib.rs`, `retained.rs`, `gizmos.rs`,
`pipeline_3d.rs` and `pipeline_2d.rs`
- The other files contain almost exclusively the churn from moving the
gizmo API from `Gizmos` to `GizmoBuffer`
## Testing
### Performance
Performance compared to the immediate mode API is from 65 to 80 times
better for static lines.
```
7900 XTX, 3700X
1707.9k lines/ms: gizmos_retained (21.3ms)
3488.5k lines/ms: gizmos_retained_continuous_polyline (31.3ms)
0.5k lines/ms: gizmos_retained_separate (97.7ms)
3054.9k lines/ms: bevy_polyline_retained_nan (16.8ms)
3596.3k lines/ms: bevy_polyline_retained_continuous_polyline (14.2ms)
0.6k lines/ms: bevy_polyline_retained_separate (78.9ms)
26.9k lines/ms: gizmos_immediate (14.9ms)
43.8k lines/ms: gizmos_immediate_continuous_polyline (18.3ms)
```
Looks like performance is good enough, being close to par with
`bevy_polyline`.
Benchmarks can be found here:
This branch:
https://github.com/tim-blackbird/line_racing/tree/retained-gizmos
Bevy 0.14: https://github.com/DGriffin91/line_racing
## Showcase
```rust
fn setup(
mut commands: Commands,
mut gizmo_assets: ResMut<Assets<GizmoAsset>>
) {
let mut gizmo = GizmoAsset::default();
// A sphere made out of one million lines!
gizmo
.sphere(default(), 1., CRIMSON)
.resolution(1_000_000 / 3);
commands.spawn(Gizmo {
handle: gizmo_assets.add(gizmo),
..default()
});
}
```
## Follow-up work
- Port over to the retained rendering world proper
- Calculate visibility and cull `Gizmo`s
2024-12-04 21:21:06 +00:00
|
|
|
world_from_local: mat3x4<f32>,
|
Gizmo line joints (#12252)
# Objective
- Adds gizmo line joints, suggestion of #9400
## Solution
- Adds `line_joints: GizmoLineJoint` to `GizmoConfig`. Currently the
following values are supported:
- `GizmoLineJoint::None`: does not draw line joints, same behaviour as
previously
- `GizmoLineJoint::Bevel`: draws a single triangle between the lines
- `GizmoLineJoint::Miter` / 'spiky joints': draws two triangles between
the lines extending them until they meet at a (miter) point.
- NOTE: for very small angles between the lines, which happens
frequently in 3d, the miter point will be very far away from the point
at which the lines meet.
- `GizmoLineJoint::Round(resolution)`: Draw a circle arc between the
lines. The circle is a triangle fan of `resolution` triangles.
---
## Changelog
- Added `GizmoLineJoint`, use that in `GizmoConfig` and added necessary
pipelines and draw commands.
- Added a new `line_joints.wgsl` shader containing three vertex shaders
`vertex_bevel`, `vertex_miter` and `vertex_round` as well as a basic
`fragment` shader.
## Migration Guide
Any manually created `GizmoConfig`s must now set the `.line_joints`
field.
## Known issues
- The way we currently create basic closed shapes like rectangles,
circles, triangles or really any closed 2d shape means that one of the
corners will not be drawn with joints, although that would probably be
expected. (see the triangle in the 2d image)
- This could be somewhat mitigated by introducing line caps or fixed by
adding another segment overlapping the first of the strip. (Maybe in a
followup PR?)
- 3d shapes can look 'off' with line joints (especially bevel) because
wherever 3 or more lines meet one of them may stick out beyond the joint
drawn between the other 2.
- Adding additional lines so that there is a joint between every line at
a corner would fix this but would probably be too computationally
expensive.
- Miter joints are 'unreasonably long' for very small angles between the
lines (the angle is the angle between the lines in screen space). This
is technically correct but distracting and does not feel right,
especially in 3d contexts. I think limiting the length of the miter to
the point at which the lines meet might be a good idea.
- The joints may be drawn with a different gizmo in-between them and
their corresponding lines in 2d. Some sort of z-ordering would probably
be good here, but I believe this may be out of scope for this PR.
## Additional information
Some pretty images :)
<img width="1175" alt="Screenshot 2024-03-02 at 04 53 50"
src="https://github.com/bevyengine/bevy/assets/62256001/58df7e63-9376-4430-8871-32adba0cb53b">
- Note that the top vertex does not have a joint drawn.
<img width="1440" alt="Screenshot 2024-03-02 at 05 03 55"
src="https://github.com/bevyengine/bevy/assets/62256001/137a00cf-cbd4-48c2-a46f-4b47492d4fd9">
Now for a weird video:
https://github.com/bevyengine/bevy/assets/62256001/93026f48-f1d6-46fe-9163-5ab548a3fce4
- The black lines shooting out from the cube are miter joints that get
very long because the lines between which they are drawn are (almost)
collinear in screen space.
---------
Co-authored-by: Pablo Reinhardt <126117294+pablo-lua@users.noreply.github.com>
2024-03-11 19:21:32 +00:00
|
|
|
line_width: f32,
|
|
|
|
depth_bias: f32,
|
|
|
|
resolution: u32,
|
|
|
|
#ifdef SIXTEEN_BYTE_ALIGNMENT
|
|
|
|
// WebGL2 structs must be 16 byte aligned.
|
|
|
|
_padding: f32,
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
@group(1) @binding(0) var<uniform> joints_gizmo: LineGizmoUniform;
|
|
|
|
|
|
|
|
struct VertexInput {
|
|
|
|
@location(0) position_a: vec3<f32>,
|
|
|
|
@location(1) position_b: vec3<f32>,
|
|
|
|
@location(2) position_c: vec3<f32>,
|
|
|
|
@location(3) color: vec4<f32>,
|
|
|
|
@builtin(vertex_index) index: u32,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct VertexOutput {
|
|
|
|
@builtin(position) clip_position: vec4<f32>,
|
|
|
|
@location(0) color: vec4<f32>,
|
|
|
|
};
|
|
|
|
|
|
|
|
const EPSILON: f32 = 4.88e-04;
|
|
|
|
|
|
|
|
@vertex
|
|
|
|
fn vertex_bevel(vertex: VertexInput) -> VertexOutput {
|
|
|
|
var positions = array<vec2<f32>, 3>(
|
|
|
|
vec2(0, 0),
|
|
|
|
vec2(0, 0.5),
|
|
|
|
vec2(0.5, 0),
|
|
|
|
);
|
|
|
|
var position = positions[vertex.index];
|
|
|
|
|
Retained `Gizmo`s (#15473)
# Objective
Add a way to use the gizmo API in a retained manner, for increased
performance.
## Solution
- Move gizmo API from `Gizmos` to `GizmoBuffer`, ~ab~using `Deref` to
keep usage the same as before.
- Merge non-strip and strip variant of `LineGizmo` into one, storing the
data in a `GizmoBuffer` to have the same API for retained `LineGizmo`s.
### Review guide
- The meat of the changes are in `lib.rs`, `retained.rs`, `gizmos.rs`,
`pipeline_3d.rs` and `pipeline_2d.rs`
- The other files contain almost exclusively the churn from moving the
gizmo API from `Gizmos` to `GizmoBuffer`
## Testing
### Performance
Performance compared to the immediate mode API is from 65 to 80 times
better for static lines.
```
7900 XTX, 3700X
1707.9k lines/ms: gizmos_retained (21.3ms)
3488.5k lines/ms: gizmos_retained_continuous_polyline (31.3ms)
0.5k lines/ms: gizmos_retained_separate (97.7ms)
3054.9k lines/ms: bevy_polyline_retained_nan (16.8ms)
3596.3k lines/ms: bevy_polyline_retained_continuous_polyline (14.2ms)
0.6k lines/ms: bevy_polyline_retained_separate (78.9ms)
26.9k lines/ms: gizmos_immediate (14.9ms)
43.8k lines/ms: gizmos_immediate_continuous_polyline (18.3ms)
```
Looks like performance is good enough, being close to par with
`bevy_polyline`.
Benchmarks can be found here:
This branch:
https://github.com/tim-blackbird/line_racing/tree/retained-gizmos
Bevy 0.14: https://github.com/DGriffin91/line_racing
## Showcase
```rust
fn setup(
mut commands: Commands,
mut gizmo_assets: ResMut<Assets<GizmoAsset>>
) {
let mut gizmo = GizmoAsset::default();
// A sphere made out of one million lines!
gizmo
.sphere(default(), 1., CRIMSON)
.resolution(1_000_000 / 3);
commands.spawn(Gizmo {
handle: gizmo_assets.add(gizmo),
..default()
});
}
```
## Follow-up work
- Port over to the retained rendering world proper
- Calculate visibility and cull `Gizmo`s
2024-12-04 21:21:06 +00:00
|
|
|
let world_from_local = affine3_to_square(joints_gizmo.world_from_local);
|
|
|
|
|
|
|
|
var clip_a = view.clip_from_world * world_from_local * vec4(vertex.position_a, 1.);
|
|
|
|
var clip_b = view.clip_from_world * world_from_local * vec4(vertex.position_b, 1.);
|
|
|
|
var clip_c = view.clip_from_world * world_from_local * vec4(vertex.position_c, 1.);
|
Gizmo line joints (#12252)
# Objective
- Adds gizmo line joints, suggestion of #9400
## Solution
- Adds `line_joints: GizmoLineJoint` to `GizmoConfig`. Currently the
following values are supported:
- `GizmoLineJoint::None`: does not draw line joints, same behaviour as
previously
- `GizmoLineJoint::Bevel`: draws a single triangle between the lines
- `GizmoLineJoint::Miter` / 'spiky joints': draws two triangles between
the lines extending them until they meet at a (miter) point.
- NOTE: for very small angles between the lines, which happens
frequently in 3d, the miter point will be very far away from the point
at which the lines meet.
- `GizmoLineJoint::Round(resolution)`: Draw a circle arc between the
lines. The circle is a triangle fan of `resolution` triangles.
---
## Changelog
- Added `GizmoLineJoint`, use that in `GizmoConfig` and added necessary
pipelines and draw commands.
- Added a new `line_joints.wgsl` shader containing three vertex shaders
`vertex_bevel`, `vertex_miter` and `vertex_round` as well as a basic
`fragment` shader.
## Migration Guide
Any manually created `GizmoConfig`s must now set the `.line_joints`
field.
## Known issues
- The way we currently create basic closed shapes like rectangles,
circles, triangles or really any closed 2d shape means that one of the
corners will not be drawn with joints, although that would probably be
expected. (see the triangle in the 2d image)
- This could be somewhat mitigated by introducing line caps or fixed by
adding another segment overlapping the first of the strip. (Maybe in a
followup PR?)
- 3d shapes can look 'off' with line joints (especially bevel) because
wherever 3 or more lines meet one of them may stick out beyond the joint
drawn between the other 2.
- Adding additional lines so that there is a joint between every line at
a corner would fix this but would probably be too computationally
expensive.
- Miter joints are 'unreasonably long' for very small angles between the
lines (the angle is the angle between the lines in screen space). This
is technically correct but distracting and does not feel right,
especially in 3d contexts. I think limiting the length of the miter to
the point at which the lines meet might be a good idea.
- The joints may be drawn with a different gizmo in-between them and
their corresponding lines in 2d. Some sort of z-ordering would probably
be good here, but I believe this may be out of scope for this PR.
## Additional information
Some pretty images :)
<img width="1175" alt="Screenshot 2024-03-02 at 04 53 50"
src="https://github.com/bevyengine/bevy/assets/62256001/58df7e63-9376-4430-8871-32adba0cb53b">
- Note that the top vertex does not have a joint drawn.
<img width="1440" alt="Screenshot 2024-03-02 at 05 03 55"
src="https://github.com/bevyengine/bevy/assets/62256001/137a00cf-cbd4-48c2-a46f-4b47492d4fd9">
Now for a weird video:
https://github.com/bevyengine/bevy/assets/62256001/93026f48-f1d6-46fe-9163-5ab548a3fce4
- The black lines shooting out from the cube are miter joints that get
very long because the lines between which they are drawn are (almost)
collinear in screen space.
---------
Co-authored-by: Pablo Reinhardt <126117294+pablo-lua@users.noreply.github.com>
2024-03-11 19:21:32 +00:00
|
|
|
|
|
|
|
// Manual near plane clipping to avoid errors when doing the perspective divide inside this shader.
|
|
|
|
clip_a = clip_near_plane(clip_a, clip_c);
|
|
|
|
clip_b = clip_near_plane(clip_b, clip_a);
|
|
|
|
clip_c = clip_near_plane(clip_c, clip_b);
|
|
|
|
clip_a = clip_near_plane(clip_a, clip_c);
|
|
|
|
|
|
|
|
let resolution = view.viewport.zw;
|
|
|
|
let screen_a = resolution * (0.5 * clip_a.xy / clip_a.w + 0.5);
|
|
|
|
let screen_b = resolution * (0.5 * clip_b.xy / clip_b.w + 0.5);
|
|
|
|
let screen_c = resolution * (0.5 * clip_c.xy / clip_c.w + 0.5);
|
|
|
|
|
|
|
|
var color = vertex.color;
|
|
|
|
var line_width = joints_gizmo.line_width;
|
|
|
|
|
|
|
|
#ifdef PERSPECTIVE
|
|
|
|
line_width /= clip_b.w;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Line thinness fade from https://acegikmo.com/shapes/docs/#anti-aliasing
|
|
|
|
if line_width > 0.0 && line_width < 1. {
|
|
|
|
color.a *= line_width;
|
|
|
|
line_width = 1.;
|
|
|
|
}
|
|
|
|
|
|
|
|
let ab = normalize(screen_b - screen_a);
|
|
|
|
let cb = normalize(screen_b - screen_c);
|
|
|
|
let ab_norm = vec2(-ab.y, ab.x);
|
|
|
|
let cb_norm = vec2(cb.y, -cb.x);
|
|
|
|
let tangent = normalize(ab - cb);
|
|
|
|
let normal = vec2(-tangent.y, tangent.x);
|
|
|
|
let sigma = sign(dot(ab + cb, normal));
|
|
|
|
|
|
|
|
var p0 = line_width * sigma * ab_norm;
|
|
|
|
var p1 = line_width * sigma * cb_norm;
|
|
|
|
|
|
|
|
let screen = screen_b + position.x * p0 + position.y * p1;
|
|
|
|
|
|
|
|
let depth = depth(clip_b);
|
|
|
|
|
|
|
|
var clip_position = vec4(clip_b.w * ((2. * screen) / resolution - 1.), depth, clip_b.w);
|
|
|
|
return VertexOutput(clip_position, color);
|
|
|
|
}
|
|
|
|
|
|
|
|
@vertex
|
|
|
|
fn vertex_miter(vertex: VertexInput) -> VertexOutput {
|
|
|
|
var positions = array<vec3<f32>, 6>(
|
|
|
|
vec3(0, 0, 0),
|
|
|
|
vec3(0.5, 0, 0),
|
|
|
|
vec3(0, 0.5, 0),
|
|
|
|
vec3(0, 0, 0),
|
|
|
|
vec3(0, 0.5, 0),
|
|
|
|
vec3(0, 0, 0.5),
|
|
|
|
);
|
|
|
|
var position = positions[vertex.index];
|
Normalise matrix naming (#13489)
# Objective
- Fixes #10909
- Fixes #8492
## Solution
- Name all matrices `x_from_y`, for example `world_from_view`.
## Testing
- I've tested most of the 3D examples. The `lighting` example
particularly should hit a lot of the changes and appears to run fine.
---
## Changelog
- Renamed matrices across the engine to follow a `y_from_x` naming,
making the space conversion more obvious.
## Migration Guide
- `Frustum`'s `from_view_projection`, `from_view_projection_custom_far`
and `from_view_projection_no_far` were renamed to
`from_clip_from_world`, `from_clip_from_world_custom_far` and
`from_clip_from_world_no_far`.
- `ComputedCameraValues::projection_matrix` was renamed to
`clip_from_view`.
- `CameraProjection::get_projection_matrix` was renamed to
`get_clip_from_view` (this affects implementations on `Projection`,
`PerspectiveProjection` and `OrthographicProjection`).
- `ViewRangefinder3d::from_view_matrix` was renamed to
`from_world_from_view`.
- `PreviousViewData`'s members were renamed to `view_from_world` and
`clip_from_world`.
- `ExtractedView`'s `projection`, `transform` and `view_projection` were
renamed to `clip_from_view`, `world_from_view` and `clip_from_world`.
- `ViewUniform`'s `view_proj`, `unjittered_view_proj`,
`inverse_view_proj`, `view`, `inverse_view`, `projection` and
`inverse_projection` were renamed to `clip_from_world`,
`unjittered_clip_from_world`, `world_from_clip`, `world_from_view`,
`view_from_world`, `clip_from_view` and `view_from_clip`.
- `GpuDirectionalCascade::view_projection` was renamed to
`clip_from_world`.
- `MeshTransforms`' `transform` and `previous_transform` were renamed to
`world_from_local` and `previous_world_from_local`.
- `MeshUniform`'s `transform`, `previous_transform`,
`inverse_transpose_model_a` and `inverse_transpose_model_b` were renamed
to `world_from_local`, `previous_world_from_local`,
`local_from_world_transpose_a` and `local_from_world_transpose_b` (the
`Mesh` type in WGSL mirrors this, however `transform` and
`previous_transform` were named `model` and `previous_model`).
- `Mesh2dTransforms::transform` was renamed to `world_from_local`.
- `Mesh2dUniform`'s `transform`, `inverse_transpose_model_a` and
`inverse_transpose_model_b` were renamed to `world_from_local`,
`local_from_world_transpose_a` and `local_from_world_transpose_b` (the
`Mesh2d` type in WGSL mirrors this).
- In WGSL, in `bevy_pbr::mesh_functions`, `get_model_matrix` and
`get_previous_model_matrix` were renamed to `get_world_from_local` and
`get_previous_world_from_local`.
- In WGSL, `bevy_sprite::mesh2d_functions::get_model_matrix` was renamed
to `get_world_from_local`.
2024-06-03 16:56:53 +00:00
|
|
|
|
Retained `Gizmo`s (#15473)
# Objective
Add a way to use the gizmo API in a retained manner, for increased
performance.
## Solution
- Move gizmo API from `Gizmos` to `GizmoBuffer`, ~ab~using `Deref` to
keep usage the same as before.
- Merge non-strip and strip variant of `LineGizmo` into one, storing the
data in a `GizmoBuffer` to have the same API for retained `LineGizmo`s.
### Review guide
- The meat of the changes are in `lib.rs`, `retained.rs`, `gizmos.rs`,
`pipeline_3d.rs` and `pipeline_2d.rs`
- The other files contain almost exclusively the churn from moving the
gizmo API from `Gizmos` to `GizmoBuffer`
## Testing
### Performance
Performance compared to the immediate mode API is from 65 to 80 times
better for static lines.
```
7900 XTX, 3700X
1707.9k lines/ms: gizmos_retained (21.3ms)
3488.5k lines/ms: gizmos_retained_continuous_polyline (31.3ms)
0.5k lines/ms: gizmos_retained_separate (97.7ms)
3054.9k lines/ms: bevy_polyline_retained_nan (16.8ms)
3596.3k lines/ms: bevy_polyline_retained_continuous_polyline (14.2ms)
0.6k lines/ms: bevy_polyline_retained_separate (78.9ms)
26.9k lines/ms: gizmos_immediate (14.9ms)
43.8k lines/ms: gizmos_immediate_continuous_polyline (18.3ms)
```
Looks like performance is good enough, being close to par with
`bevy_polyline`.
Benchmarks can be found here:
This branch:
https://github.com/tim-blackbird/line_racing/tree/retained-gizmos
Bevy 0.14: https://github.com/DGriffin91/line_racing
## Showcase
```rust
fn setup(
mut commands: Commands,
mut gizmo_assets: ResMut<Assets<GizmoAsset>>
) {
let mut gizmo = GizmoAsset::default();
// A sphere made out of one million lines!
gizmo
.sphere(default(), 1., CRIMSON)
.resolution(1_000_000 / 3);
commands.spawn(Gizmo {
handle: gizmo_assets.add(gizmo),
..default()
});
}
```
## Follow-up work
- Port over to the retained rendering world proper
- Calculate visibility and cull `Gizmo`s
2024-12-04 21:21:06 +00:00
|
|
|
let world_from_local = affine3_to_square(joints_gizmo.world_from_local);
|
|
|
|
|
|
|
|
var clip_a = view.clip_from_world * world_from_local * vec4(vertex.position_a, 1.);
|
|
|
|
var clip_b = view.clip_from_world * world_from_local * vec4(vertex.position_b, 1.);
|
|
|
|
var clip_c = view.clip_from_world * world_from_local * vec4(vertex.position_c, 1.);
|
Gizmo line joints (#12252)
# Objective
- Adds gizmo line joints, suggestion of #9400
## Solution
- Adds `line_joints: GizmoLineJoint` to `GizmoConfig`. Currently the
following values are supported:
- `GizmoLineJoint::None`: does not draw line joints, same behaviour as
previously
- `GizmoLineJoint::Bevel`: draws a single triangle between the lines
- `GizmoLineJoint::Miter` / 'spiky joints': draws two triangles between
the lines extending them until they meet at a (miter) point.
- NOTE: for very small angles between the lines, which happens
frequently in 3d, the miter point will be very far away from the point
at which the lines meet.
- `GizmoLineJoint::Round(resolution)`: Draw a circle arc between the
lines. The circle is a triangle fan of `resolution` triangles.
---
## Changelog
- Added `GizmoLineJoint`, use that in `GizmoConfig` and added necessary
pipelines and draw commands.
- Added a new `line_joints.wgsl` shader containing three vertex shaders
`vertex_bevel`, `vertex_miter` and `vertex_round` as well as a basic
`fragment` shader.
## Migration Guide
Any manually created `GizmoConfig`s must now set the `.line_joints`
field.
## Known issues
- The way we currently create basic closed shapes like rectangles,
circles, triangles or really any closed 2d shape means that one of the
corners will not be drawn with joints, although that would probably be
expected. (see the triangle in the 2d image)
- This could be somewhat mitigated by introducing line caps or fixed by
adding another segment overlapping the first of the strip. (Maybe in a
followup PR?)
- 3d shapes can look 'off' with line joints (especially bevel) because
wherever 3 or more lines meet one of them may stick out beyond the joint
drawn between the other 2.
- Adding additional lines so that there is a joint between every line at
a corner would fix this but would probably be too computationally
expensive.
- Miter joints are 'unreasonably long' for very small angles between the
lines (the angle is the angle between the lines in screen space). This
is technically correct but distracting and does not feel right,
especially in 3d contexts. I think limiting the length of the miter to
the point at which the lines meet might be a good idea.
- The joints may be drawn with a different gizmo in-between them and
their corresponding lines in 2d. Some sort of z-ordering would probably
be good here, but I believe this may be out of scope for this PR.
## Additional information
Some pretty images :)
<img width="1175" alt="Screenshot 2024-03-02 at 04 53 50"
src="https://github.com/bevyengine/bevy/assets/62256001/58df7e63-9376-4430-8871-32adba0cb53b">
- Note that the top vertex does not have a joint drawn.
<img width="1440" alt="Screenshot 2024-03-02 at 05 03 55"
src="https://github.com/bevyengine/bevy/assets/62256001/137a00cf-cbd4-48c2-a46f-4b47492d4fd9">
Now for a weird video:
https://github.com/bevyengine/bevy/assets/62256001/93026f48-f1d6-46fe-9163-5ab548a3fce4
- The black lines shooting out from the cube are miter joints that get
very long because the lines between which they are drawn are (almost)
collinear in screen space.
---------
Co-authored-by: Pablo Reinhardt <126117294+pablo-lua@users.noreply.github.com>
2024-03-11 19:21:32 +00:00
|
|
|
|
|
|
|
// Manual near plane clipping to avoid errors when doing the perspective divide inside this shader.
|
|
|
|
clip_a = clip_near_plane(clip_a, clip_c);
|
|
|
|
clip_b = clip_near_plane(clip_b, clip_a);
|
|
|
|
clip_c = clip_near_plane(clip_c, clip_b);
|
|
|
|
clip_a = clip_near_plane(clip_a, clip_c);
|
|
|
|
|
|
|
|
let resolution = view.viewport.zw;
|
|
|
|
let screen_a = resolution * (0.5 * clip_a.xy / clip_a.w + 0.5);
|
|
|
|
let screen_b = resolution * (0.5 * clip_b.xy / clip_b.w + 0.5);
|
|
|
|
let screen_c = resolution * (0.5 * clip_c.xy / clip_c.w + 0.5);
|
|
|
|
|
|
|
|
var color = vertex.color;
|
|
|
|
var line_width = joints_gizmo.line_width;
|
|
|
|
|
|
|
|
#ifdef PERSPECTIVE
|
|
|
|
line_width /= clip_b.w;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Line thinness fade from https://acegikmo.com/shapes/docs/#anti-aliasing
|
|
|
|
if line_width > 0.0 && line_width < 1. {
|
|
|
|
color.a *= line_width;
|
|
|
|
line_width = 1.;
|
|
|
|
}
|
|
|
|
|
|
|
|
let ab = normalize(screen_b - screen_a);
|
|
|
|
let cb = normalize(screen_b - screen_c);
|
|
|
|
let ab_norm = vec2(-ab.y, ab.x);
|
|
|
|
let cb_norm = vec2(cb.y, -cb.x);
|
|
|
|
let tangent = normalize(ab - cb);
|
|
|
|
let normal = vec2(-tangent.y, tangent.x);
|
|
|
|
let sigma = sign(dot(ab + cb, normal));
|
|
|
|
|
|
|
|
var p0 = line_width * sigma * ab_norm;
|
|
|
|
var p1 = line_width * sigma * normal / dot(normal, ab_norm);
|
|
|
|
var p2 = line_width * sigma * cb_norm;
|
|
|
|
|
|
|
|
var screen = screen_b + position.x * p0 + position.y * p1 + position.z * p2;
|
|
|
|
|
|
|
|
var depth = depth(clip_b);
|
|
|
|
|
|
|
|
var clip_position = vec4(clip_b.w * ((2. * screen) / resolution - 1.), depth, clip_b.w);
|
|
|
|
return VertexOutput(clip_position, color);
|
|
|
|
}
|
|
|
|
|
|
|
|
@vertex
|
|
|
|
fn vertex_round(vertex: VertexInput) -> VertexOutput {
|
Retained `Gizmo`s (#15473)
# Objective
Add a way to use the gizmo API in a retained manner, for increased
performance.
## Solution
- Move gizmo API from `Gizmos` to `GizmoBuffer`, ~ab~using `Deref` to
keep usage the same as before.
- Merge non-strip and strip variant of `LineGizmo` into one, storing the
data in a `GizmoBuffer` to have the same API for retained `LineGizmo`s.
### Review guide
- The meat of the changes are in `lib.rs`, `retained.rs`, `gizmos.rs`,
`pipeline_3d.rs` and `pipeline_2d.rs`
- The other files contain almost exclusively the churn from moving the
gizmo API from `Gizmos` to `GizmoBuffer`
## Testing
### Performance
Performance compared to the immediate mode API is from 65 to 80 times
better for static lines.
```
7900 XTX, 3700X
1707.9k lines/ms: gizmos_retained (21.3ms)
3488.5k lines/ms: gizmos_retained_continuous_polyline (31.3ms)
0.5k lines/ms: gizmos_retained_separate (97.7ms)
3054.9k lines/ms: bevy_polyline_retained_nan (16.8ms)
3596.3k lines/ms: bevy_polyline_retained_continuous_polyline (14.2ms)
0.6k lines/ms: bevy_polyline_retained_separate (78.9ms)
26.9k lines/ms: gizmos_immediate (14.9ms)
43.8k lines/ms: gizmos_immediate_continuous_polyline (18.3ms)
```
Looks like performance is good enough, being close to par with
`bevy_polyline`.
Benchmarks can be found here:
This branch:
https://github.com/tim-blackbird/line_racing/tree/retained-gizmos
Bevy 0.14: https://github.com/DGriffin91/line_racing
## Showcase
```rust
fn setup(
mut commands: Commands,
mut gizmo_assets: ResMut<Assets<GizmoAsset>>
) {
let mut gizmo = GizmoAsset::default();
// A sphere made out of one million lines!
gizmo
.sphere(default(), 1., CRIMSON)
.resolution(1_000_000 / 3);
commands.spawn(Gizmo {
handle: gizmo_assets.add(gizmo),
..default()
});
}
```
## Follow-up work
- Port over to the retained rendering world proper
- Calculate visibility and cull `Gizmo`s
2024-12-04 21:21:06 +00:00
|
|
|
let world_from_local = affine3_to_square(joints_gizmo.world_from_local);
|
|
|
|
|
|
|
|
var clip_a = view.clip_from_world * world_from_local * vec4(vertex.position_a, 1.);
|
|
|
|
var clip_b = view.clip_from_world * world_from_local * vec4(vertex.position_b, 1.);
|
|
|
|
var clip_c = view.clip_from_world * world_from_local * vec4(vertex.position_c, 1.);
|
Gizmo line joints (#12252)
# Objective
- Adds gizmo line joints, suggestion of #9400
## Solution
- Adds `line_joints: GizmoLineJoint` to `GizmoConfig`. Currently the
following values are supported:
- `GizmoLineJoint::None`: does not draw line joints, same behaviour as
previously
- `GizmoLineJoint::Bevel`: draws a single triangle between the lines
- `GizmoLineJoint::Miter` / 'spiky joints': draws two triangles between
the lines extending them until they meet at a (miter) point.
- NOTE: for very small angles between the lines, which happens
frequently in 3d, the miter point will be very far away from the point
at which the lines meet.
- `GizmoLineJoint::Round(resolution)`: Draw a circle arc between the
lines. The circle is a triangle fan of `resolution` triangles.
---
## Changelog
- Added `GizmoLineJoint`, use that in `GizmoConfig` and added necessary
pipelines and draw commands.
- Added a new `line_joints.wgsl` shader containing three vertex shaders
`vertex_bevel`, `vertex_miter` and `vertex_round` as well as a basic
`fragment` shader.
## Migration Guide
Any manually created `GizmoConfig`s must now set the `.line_joints`
field.
## Known issues
- The way we currently create basic closed shapes like rectangles,
circles, triangles or really any closed 2d shape means that one of the
corners will not be drawn with joints, although that would probably be
expected. (see the triangle in the 2d image)
- This could be somewhat mitigated by introducing line caps or fixed by
adding another segment overlapping the first of the strip. (Maybe in a
followup PR?)
- 3d shapes can look 'off' with line joints (especially bevel) because
wherever 3 or more lines meet one of them may stick out beyond the joint
drawn between the other 2.
- Adding additional lines so that there is a joint between every line at
a corner would fix this but would probably be too computationally
expensive.
- Miter joints are 'unreasonably long' for very small angles between the
lines (the angle is the angle between the lines in screen space). This
is technically correct but distracting and does not feel right,
especially in 3d contexts. I think limiting the length of the miter to
the point at which the lines meet might be a good idea.
- The joints may be drawn with a different gizmo in-between them and
their corresponding lines in 2d. Some sort of z-ordering would probably
be good here, but I believe this may be out of scope for this PR.
## Additional information
Some pretty images :)
<img width="1175" alt="Screenshot 2024-03-02 at 04 53 50"
src="https://github.com/bevyengine/bevy/assets/62256001/58df7e63-9376-4430-8871-32adba0cb53b">
- Note that the top vertex does not have a joint drawn.
<img width="1440" alt="Screenshot 2024-03-02 at 05 03 55"
src="https://github.com/bevyengine/bevy/assets/62256001/137a00cf-cbd4-48c2-a46f-4b47492d4fd9">
Now for a weird video:
https://github.com/bevyengine/bevy/assets/62256001/93026f48-f1d6-46fe-9163-5ab548a3fce4
- The black lines shooting out from the cube are miter joints that get
very long because the lines between which they are drawn are (almost)
collinear in screen space.
---------
Co-authored-by: Pablo Reinhardt <126117294+pablo-lua@users.noreply.github.com>
2024-03-11 19:21:32 +00:00
|
|
|
|
|
|
|
// Manual near plane clipping to avoid errors when doing the perspective divide inside this shader.
|
|
|
|
clip_a = clip_near_plane(clip_a, clip_c);
|
|
|
|
clip_b = clip_near_plane(clip_b, clip_a);
|
|
|
|
clip_c = clip_near_plane(clip_c, clip_b);
|
|
|
|
clip_a = clip_near_plane(clip_a, clip_c);
|
|
|
|
|
|
|
|
let resolution = view.viewport.zw;
|
|
|
|
let screen_a = resolution * (0.5 * clip_a.xy / clip_a.w + 0.5);
|
|
|
|
let screen_b = resolution * (0.5 * clip_b.xy / clip_b.w + 0.5);
|
|
|
|
let screen_c = resolution * (0.5 * clip_c.xy / clip_c.w + 0.5);
|
|
|
|
|
|
|
|
var color = vertex.color;
|
|
|
|
var line_width = joints_gizmo.line_width;
|
|
|
|
|
|
|
|
#ifdef PERSPECTIVE
|
|
|
|
line_width /= clip_b.w;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Line thinness fade from https://acegikmo.com/shapes/docs/#anti-aliasing
|
|
|
|
if line_width > 0.0 && line_width < 1. {
|
|
|
|
color.a *= line_width;
|
|
|
|
line_width = 1.;
|
|
|
|
}
|
|
|
|
|
|
|
|
let ab = normalize(screen_b - screen_a);
|
|
|
|
let cb = normalize(screen_b - screen_c);
|
|
|
|
let ab_norm = vec2(-ab.y, ab.x);
|
|
|
|
let cb_norm = vec2(cb.y, -cb.x);
|
|
|
|
|
|
|
|
// We render `joints_gizmo.resolution`triangles. The vertices in each triangle are ordered as follows:
|
|
|
|
// - 0: The 'center' vertex at `screen_b`.
|
|
|
|
// - 1: The vertex closer to the ab line.
|
|
|
|
// - 2: The vertex closer to the cb line.
|
|
|
|
var in_triangle_index = f32(vertex.index) % 3.0;
|
|
|
|
var tri_index = floor(f32(vertex.index) / 3.0);
|
|
|
|
var radius = sign(in_triangle_index) * 0.5 * line_width;
|
|
|
|
var theta = acos(dot(ab_norm, cb_norm));
|
|
|
|
let sigma = sign(dot(ab_norm, cb));
|
|
|
|
var angle = theta * (tri_index + in_triangle_index - 1) / f32(joints_gizmo.resolution);
|
|
|
|
var position_x = sigma * radius * cos(angle);
|
|
|
|
var position_y = radius * sin(angle);
|
|
|
|
|
|
|
|
var screen = screen_b + position_x * ab_norm + position_y * ab;
|
|
|
|
|
|
|
|
var depth = depth(clip_b);
|
|
|
|
|
|
|
|
var clip_position = vec4(clip_b.w * ((2. * screen) / resolution - 1.), depth, clip_b.w);
|
|
|
|
return VertexOutput(clip_position, color);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn clip_near_plane(a: vec4<f32>, b: vec4<f32>) -> vec4<f32> {
|
|
|
|
// Move a if a is behind the near plane and b is in front.
|
|
|
|
if a.z > a.w && b.z <= b.w {
|
|
|
|
// Interpolate a towards b until it's at the near plane.
|
|
|
|
let distance_a = a.z - a.w;
|
|
|
|
let distance_b = b.z - b.w;
|
|
|
|
// Add an epsilon to the interpolator to ensure that the point is
|
|
|
|
// not just behind the clip plane due to floating-point imprecision.
|
|
|
|
let t = distance_a / (distance_a - distance_b) + EPSILON;
|
|
|
|
return mix(a, b, t);
|
|
|
|
}
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn depth(clip: vec4<f32>) -> f32 {
|
|
|
|
var depth: f32;
|
|
|
|
if joints_gizmo.depth_bias >= 0. {
|
|
|
|
depth = clip.z * (1. - joints_gizmo.depth_bias);
|
|
|
|
} else {
|
|
|
|
// depth * (clip.w / depth)^-depth_bias. So that when -depth_bias is 1.0, this is equal to clip.w
|
|
|
|
// and when equal to 0.0, it is exactly equal to depth.
|
|
|
|
// the epsilon is here to prevent the depth from exceeding clip.w when -depth_bias = 1.0
|
|
|
|
// clip.w represents the near plane in homogeneous clip space in bevy, having a depth
|
|
|
|
// of this value means nothing can be in front of this
|
|
|
|
// The reason this uses an exponential function is that it makes it much easier for the
|
|
|
|
// user to chose a value that is convenient for them
|
|
|
|
depth = clip.z * exp2(-joints_gizmo.depth_bias * log2(clip.w / clip.z - EPSILON));
|
|
|
|
}
|
|
|
|
return depth;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct FragmentInput {
|
|
|
|
@location(0) color: vec4<f32>,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct FragmentOutput {
|
|
|
|
@location(0) color: vec4<f32>,
|
|
|
|
};
|
|
|
|
|
|
|
|
@fragment
|
|
|
|
fn fragment(in: FragmentInput) -> FragmentOutput {
|
|
|
|
// return FragmentOutput(vec4(1, 1, 1, 1));
|
|
|
|
return FragmentOutput(in.color);
|
Normalise matrix naming (#13489)
# Objective
- Fixes #10909
- Fixes #8492
## Solution
- Name all matrices `x_from_y`, for example `world_from_view`.
## Testing
- I've tested most of the 3D examples. The `lighting` example
particularly should hit a lot of the changes and appears to run fine.
---
## Changelog
- Renamed matrices across the engine to follow a `y_from_x` naming,
making the space conversion more obvious.
## Migration Guide
- `Frustum`'s `from_view_projection`, `from_view_projection_custom_far`
and `from_view_projection_no_far` were renamed to
`from_clip_from_world`, `from_clip_from_world_custom_far` and
`from_clip_from_world_no_far`.
- `ComputedCameraValues::projection_matrix` was renamed to
`clip_from_view`.
- `CameraProjection::get_projection_matrix` was renamed to
`get_clip_from_view` (this affects implementations on `Projection`,
`PerspectiveProjection` and `OrthographicProjection`).
- `ViewRangefinder3d::from_view_matrix` was renamed to
`from_world_from_view`.
- `PreviousViewData`'s members were renamed to `view_from_world` and
`clip_from_world`.
- `ExtractedView`'s `projection`, `transform` and `view_projection` were
renamed to `clip_from_view`, `world_from_view` and `clip_from_world`.
- `ViewUniform`'s `view_proj`, `unjittered_view_proj`,
`inverse_view_proj`, `view`, `inverse_view`, `projection` and
`inverse_projection` were renamed to `clip_from_world`,
`unjittered_clip_from_world`, `world_from_clip`, `world_from_view`,
`view_from_world`, `clip_from_view` and `view_from_clip`.
- `GpuDirectionalCascade::view_projection` was renamed to
`clip_from_world`.
- `MeshTransforms`' `transform` and `previous_transform` were renamed to
`world_from_local` and `previous_world_from_local`.
- `MeshUniform`'s `transform`, `previous_transform`,
`inverse_transpose_model_a` and `inverse_transpose_model_b` were renamed
to `world_from_local`, `previous_world_from_local`,
`local_from_world_transpose_a` and `local_from_world_transpose_b` (the
`Mesh` type in WGSL mirrors this, however `transform` and
`previous_transform` were named `model` and `previous_model`).
- `Mesh2dTransforms::transform` was renamed to `world_from_local`.
- `Mesh2dUniform`'s `transform`, `inverse_transpose_model_a` and
`inverse_transpose_model_b` were renamed to `world_from_local`,
`local_from_world_transpose_a` and `local_from_world_transpose_b` (the
`Mesh2d` type in WGSL mirrors this).
- In WGSL, in `bevy_pbr::mesh_functions`, `get_model_matrix` and
`get_previous_model_matrix` were renamed to `get_world_from_local` and
`get_previous_world_from_local`.
- In WGSL, `bevy_sprite::mesh2d_functions::get_model_matrix` was renamed
to `get_world_from_local`.
2024-06-03 16:56:53 +00:00
|
|
|
}
|