aboutsummaryrefslogtreecommitdiff
path: root/src_testbed
diff options
context:
space:
mode:
authorSébastien Crozet <developer@crozet.re>2022-04-28 18:24:01 +0200
committerGitHub <noreply@github.com>2022-04-28 18:24:01 +0200
commit488aad0af3f772e14fd85b27bfff6c1db5d23829 (patch)
tree4c19f613750fcd8779714915dbb752ce369a4173 /src_testbed
parent21a31bc1026d17d30b3a5ac35e6bb716dc66be6e (diff)
parent7dc038aec66783d72abda446d6251385e6ad30f4 (diff)
downloadrapier-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.rs6
-rw-r--r--src_testbed/camera3d.rs6
-rw-r--r--src_testbed/debug_render.rs72
-rw-r--r--src_testbed/lib.rs2
-rw-r--r--src_testbed/lines/debuglines.wgsl49
-rw-r--r--src_testbed/lines/debuglines2d.wgsl31
-rw-r--r--src_testbed/lines/mod.rs373
-rw-r--r--src_testbed/lines/render_dim.rs335
-rw-r--r--src_testbed/objects/node.rs36
-rw-r--r--src_testbed/physx_backend.rs2
-rw-r--r--src_testbed/testbed.rs41
-rw-r--r--src_testbed/ui.rs4
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