From bc2ae4b512b8bc7a2b61dd24d9685289453681c5 Mon Sep 17 00:00:00 2001 From: Sébastien Crozet Date: Fri, 22 Apr 2022 15:45:53 +0200 Subject: Add a basic lines-based debug-renderer --- src_testbed/debug_render.rs | 81 +++++++++++++++++++++++++++++++++++++++++++++ src_testbed/lib.rs | 1 + src_testbed/testbed.rs | 7 ++-- 3 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 src_testbed/debug_render.rs (limited to 'src_testbed') diff --git a/src_testbed/debug_render.rs b/src_testbed/debug_render.rs new file mode 100644 index 0000000..135529f --- /dev/null +++ b/src_testbed/debug_render.rs @@ -0,0 +1,81 @@ +use crate::harness::Harness; +use bevy::prelude::*; +use bevy_prototype_debug_lines::DebugLines; +use rapier::math::{Point, Real, DIM}; +use rapier::pipeline::{ + DebugRenderBackend, DebugRenderMode, DebugRenderObject, DebugRenderPipeline, +}; +use std::fmt::Debug; + +pub struct RapierDebugRenderPlugin { + depth_test: bool, +} + +impl Default for RapierDebugRenderPlugin { + fn default() -> Self { + Self { + depth_test: cfg!(feature = "dim3"), + } + } +} + +impl RapierDebugRenderPlugin { + pub fn with_depth_test(enabled: bool) -> Self { + Self { + depth_test: enabled, + } + } +} + +impl Plugin for RapierDebugRenderPlugin { + fn build(&self, app: &mut App) { + app.add_plugin( + bevy_prototype_debug_lines::DebugLinesPlugin::with_depth_test(self.depth_test), + ) + .insert_resource(DebugRenderPipeline::new( + Default::default(), + !DebugRenderMode::RIGID_BODY_AXES, + )) + .add_system_to_stage(CoreStage::Update, debug_render_scene); + } +} + +struct BevyLinesRenderBackend<'a> { + lines: &'a mut DebugLines, +} + +impl<'a> DebugRenderBackend for BevyLinesRenderBackend<'a> { + #[cfg(feature = "dim2")] + fn draw_line(&mut self, _: DebugRenderObject, a: Point, b: Point, color: [f32; 4]) { + self.lines.line_colored( + [a.x, a.y, 1.0e-8].into(), + [b.x, b.y, 1.0e-8].into(), + 0.0, + Color::hsla(color[0], color[1], color[2], color[3]), + ) + } + #[cfg(feature = "dim3")] + fn draw_line(&mut self, _: DebugRenderObject, a: Point, b: Point, color: [f32; 4]) { + self.lines.line_colored( + [a.x, a.y, a.z].into(), + [b.x, b.y, b.z].into(), + 0.0, + Color::hsla(color[0], color[1], color[2], color[3]), + ) + } +} + +fn debug_render_scene( + mut pipeline: ResMut, + harness: NonSend, + mut lines: ResMut, +) { + let mut backend = BevyLinesRenderBackend { lines: &mut *lines }; + pipeline.render( + &mut backend, + &harness.physics.bodies, + &harness.physics.colliders, + &harness.physics.impulse_joints, + &harness.physics.multibody_joints, + ); +} diff --git a/src_testbed/lib.rs b/src_testbed/lib.rs index bbf2d2f..881e05d 100644 --- a/src_testbed/lib.rs +++ b/src_testbed/lib.rs @@ -19,6 +19,7 @@ mod box2d_backend; mod camera2d; #[cfg(feature = "dim3")] mod camera3d; +mod debug_render; mod graphics; pub mod harness; pub mod objects; diff --git a/src_testbed/testbed.rs b/src_testbed/testbed.rs index 1bb30eb..2c40cf2 100644 --- a/src_testbed/testbed.rs +++ b/src_testbed/testbed.rs @@ -5,7 +5,7 @@ use bevy::prelude::*; use crate::physics::{PhysicsEvents, PhysicsSnapshot, PhysicsState}; use crate::plugin::TestbedPlugin; -use crate::ui; +use crate::{debug_render, ui}; use crate::{graphics::GraphicsManager, harness::RunState}; use na::{self, Point2, Point3, Vector3}; @@ -17,7 +17,7 @@ use rapier::geometry::{ColliderHandle, ColliderSet, NarrowPhase}; #[cfg(feature = "dim3")] use rapier::geometry::{InteractionGroups, Ray}; use rapier::math::{Real, Vector}; -use rapier::pipeline::PhysicsHooks; +use rapier::pipeline::{DebugRenderMode, PhysicsHooks}; #[cfg(all(feature = "dim2", feature = "other-backends"))] use crate::box2d_backend::Box2dWorld; @@ -380,7 +380,8 @@ impl TestbedApp { .add_plugins(DefaultPlugins) .add_plugin(OrbitCameraPlugin) .add_plugin(WireframePlugin) - .add_plugin(bevy_egui::EguiPlugin); + .add_plugin(bevy_egui::EguiPlugin) + .add_plugin(debug_render::RapierDebugRenderPlugin::default()); #[cfg(target_arch = "wasm32")] app.add_plugin(bevy_webgl2::WebGL2Plugin); -- cgit From 5063fa420392455f7926f1ba3e65612f79a0b066 Mon Sep 17 00:00:00 2001 From: Sébastien Crozet Date: Thu, 28 Apr 2022 13:19:58 +0200 Subject: Testbed: switch to bevy 0.7 --- src_testbed/camera2d.rs | 6 +- src_testbed/camera3d.rs | 6 +- src_testbed/debug_render.rs | 8 +- src_testbed/lib.rs | 1 + src_testbed/lines/debuglines.wgsl | 49 +++++ src_testbed/lines/debuglines2d.wgsl | 31 ++++ src_testbed/lines/mod.rs | 358 ++++++++++++++++++++++++++++++++++++ src_testbed/lines/render_dim.rs | 335 +++++++++++++++++++++++++++++++++ src_testbed/objects/node.rs | 36 ++-- src_testbed/physx_backend.rs | 2 +- src_testbed/testbed.rs | 37 ++-- src_testbed/ui.rs | 4 +- 12 files changed, 828 insertions(+), 45 deletions(-) create mode 100644 src_testbed/lines/debuglines.wgsl create mode 100644 src_testbed/lines/debuglines2d.wgsl create mode 100644 src_testbed/lines/mod.rs create mode 100644 src_testbed/lines/render_dim.rs (limited to 'src_testbed') diff --git a/src_testbed/camera2d.rs b/src_testbed/camera2d.rs index 1964385..ddac1ed 100644 --- a/src_testbed/camera2d.rs +++ b/src_testbed/camera2d.rs @@ -87,8 +87,8 @@ impl OrbitCameraPlugin { } impl Plugin for OrbitCameraPlugin { fn build(&self, app: &mut App) { - app.add_system(Self::mouse_motion_system.system()) - .add_system(Self::zoom_system.system()) - .add_system(Self::update_transform_system.system()); + app.add_system(Self::mouse_motion_system) + .add_system(Self::zoom_system) + .add_system(Self::update_transform_system); } } diff --git a/src_testbed/camera3d.rs b/src_testbed/camera3d.rs index 979f8e8..12ee508 100644 --- a/src_testbed/camera3d.rs +++ b/src_testbed/camera3d.rs @@ -113,8 +113,8 @@ impl OrbitCameraPlugin { } impl Plugin for OrbitCameraPlugin { fn build(&self, app: &mut App) { - app.add_system(Self::mouse_motion_system.system()) - .add_system(Self::zoom_system.system()) - .add_system(Self::update_transform_system.system()); + app.add_system(Self::mouse_motion_system) + .add_system(Self::zoom_system) + .add_system(Self::update_transform_system); } } diff --git a/src_testbed/debug_render.rs b/src_testbed/debug_render.rs index 135529f..ff884e2 100644 --- a/src_testbed/debug_render.rs +++ b/src_testbed/debug_render.rs @@ -1,6 +1,6 @@ use crate::harness::Harness; +use crate::lines::DebugLines; use bevy::prelude::*; -use bevy_prototype_debug_lines::DebugLines; use rapier::math::{Point, Real, DIM}; use rapier::pipeline::{ DebugRenderBackend, DebugRenderMode, DebugRenderObject, DebugRenderPipeline, @@ -29,9 +29,9 @@ impl RapierDebugRenderPlugin { impl Plugin for RapierDebugRenderPlugin { fn build(&self, app: &mut App) { - app.add_plugin( - bevy_prototype_debug_lines::DebugLinesPlugin::with_depth_test(self.depth_test), - ) + app.add_plugin(crate::lines::DebugLinesPlugin::with_depth_test( + self.depth_test, + )) .insert_resource(DebugRenderPipeline::new( Default::default(), !DebugRenderMode::RIGID_BODY_AXES, diff --git a/src_testbed/lib.rs b/src_testbed/lib.rs index 881e05d..ed7294d 100644 --- a/src_testbed/lib.rs +++ b/src_testbed/lib.rs @@ -22,6 +22,7 @@ mod camera3d; mod debug_render; mod graphics; pub mod harness; +mod lines; pub mod objects; pub mod physics; #[cfg(all(feature = "dim3", feature = "other-backends"))] diff --git a/src_testbed/lines/debuglines.wgsl b/src_testbed/lines/debuglines.wgsl new file mode 100644 index 0000000..0987839 --- /dev/null +++ b/src_testbed/lines/debuglines.wgsl @@ -0,0 +1,49 @@ +// This should work, but it's bugged right now so we have to use 2 shaders: https://github.com/bevyengine/bevy/issues/4011 +#ifdef LINES_3D + #import bevy_pbr::mesh_view_bind_group + //#import bevy_pbr::mesh_struct +#else + //#import bevy_sprite::mesh2d_view_bind_group +#endif + +struct Vertex { + [[location(0)]] pos: vec3; + [[location(1)]] color: u32; +}; + +struct VertexOutput { + [[builtin(position)]] clip_position: vec4; + [[location(0)]] color: vec4; +}; + +struct FragmentOutput { + [[builtin(frag_depth)]] depth: f32; + [[location(0)]] color: vec4; +}; + +[[stage(vertex)]] +fn vertex(vertex: Vertex) -> VertexOutput { + var out: VertexOutput; + out.clip_position = view.view_proj * vec4(vertex.pos, 1.0); + //out.color = vertex.color; + // https://github.com/bevyengine/bevy/blob/328c26d02c50de0bc77f0d24a376f43ba89517b1/examples/2d/mesh2d_manual.rs#L234 + out.color = vec4((vec4(vertex.color) >> vec4(8u, 8u, 16u, 24u)) & vec4(255u)) / 255.0; + + return out; +} + +[[stage(fragment)]] +fn fragment(in: VertexOutput) -> FragmentOutput { + var out: FragmentOutput; + +// This should be #ifdef DEPTH_TEST_ENABLED && LINES_3D, but the +// preprocessor doesn't support that yet. +// Luckily, DEPTH_TEST_ENABLED isn't set in 2d anyway. +#ifdef DEPTH_TEST_ENABLED + out.depth = in.clip_position.z; +#else + out.depth = 1.0; +#endif + out.color = in.color; + return out; +} diff --git a/src_testbed/lines/debuglines2d.wgsl b/src_testbed/lines/debuglines2d.wgsl new file mode 100644 index 0000000..9316531 --- /dev/null +++ b/src_testbed/lines/debuglines2d.wgsl @@ -0,0 +1,31 @@ +#import bevy_sprite::mesh2d_view_bind_group +[[group(0), binding(0)]] +var view: View; + +struct Vertex { + //[[location(0)]] color: vec4; + [[location(0)]] place: vec3; + [[location(1)]] color: u32; +}; + +struct VertexOutput { + [[builtin(position)]] clip_position: vec4; + [[location(0)]] color: vec4; +}; + +[[stage(vertex)]] +fn vertex(vertex: Vertex) -> VertexOutput { + var out: VertexOutput; + out.clip_position = view.view_proj * vec4(vertex.place, 1.0); + // What is this craziness? + out.color = vec4((vec4(vertex.color) >> vec4(0u, 8u, 16u, 24u)) & vec4(255u)) / 255.0; + //out.color = vertex.color; + //out.color = vec4(1.0, 0.0, 0.0, 1.0); + + return out; +} + +[[stage(fragment)]] +fn fragment(in: VertexOutput) -> [[location(0)]] vec4 { + return in.color; +} diff --git a/src_testbed/lines/mod.rs b/src_testbed/lines/mod.rs new file mode 100644 index 0000000..c80c5df --- /dev/null +++ b/src_testbed/lines/mod.rs @@ -0,0 +1,358 @@ +use bevy::{ + asset::{Assets, HandleUntyped}, + pbr::{NotShadowCaster, NotShadowReceiver}, + prelude::*, + reflect::TypeUuid, + render::{ + mesh::{/*Indices,*/ Mesh, VertexAttributeValues}, + render_phase::AddRenderCommand, + render_resource::PrimitiveTopology, + render_resource::Shader, + }, +}; + +mod render_dim; + +// This module exists to "isolate" the `#[cfg]` attributes to this part of the +// code. Otherwise, we would pollute the code with a lot of feature +// gates-specific code. +#[cfg(feature = "dim3")] +mod dim { + pub(crate) use crate::lines::render_dim::r3d::{queue, DebugLinePipeline, DrawDebugLines}; + pub(crate) use bevy::core_pipeline::Opaque3d as Phase; + use bevy::{asset::Handle, render::mesh::Mesh}; + + pub(crate) type MeshHandle = Handle; + pub(crate) fn from_handle(from: &MeshHandle) -> &Handle { + from + } + pub(crate) fn into_handle(from: Handle) -> MeshHandle { + from + } + pub(crate) const SHADER_FILE: &str = include_str!("debuglines.wgsl"); + pub(crate) const DIMMENSION: &str = "3d"; +} +#[cfg(feature = "dim2")] +mod dim { + pub(crate) use crate::lines::render_dim::r2d::{queue, DebugLinePipeline, DrawDebugLines}; + pub(crate) use bevy::core_pipeline::Transparent2d as Phase; + use bevy::{asset::Handle, render::mesh::Mesh, sprite::Mesh2dHandle}; + + pub(crate) type MeshHandle = Mesh2dHandle; + pub(crate) fn from_handle(from: &MeshHandle) -> &Handle { + &from.0 + } + pub(crate) fn into_handle(from: Handle) -> MeshHandle { + Mesh2dHandle(from) + } + pub(crate) const SHADER_FILE: &str = include_str!("debuglines2d.wgsl"); + pub(crate) const DIMMENSION: &str = "2d"; +} + +// See debuglines.wgsl for explanation on 2 shaders. +//pub(crate) const SHADER_FILE: &str = include_str!("debuglines.wgsl"); +pub(crate) const DEBUG_LINES_SHADER_HANDLE: HandleUntyped = + HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 17477439189930443325); + +pub(crate) struct DebugLinesConfig { + depth_test: bool, +} + +/// Bevy plugin, for initializing stuff. +/// +/// # Usage +/// +/// ``` +/// use bevy::prelude::*; +/// use bevy_prototype_debug_lines::*; +/// +/// App::new() +/// .add_plugins(DefaultPlugins) +/// .add_plugin(DebugLinesPlugin::default()) +/// .run(); +/// ``` +/// +/// Alternatively, you can initialize the plugin with depth testing, so that +/// debug lines cut through geometry. To do this, use [`DebugLinesPlugin::with_depth_test(true)`]. +/// ``` +/// use bevy::prelude::*; +/// use bevy_prototype_debug_lines::*; +/// +/// App::new() +/// .add_plugins(DefaultPlugins) +/// .add_plugin(DebugLinesPlugin::with_depth_test(true)) +/// .run(); +/// ``` +#[derive(Debug, Default, Clone)] +pub struct DebugLinesPlugin { + depth_test: bool, +} + +impl DebugLinesPlugin { + /// Controls whether debug lines should be drawn with depth testing enabled + /// or disabled. + /// + /// # Arguments + /// + /// * `val` - True if lines should intersect with other geometry, or false + /// if lines should always draw on top be drawn on top (the default). + pub fn with_depth_test(val: bool) -> Self { + Self { depth_test: val } + } +} + +impl Plugin for DebugLinesPlugin { + fn build(&self, app: &mut App) { + use bevy::render::{render_resource::SpecializedMeshPipelines, RenderApp, RenderStage}; + let mut shaders = app.world.get_resource_mut::>().unwrap(); + shaders.set_untracked( + DEBUG_LINES_SHADER_HANDLE, + Shader::from_wgsl(dim::SHADER_FILE), + ); + app.init_resource::(); + app.add_startup_system(setup) + .add_system_to_stage(CoreStage::PostUpdate, update.label("draw_lines")); + app.sub_app_mut(RenderApp) + .add_render_command::() + .insert_resource(DebugLinesConfig { + depth_test: self.depth_test, + }) + .init_resource::() + .init_resource::>() + .add_system_to_stage(RenderStage::Extract, extract) + .add_system_to_stage(RenderStage::Queue, dim::queue); + + info!("Loaded {} debug lines plugin.", dim::DIMMENSION); + } +} + +// Number of meshes to separate line buffers into. +// We don't really do culling currently but this is a gateway to that. +const MESH_COUNT: usize = 4; +// Maximum number of points for each individual mesh. +const MAX_POINTS_PER_MESH: usize = 2_usize.pow(16); +const _MAX_LINES_PER_MESH: usize = MAX_POINTS_PER_MESH / 2; +/// Maximum number of points. +pub const MAX_POINTS: usize = MAX_POINTS_PER_MESH * MESH_COUNT; +/// Maximum number of unique lines to draw at once. +pub const MAX_LINES: usize = MAX_POINTS / 2; + +fn setup(mut cmds: Commands, mut meshes: ResMut>) { + // Spawn a bunch of meshes to use for lines. + for i in 0..MESH_COUNT { + // Create a new mesh with the number of vertices we need. + let mut mesh = Mesh::new(PrimitiveTopology::LineList); + mesh.insert_attribute( + Mesh::ATTRIBUTE_POSITION, + VertexAttributeValues::Float32x3(Vec::with_capacity(MAX_POINTS_PER_MESH)), + ); + mesh.insert_attribute( + Mesh::ATTRIBUTE_COLOR, + VertexAttributeValues::Uint32(Vec::with_capacity(MAX_POINTS_PER_MESH)), + ); + // https://github.com/Toqozz/bevy_debug_lines/issues/16 + //mesh.set_indices(Some(Indices::U16(Vec::with_capacity(MAX_POINTS_PER_MESH)))); + + cmds.spawn_bundle(( + dim::into_handle(meshes.add(mesh)), + NotShadowCaster, + NotShadowReceiver, + Transform::default(), + GlobalTransform::default(), + Visibility::default(), + ComputedVisibility::default(), + DebugLinesMesh(i), + )); + } +} + +fn update( + debug_line_meshes: Query<(&dim::MeshHandle, &DebugLinesMesh)>, + time: Res