diff options
| author | Sébastien Crozet <developer@crozet.re> | 2022-04-28 18:24:01 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-04-28 18:24:01 +0200 |
| commit | 488aad0af3f772e14fd85b27bfff6c1db5d23829 (patch) | |
| tree | 4c19f613750fcd8779714915dbb752ce369a4173 /src_testbed | |
| parent | 21a31bc1026d17d30b3a5ac35e6bb716dc66be6e (diff) | |
| parent | 7dc038aec66783d72abda446d6251385e6ad30f4 (diff) | |
| download | rapier-488aad0af3f772e14fd85b27bfff6c1db5d23829.tar.gz rapier-488aad0af3f772e14fd85b27bfff6c1db5d23829.tar.bz2 rapier-488aad0af3f772e14fd85b27bfff6c1db5d23829.zip | |
Merge pull request #315 from dimforge/debug-renderer
Add a basic lines-based debug-renderer
Diffstat (limited to 'src_testbed')
| -rw-r--r-- | src_testbed/camera2d.rs | 6 | ||||
| -rw-r--r-- | src_testbed/camera3d.rs | 6 | ||||
| -rw-r--r-- | src_testbed/debug_render.rs | 72 | ||||
| -rw-r--r-- | src_testbed/lib.rs | 2 | ||||
| -rw-r--r-- | src_testbed/lines/debuglines.wgsl | 49 | ||||
| -rw-r--r-- | src_testbed/lines/debuglines2d.wgsl | 31 | ||||
| -rw-r--r-- | src_testbed/lines/mod.rs | 373 | ||||
| -rw-r--r-- | src_testbed/lines/render_dim.rs | 335 | ||||
| -rw-r--r-- | src_testbed/objects/node.rs | 36 | ||||
| -rw-r--r-- | src_testbed/physx_backend.rs | 2 | ||||
| -rw-r--r-- | src_testbed/testbed.rs | 41 | ||||
| -rw-r--r-- | src_testbed/ui.rs | 4 |
12 files changed, 914 insertions, 43 deletions
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 new file mode 100644 index 0000000..913663e --- /dev/null +++ b/src_testbed/debug_render.rs @@ -0,0 +1,72 @@ +use crate::harness::Harness; +use crate::lines::DebugLines; +use bevy::prelude::*; +use rapier::math::{Point, Real}; +use rapier::pipeline::{ + DebugRenderBackend, DebugRenderMode, DebugRenderObject, DebugRenderPipeline, +}; + +pub struct RapierDebugRenderPlugin { + depth_test: bool, +} + +impl Default for RapierDebugRenderPlugin { + fn default() -> Self { + Self { + depth_test: cfg!(feature = "dim3"), + } + } +} + +impl Plugin for RapierDebugRenderPlugin { + fn build(&self, app: &mut App) { + app.add_plugin(crate::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<Real>, b: Point<Real>, color: [f32; 4]) { + self.lines.line_colored( + [a.x as f32, a.y as f32, 1.0e-8 as f32].into(), + [b.x as f32, b.y as f32, 1.0e-8 as f32].into(), + 0.0, + Color::hsla(color[0], color[1], color[2], color[3]), + ) + } + #[cfg(feature = "dim3")] + fn draw_line(&mut self, _: DebugRenderObject, a: Point<Real>, b: Point<Real>, color: [f32; 4]) { + self.lines.line_colored( + [a.x as f32, a.y as f32, a.z as f32].into(), + [b.x as f32, b.y as f32, b.z as f32].into(), + 0.0, + Color::hsla(color[0], color[1], color[2], color[3]), + ) + } +} + +fn debug_render_scene( + mut pipeline: ResMut<DebugRenderPipeline>, + harness: NonSend<Harness>, + mut lines: ResMut<DebugLines>, +) { + 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..ed7294d 100644 --- a/src_testbed/lib.rs +++ b/src_testbed/lib.rs @@ -19,8 +19,10 @@ mod box2d_backend; mod camera2d; #[cfg(feature = "dim3")] 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<f32>; + [[location(1)]] color: u32; +}; + +struct VertexOutput { + [[builtin(position)]] clip_position: vec4<f32>; + [[location(0)]] color: vec4<f32>; +}; + +struct FragmentOutput { + [[builtin(frag_depth)]] depth: f32; + [[location(0)]] color: vec4<f32>; +}; + +[[stage(vertex)]] +fn vertex(vertex: Vertex) -> VertexOutput { + var out: VertexOutput; + out.clip_position = view.view_proj * vec4<f32>(vertex.pos, 1.0); + //out.color = vertex.color; + // https://github.com/bevyengine/bevy/blob/328c26d02c50de0bc77f0d24a376f43ba89517b1/examples/2d/mesh2d_manual.rs#L234 + out.color = vec4<f32>((vec4<u32>(vertex.color) >> vec4<u32>(8u, 8u, 16u, 24u)) & vec4<u32>(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<uniform> view: View; + +struct Vertex { + //[[location(0)]] color: vec4<f32>; + [[location(0)]] place: vec3<f32>; + [[location(1)]] color: u32; +}; + +struct VertexOutput { + [[builtin(position)]] clip_position: vec4<f32>; + [[location(0)]] color: vec4<f32>; +}; + +[[stage(vertex)]] +fn vertex(vertex: Vertex) -> VertexOutput { + var out: VertexOutput; + out.clip_position = view.view_proj * vec4<f32>(vertex.place, 1.0); + // What is this craziness? + out.color = vec4<f32>((vec4<u32>(vertex.color) >> vec4<u32>(0u, 8u, 16u, 24u)) & vec4<u32>(255u)) / 255.0; + //out.color = vertex.color; + //out.color = vec4<f32>(1.0, 0.0, 0.0, 1.0); + + return out; +} + +[[stage(fragment)]] +fn fragment(in: VertexOutput) -> [[location(0)]] vec4<f32> { + return in.color; +} diff --git a/src_testbed/lines/mod.rs b/src_testbed/lines/mod.rs new file mode 100644 index 0000000..7e78ee9 --- /dev/null +++ b/src_testbed/lines/mod.rs @@ -0,0 +1,373 @@ +#![allow(warnings)] +/** + * + * NOTE: this module and its submodules are only temporary. It is a copy-paste of the bevy-debug-lines + * crate: https://github.com/Toqozz/bevy_debug_lines (MIT license) + * It has been partially updated to work with bevy 0.7, but hasn’t been released yet. + * So, in the mean time, we are keeping a version here that we will replace by the + * upstream dependency once: + * 1. The version compatible with bevy 0.7 is released to crates.io. + * 2. We find a way to make the 2D version work with our examples. The problem + * only happens when running our own examples because cargo’s unification of + * features will enable the `3d` feature of `bevy_debug_lines` when running + * a `2d` example. + * + */ +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<Mesh>; + pub(crate) fn from_handle(from: &MeshHandle) -> &Handle<Mesh> { + from + } + pub(crate) fn into_handle(from: Handle<Mesh>) -> 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<Mesh> { + &from.0 + } + pub(crate) fn into_handle(from: Handle<Mesh>) -> 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 +/// +/// ```.ignore +/// 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)`]. +/// ```.ignore +/// 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::<Assets<Shader>>().unwrap(); + shaders.set_untracked( + DEBUG_LINES_SHADER_HANDLE, + Shader::from_wgsl(dim::SHADER_FILE), + ); + app.init_resource::<DebugLines>(); + app.add_startup_system(setup) + .add_system_to_stage(CoreStage::PostUpdate, update.label("draw_lines")); + app.sub_app_mut(RenderApp) + .add_render_command::<dim::Phase, dim::DrawDebugLines>() + .insert_resource(DebugLinesConfig { + depth_test: self.depth_test, + }) + .init_resource::<dim::DebugLinePipeline>() + .init_resource::<SpecializedMeshPipelines<dim::DebugLinePipeline>>() + .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<Assets<Mesh>>) { + // 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<Time>, + mut meshes: ResMut<Assets<Mesh>>, + mut lines: ResMut<DebugLines>, +) { + // For each debug line mesh, fill its buffers with the relevant positions/colors chunks. + for (mesh_handle, debug_lines_idx) in debug_line_meshes.iter() { + let mesh = meshes.get_mut(dim::from_handle(mesh_handle)).unwrap(); + use VertexAttributeValues::{Float32x3, Float32x4, Uint32}; + if let Some(Float32x3(vbuffer)) = mesh.attribute_mut(Mesh::ATTRIBUTE_POSITION) { + vbuffer.clear(); + if let Some(new_content) = lines + .positions + .chunks(MAX_POINTS_PER_MESH) + .nth(debug_lines_idx.0) + { + vbuffer.extend(new_content); + } + } + + if let Some(Uint32(cbuffer)) = mesh.attribute_mut(Mesh::ATTRIBUTE_COLOR) { + cbuffer.clear(); + if let Some(new_content) = lines + .colors + .chunks(MAX_POINTS_PER_MESH) + .nth(debug_lines_idx.0) + { + cbuffer.extend(new_content); + } + } + + /* + // https://github.com/Toqozz/bevy_debug_lines/issues/16 + if let Some(Indices::U16(indices)) = mesh.indices_mut() { + indices.clear(); + if let Some(new_content) = lines.durations.chunks(_MAX_LINES_PER_MESH).nth(debug_lines_idx.0) { + indices.extend( + new_content.iter().enumerate().map(|(i, _)| i as u16).flat_map(|i| [i * 2, i*2 + 1]) + ); + } + } + */ + } + + // Processes stuff like getting rid of expired lines and stuff. + lines.update(time.delta_seconds()); +} + +/// Move the DebugLinesMesh marker Component to the render context. +fn extract(mut commands: Commands, query: Query<Entity, With<DebugLinesMesh>>) { + for entity in query.iter() { + commands.get_or_spawn(entity).insert(RenderDebugLinesMesh); + } +} + +#[derive(Component)] +struct DebugLinesMesh(usize); + +#[derive(Component)] +pub(crate) struct RenderDebugLinesMesh; + +/// Bevy resource providing facilities to draw lines. +/// +/// # Usage +/// ```.ignore +/// use bevy::prelude::*; +/// use bevy_prototype_debug_lines::*; +/// +/// // Draws 3 horizontal lines, which disappear after 1 frame. +/// fn some_system(mut lines: ResMut<DebugLines>) { +/// lines.line(Vec3::new(-1.0, 1.0, 0.0), Vec3::new(1.0, 1.0, 0.0), 0.0); +/// lines.line_colored( +/// Vec3::new(-1.0, 0.0, 0.0), +/// Vec3::new(1.0, 0.0, 0.0), +/// 0.0, +/// Color::WHITE +/// ); +/// lines.line_gradient( +/// Vec3::new(-1.0, -1.0, 0.0), +/// Vec3::new(1.0, -1.0, 0.0), +/// 0.0, +/// Color::WHITE, Color::PINK +/// ); +/// } +/// ``` +#[derive(Default)] +pub struct DebugLines { + pub positions: Vec<[f32; 3]>, + //pub colors: Vec<[f32; 4]>, + pub colors: Vec<u32>, + pub durations: Vec<f32>, +} + +impl DebugLines { + /// Draw a line in world space, or update an existing line + /// + /// # Arguments + /// + /// * `start` - The start of the line in world space + /// * `end` - The end of the line in world space + /// * `duration` - Duration (in seconds) that the line should show for -- a value of + /// zero will show the line for 1 frame. + pub fn line(&mut self, start: Vec3, end: Vec3, duration: f32) { + self.line_colored(start, end, duration, Color::WHITE); + } + + /// Draw a line in world space with a specified color, or update an existing line + /// + /// # Arguments + /// + /// * `start` - The start of the line in world space + /// * `end` - The end of the line in world space + /// * `duration` - Duration (in seconds) that the line should show for -- a value of + /// zero will show the line for 1 frame. + /// * `color` - Line color + pub fn line_colored(&mut self, start: Vec3, end: Vec3, duration: f32, color: Color) { + self.line_gradient(start, end, duration, color, color); + } + + /// Draw a line in world space with a specified gradient color, or update an existing line + /// + /// # Arguments + /// + /// * `start` - The start of the line in world space + /// * `end` - The end of the line in world space + /// * `duration` - Duration (in seconds) that the line should show for -- a value of + /// zero will show the line for 1 frame. + /// * `start_color` - Line color + /// * `end_color` - Line color + pub fn line_gradient( + &mut self, + start: Vec3, + end: Vec3, + duration: f32, + start_color: Color, + end_color: Color, + ) { + if self.positions.len() >= MAX_POINTS { + warn!("Tried to add a new line when existing number of lines was already at maximum, ignoring."); + return; + } + + self.positions.push(start.into()); + self.positions.push(end.into()); + //self.colors.push(start_color.into()); + //self.colors.push(end_color.into()); + self.colors.push(start_color.as_rgba_u32()); + self.colors.push(end_color.as_rgba_u32()); + self.durations.push(duration); + } + + // Returns the indices of the start and end positions of the nth line. + // The indices can also be used to access color data. + fn nth(&self, idx: usize) -> (usize, usize) { + let i = idx * 2; + (i, i + 1) + } + + // Prepare [`ImmediateLinesStorage`] and [`RetainedLinesStorage`] for next + // frame. + // This clears the immediate mod buffers and tells the retained mode + // buffers to recompute expired lines list. + fn update(&mut self, dt: f32) { + // TODO: an actual line counter wouldn't hurt. + let mut i = 0; + let mut len = self.durations.len(); + while i != len { + self.durations[i] -= dt; + // <= instead of < is fine here because this is always called AFTER sending the + // data to the mesh, so we're guaranteed at least a frame here. + if self.durations[i] <= 0.0 { + let (cur_s, cur_e) = self.nth(i); + let (last_s, last_e) = self.nth(len - 1); + self.positions.swap(cur_s, last_s); + self.positions.swap(cur_e, last_e); + self.colors.swap(cur_s, last_s); + self.colors.swap(cur_e, last_e); + self.durations.swap(i, len - 1); + len -= 1; + } else { + i += 1; + } + } + + self.positions.truncate(len * 2); + self.colors.truncate(len * 2); + self.durations.truncate(len); + } +} diff --git a/src_testbed/lines/render_dim.rs b/src_testbed/lines/render_dim.rs new file mode 100644 index 0000000..92215f2 --- /dev/null +++ b/src_testbed/lines/render_dim.rs @@ -0,0 +1,335 @@ +pub mod r3d { + use bevy::{ + core_pipeline::Opaque3d, + pbr::{ + DrawMesh, MeshPipeline, MeshPipelineKey, MeshUniform, SetMeshBindGroup, + SetMeshViewBindGroup, + }, + prelude::*, + render::{ + mesh::MeshVertexBufferLayout, + render_asset::RenderAssets, + render_phase::{DrawFunctions, RenderPhase, SetItemPipeline}, + render_resource::{ + BlendState, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, + DepthStencilState, FragmentState, FrontFace, MultisampleState, PipelineCache, + PolygonMode, PrimitiveState, PrimitiveTopology, RenderPipelineDescriptor, + SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines, + StencilFaceState, StencilState, TextureFormat, VertexAttribute, VertexBufferLayout, + VertexFormat, VertexState, VertexStepMode, + }, + texture::BevyDefault, + view::{ExtractedView, Msaa}, + }, + utils::Hashed, + }; + + use crate::lines::{DebugLinesConfig, RenderDebugLinesMesh, DEBUG_LINES_SHADER_HANDLE}; + + pub(crate) struct DebugLinePipeline { + mesh_pipeline: MeshPipeline, + shader: Handle<Shader>, + //always_in_front: bool, + } + impl FromWorld for DebugLinePipeline { + fn from_world(render_world: &mut World) -> Self { + //let config = render_world.get_resource::<DebugLinesConfig>().unwrap(); + DebugLinePipeline { + mesh_pipeline: render_world.get_resource::<MeshPipeline>().unwrap().clone(), + shader: DEBUG_LINES_SHADER_HANDLE.typed(), + //always_in_front: config.always_in_front, + } + } + } + + impl SpecializedMeshPipeline for DebugLinePipeline { + type Key = (bool, MeshPipelineKey); + + fn specialize( + &self, + (depth_test, key): Self::Key, + layout: &MeshVertexBufferLayout, + ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> { + //use VertexFormat::{Float32x3, Float32x4}; + + let mut shader_defs = Vec::new(); + shader_defs.push("LINES_3D".to_string()); + if depth_test { + shader_defs.push("DEPTH_TEST_ENABLED".to_string()); + } + + let vertex_buffer_layout = layout.get_layout(&[ + Mesh::ATTRIBUTE_POSITION.at_shader_location(0), + Mesh::ATTRIBUTE_COLOR.at_shader_location(1), + ])?; + let (label, blend, depth_write_enabled); + if key.contains(MeshPipelineKey::TRANSPARENT_MAIN_PASS) { + label = "transparent_mesh_pipeline".into(); + blend = Some(BlendState::ALPHA_BLENDING); + // For the transparent pass, fragments that are closer will be alpha + // blended but their depth is not written to the depth buffer. + depth_write_enabled = false; + } else { + label = "opaque_mesh_pipeline".into(); + blend = Some(BlendState::REPLACE); + // For the opaque and alpha mask passes, fragments that are closer + // will replace the current fragment value in the output and the depth is + // written to the depth buffer. + depth_write_enabled = true; + } + + Ok(RenderPipelineDescriptor { + vertex: VertexState { + shader: self.shader.clone_weak(), + entry_point: "vertex".into(), + shader_defs: shader_defs.clone(), + buffers: vec![vertex_buffer_layout], + }, + fragment: Some(FragmentState { + shader: self.shader.clone_weak(), + shader_defs, + entry_point: "fragment".into(), + targets: vec![ColorTargetState { + format: TextureFormat::bevy_default(), + blend, + write_mask: ColorWrites::ALL, + }], + }), + layout: Some(vec![self.mesh_pipeline.view_layout.clone()]), + primitive: PrimitiveState { + front_face: FrontFace::Ccw, + cull_mode: None, + unclipped_depth: false, + polygon_mode: PolygonMode::Fill, + conservative: false, + topology: PrimitiveTopology::LineList, + strip_index_format: None, + }, + depth_stencil: Some(DepthStencilState { + format: TextureFormat::Depth32Float, + depth_write_enabled, + depth_compare: CompareFunction::Greater, + stencil: StencilState { + front: StencilFaceState::IGNORE, + back: StencilFaceState::IGNORE, + read_mask: 0, + write_mask: 0, + }, + bias: DepthBiasState { + constant: 0, + slope_scale: 0.0, + clamp: 0.0, + }, + }), + multisample: MultisampleState { + count: key.msaa_samples(), + mask: !0, + alpha_to_coverage_enabled: false, + }, + label: Some(label), + }) + } + } + + pub(crate) fn queue( + opaque_3d_draw_functions: Res<DrawFunctions<Opaque3d>>, + debug_line_pipeline: Res<DebugLinePipeline>, + mut pipelines: ResMut<SpecializedMeshPipelines<DebugLinePipeline>>, + mut pipeline_cache: ResMut<PipelineCache>, + render_meshes: Res<RenderAssets<Mesh>>, + msaa: Res<Msaa>, + material_meshes: Query<(Entity, &MeshUniform, &Handle<Mesh>), With<RenderDebugLinesMesh>>, + config: Res<DebugLinesConfig>, + mut views: Query<(&ExtractedView, &mut RenderPhase<Opaque3d>)>, + ) { + let draw_custom = opaque_3d_draw_functions + .read() + .get_id::<DrawDebugLines>() + .unwrap(); + let key = MeshPipelineKey::from_msaa_samples(msaa.samples); + for (view, mut transparent_phase) in views.iter_mut() { + let view_matrix = view.transform.compute_matrix(); + let view_row_2 = view_matrix.row(2); + for (entity, mesh_uniform, mesh_handle) in material_meshes.iter() { + if let Some(mesh) = render_meshes.get(mesh_handle) { + let pipeline = pipelines + .specialize( + &mut pipeline_cache, + &debug_line_pipeline, + (config.depth_test, key), + &mesh.layout, + ) + .unwrap(); + transparent_phase.add(Opaque3d { + entity, + pipeline, + draw_function: draw_custom, + distance: view_row_2.dot(mesh_uniform.transform.col(3)), + }); + } + } + } + } + + pub(crate) type DrawDebugLines = ( + SetItemPipeline, + SetMeshViewBindGroup<0>, + SetMeshBindGroup<1>, + DrawMesh, + ); +} + +pub mod r2d { + use bevy::{ + asset::Handle, + core::FloatOrd, + core_pipeline::Transparent2d, + prelude::*, + render::{ + mesh::MeshVertexBufferLayout, + render_asset::RenderAssets, + render_phase::{DrawFunctions, RenderPhase, SetItemPipeline}, + render_resource::{ + BlendState, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, + DepthStencilState, FragmentState, FrontFace, MultisampleState, PipelineCache, + PolygonMode, PrimitiveState, PrimitiveTopology, RenderPipelineDescriptor, Shader, + SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines, + StencilFaceState, StencilState, TextureFormat, VertexAttribute, VertexBufferLayout, + VertexFormat, VertexState, VertexStepMode, + }, + texture::BevyDefault, + view::{Msaa, VisibleEntities}, + }, + sprite::{ + DrawMesh2d, Mesh2dHandle, Mesh2dPipeline, Mesh2dPipelineKey, Mesh2dUniform, + SetMesh2dBindGroup, SetMesh2dViewBindGroup, + }, + }; + + use crate::lines::{RenderDebugLinesMesh, DEBUG_LINES_SHADER_HANDLE}; + + pub(crate) struct DebugLinePipeline { + mesh_pipeline: Mesh2dPipeline, + shader: Handle<Shader>, + } + impl FromWorld for DebugLinePipeline { + fn from_world(render_world: &mut World) -> Self { + DebugLinePipeline { + mesh_pipeline: render_world + .get_resource::<Mesh2dPipeline>() + .unwrap() + .clone(), + shader: DEBUG_LINES_SHADER_HANDLE.typed(), + } + } + } + + impl SpecializedMeshPipeline for DebugLinePipeline { + type Key = Mesh2dPipelineKey; + + fn specialize( + &self, + key: Self::Key, + layout: &MeshVertexBufferLayout, + ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> { + /* + let mut shader_defs = Vec::new(); + shader_defs.push("LINES_3D".to_string()); + if depth_test { + shader_defs.push("DEPTH_TEST_ENABLED".to_string()); + } + */ + + let vertex_buffer_layout = layout.get_layout(&[ + Mesh::ATTRIBUTE_POSITION.at_shader_location(0), + Mesh::ATTRIBUTE_COLOR.at_shader_location(1), + ])?; + + Ok(RenderPipelineDescriptor { + vertex: VertexState { + shader: self.shader.clone_weak(), + entry_point: "vertex".into(), + shader_defs: vec![], + buffers: vec![vertex_buffer_layout], + }, + fragment: Some(FragmentState { + shader: self.shader.clone_weak(), + shader_defs: vec![], + entry_point: "fragment".into(), + targets: vec![ColorTargetState { + format: TextureFormat::bevy_default(), + blend: Some(BlendState::ALPHA_BLENDING), + write_mask: ColorWrites::ALL, + }], + }), + layout: Some(vec![self.mesh_pipeline.view_layout.clone()]), + primitive: PrimitiveState { + front_face: FrontFace::Ccw, + cull_mode: None, + unclipped_depth: false, + polygon_mode: PolygonMode::Fill, + conservative: false, + topology: PrimitiveTopology::LineList, + strip_index_format: None, + }, + depth_stencil: None, + multisample: Mu |
