Skip to main content

wde_gizmos/
lib.rs

1//! WaterDropEngine's `wde-gizmos` crate renders lightweight debug gizmos (lines/boxes) on top of the main scene.
2//!
3//! Lines are recorded on the [`Gizmos`] resource from any main-world system (e.g. `ResMut<Gizmos>`).
4//! Every frame, the recorded lines are extracted, uploaded to the GPU and drawn in a single
5//! line-list draw call in the [`RenderPassTransparent`](wde_pbr::prelude::RenderPassTransparent)
6//! pass, then the [`Gizmos`] resource is cleared so it can be recorded again for the next frame.
7use bevy::prelude::*;
8use wde_pbr::prelude::*;
9use wde_renderer::prelude::{Color, *};
10
11use crate::{
12    data::{GizmoLineData, GizmoLinesBinding, GizmoQuadData, GizmoQuadsBinding},
13    extract::{
14        ExtractedGizmoLines, ExtractedGizmoQuads, GizmoDrawCount, GizmoQuadDrawCount,
15        extract_gizmo_lines, update_gizmo_buffer, update_gizmo_quad_buffer
16    },
17    pipeline::{GizmoQuadRenderPipeline, GizmoRenderPipeline},
18    subpass::{SubRenderPassGizmoQuads, SubRenderPassGizmos}
19};
20
21mod data;
22mod extract;
23mod pipeline;
24mod subpass;
25
26#[doc(hidden)]
27pub mod prelude {
28    pub use crate::Gizmos;
29}
30
31#[derive(Resource, Default)]
32pub struct Gizmos {
33    lines: Vec<(Vec3, Vec3, Color)>,
34    quads: Vec<([Vec3; 4], Color)>
35}
36impl Gizmos {
37    pub fn line(&mut self, start: Vec3, end: Vec3, color: Color) {
38        self.lines.push((start, end, color));
39    }
40
41    /// Records a filled quad from 4 world-space corners, in order (either winding), with the
42    /// given color. Use a color with alpha < 1.0 for a translucent quad.
43    pub fn quad(&mut self, corners: [Vec3; 4], color: Color) {
44        self.quads.push((corners, color));
45    }
46
47    pub fn cube(&mut self, transform: Transform, color: Color) {
48        let half_size = Vec3::splat(0.5);
49        let vertices = [
50            Vec3::new(-half_size.x, -half_size.y, -half_size.z),
51            Vec3::new(half_size.x, -half_size.y, -half_size.z),
52            Vec3::new(half_size.x, half_size.y, -half_size.z),
53            Vec3::new(-half_size.x, half_size.y, -half_size.z),
54            Vec3::new(-half_size.x, -half_size.y, half_size.z),
55            Vec3::new(half_size.x, -half_size.y, half_size.z),
56            Vec3::new(half_size.x, half_size.y, half_size.z),
57            Vec3::new(-half_size.x, half_size.y, half_size.z)
58        ];
59
60        let edges = [
61            (0, 1),
62            (1, 2),
63            (2, 3),
64            (3, 0), // back face
65            (4, 5),
66            (5, 6),
67            (6, 7),
68            (7, 4), // front face
69            (0, 4),
70            (1, 5),
71            (2, 6),
72            (3, 7) // sides
73        ];
74
75        for &(start_idx, end_idx) in &edges {
76            let start = transform.transform_point(vertices[start_idx]);
77            let end = transform.transform_point(vertices[end_idx]);
78            self.line(start, end, color);
79        }
80    }
81
82    /// Takes the recorded lines, leaving this resource empty for the next frame.
83    fn take_lines(&mut self) -> Vec<(Vec3, Vec3, Color)> {
84        std::mem::take(&mut self.lines)
85    }
86
87    /// Takes the recorded quads, leaving this resource empty for the next frame.
88    fn take_quads(&mut self) -> Vec<([Vec3; 4], Color)> {
89        std::mem::take(&mut self.quads)
90    }
91}
92
93pub struct GizmosPlugin;
94impl Plugin for GizmosPlugin {
95    fn build(&self, app: &mut App) {
96        app.init_resource::<Gizmos>();
97
98        app.add_plugins((
99            RenderDataRegisterPlugin::<GizmoLineData>::default(),
100            RenderBindingRegisterPlugin::<GizmoLinesBinding>::default(),
101            RenderPipelineRegisterPlugin::<GizmoRenderPipeline>::default(),
102            RenderDataRegisterPlugin::<GizmoQuadData>::default(),
103            RenderBindingRegisterPlugin::<GizmoQuadsBinding>::default(),
104            RenderPipelineRegisterPlugin::<GizmoQuadRenderPipeline>::default()
105        ));
106
107        app.get_sub_app_mut(RenderApp)
108            .unwrap()
109            .init_resource::<ExtractedGizmoLines>()
110            .init_resource::<ExtractedGizmoQuads>()
111            .init_resource::<GizmoDrawCount>()
112            .init_resource::<GizmoQuadDrawCount>()
113            .add_systems(Extract, extract_gizmo_lines)
114            .add_systems(
115                Render,
116                (update_gizmo_quad_buffer, update_gizmo_buffer).in_set(RenderSet::Prepare)
117            );
118    }
119
120    fn finish(&self, app: &mut App) {
121        // Quads are added first so lines are drawn on top of quad fills.
122        app.get_sub_app_mut(RenderApp)
123            .unwrap()
124            .world_mut()
125            .get_resource_mut::<RenderGraph>()
126            .unwrap()
127            .add_sub_pass::<SubRenderPassGizmoQuads, RenderPassTransparent>()
128            .add_sub_pass::<SubRenderPassGizmos, RenderPassTransparent>();
129    }
130}