Skip to content

Commit 1aadc09

Browse files
committed
LineStrip!
1 parent 416585f commit 1aadc09

File tree

4 files changed

+158
-92
lines changed

4 files changed

+158
-92
lines changed

crates/bevy_debug_draw/src/debug_draw.rs

Lines changed: 71 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{f32::consts::TAU, mem};
1+
use std::{f32::consts::TAU, iter};
22

33
use bevy_asset::Handle;
44
use bevy_ecs::system::Resource;
@@ -9,22 +9,28 @@ use bevy_render::prelude::{Color, Mesh};
99
/// Useful for visual debugging.
1010
#[derive(Resource)]
1111
pub struct DebugDraw {
12-
positions: Vec<[f32; 3]>,
13-
colors: Vec<[f32; 4]>,
14-
pub(crate) mesh_handle: Option<Handle<Mesh>>,
12+
pub(crate) list_mesh_handle: Option<Handle<Mesh>>,
13+
pub(crate) list_positions: Vec<[f32; 3]>,
14+
pub(crate) list_colors: Vec<[f32; 4]>,
15+
pub(crate) strip_mesh_handle: Option<Handle<Mesh>>,
16+
pub(crate) strip_positions: Vec<[f32; 3]>,
17+
pub(crate) strip_colors: Vec<[f32; 4]>,
1518
/// The amount of line segments to use when drawing a circle.
1619
///
17-
/// Defaults to `24`.
20+
/// Defaults to `32`.
1821
pub circle_segments: u32,
1922
}
2023

2124
impl Default for DebugDraw {
2225
fn default() -> Self {
2326
DebugDraw {
24-
positions: Vec::new(),
25-
colors: Vec::new(),
26-
mesh_handle: None,
27-
circle_segments: 24,
27+
list_mesh_handle: None,
28+
list_positions: Vec::new(),
29+
list_colors: Vec::new(),
30+
strip_mesh_handle: None,
31+
strip_positions: Vec::new(),
32+
strip_colors: Vec::new(),
33+
circle_segments: 32,
2834
}
2935
}
3036
}
@@ -39,8 +45,9 @@ impl DebugDraw {
3945
/// Draw a line from `start` to `end`.
4046
#[inline]
4147
pub fn line_gradient(&mut self, start: Vec3, end: Vec3, start_color: Color, end_color: Color) {
42-
self.positions.extend([start.to_array(), end.to_array()]);
43-
self.colors.extend([
48+
self.list_positions
49+
.extend([start.to_array(), end.to_array()]);
50+
self.list_colors.extend([
4451
start_color.as_linear_rgba_f32(),
4552
end_color.as_linear_rgba_f32(),
4653
]);
@@ -49,7 +56,7 @@ impl DebugDraw {
4956
/// Draw a line from `start` to `start + vector`.
5057
#[inline]
5158
pub fn ray(&mut self, start: Vec3, vector: Vec3, color: Color) {
52-
self.ray_gradient(start, vector, color, color);
59+
self.line(start, start + vector, color);
5360
}
5461

5562
/// Draw a line from `start` to `start + vector`.
@@ -68,25 +75,23 @@ impl DebugDraw {
6875
#[inline]
6976
pub fn circle(&mut self, position: Vec3, normal: Vec3, radius: f32, color: Color) {
7077
let rotation = Quat::from_rotation_arc(Vec3::Z, normal);
71-
self.positions
72-
.extend((0..self.circle_segments).into_iter().flat_map(|i| {
73-
let mut angle = i as f32 * TAU / self.circle_segments as f32;
74-
let start = rotation * (Vec2::from(angle.sin_cos()) * radius).extend(0.) + position;
7578

76-
angle += TAU / self.circle_segments as f32;
77-
let end = rotation * (Vec2::from(angle.sin_cos()) * radius).extend(0.) + position;
79+
let positions = self
80+
.circle_inner(radius)
81+
.map(|vec2| (rotation * vec2.extend(0.) + position).to_array())
82+
.chain(iter::once(Vec3::NAN.to_array()));
7883

79-
[start.to_array(), end.to_array()]
80-
}));
81-
82-
self.colors.extend(
83-
std::iter::repeat(color.as_linear_rgba_f32()).take(self.circle_segments as usize * 2),
84-
);
84+
self.strip_positions.extend(positions);
85+
self.add_strip_color(color, (self.circle_segments + 1) as usize);
8586
}
8687

8788
/// Draw a sphere.
8889
#[inline]
8990
pub fn sphere(&mut self, position: Vec3, radius: f32, color: Color) {
91+
self.strip_colors
92+
.reserve((self.circle_segments + 1) as usize * 3);
93+
self.strip_positions
94+
.reserve((self.circle_segments + 1) as usize * 3);
9095
self.circle(position, Vec3::X, radius, color);
9196
self.circle(position, Vec3::Y, radius, color);
9297
self.circle(position, Vec3::Z, radius, color);
@@ -100,9 +105,10 @@ impl DebugDraw {
100105
let tr = (position + rotation * vec3(half_size.x, half_size.y, 0.)).to_array();
101106
let bl = (position + rotation * vec3(-half_size.x, -half_size.y, 0.)).to_array();
102107
let br = (position + rotation * vec3(half_size.x, -half_size.y, 0.)).to_array();
103-
self.positions.extend([tl, tr, tr, br, br, bl, bl, tl]);
104-
self.colors
105-
.extend(std::iter::repeat(color.as_linear_rgba_f32()).take(8));
108+
109+
self.strip_positions
110+
.extend([tl, tr, br, bl, tl, [f32::NAN; 3]]);
111+
self.add_strip_color(color, 5);
106112
}
107113

108114
/// Draw a box.
@@ -119,19 +125,19 @@ impl DebugDraw {
119125
let trb = (position + rotation * vec3(half_size.x, half_size.y, -half_size.z)).to_array();
120126
let blb = (position + rotation * vec3(-half_size.x, -half_size.y, -half_size.z)).to_array();
121127
let brb = (position + rotation * vec3(half_size.x, -half_size.y, -half_size.z)).to_array();
122-
self.positions.extend([
128+
129+
self.list_positions.extend([
123130
tlf, trf, trf, brf, brf, blf, blf, tlf, // Front
124131
tlb, trb, trb, brb, brb, blb, blb, tlb, // Back
125132
tlf, tlb, trf, trb, brf, brb, blf, blb, // Front to back
126133
]);
127-
self.colors
128-
.extend(std::iter::repeat(color.as_linear_rgba_f32()).take(24));
134+
self.add_list_color(color, 24);
129135
}
130136

131137
/// Draw a line from `start` to `end`.
132138
#[inline]
133139
pub fn line_2d(&mut self, start: Vec2, end: Vec2, color: Color) {
134-
self.line_gradient_2d(start, end, color, color);
140+
self.line(start.extend(0.), end.extend(0.), color);
135141
}
136142

137143
/// Draw a line from `start` to `end`.
@@ -149,7 +155,7 @@ impl DebugDraw {
149155
/// Draw a line from `start` to `start + vector`.
150156
#[inline]
151157
pub fn ray_2d(&mut self, start: Vec2, vector: Vec2, color: Color) {
152-
self.ray_gradient_2d(start, vector, color, color);
158+
self.line_2d(start, start + vector, color);
153159
}
154160

155161
/// Draw a line from `start` to `start + vector`.
@@ -167,7 +173,13 @@ impl DebugDraw {
167173
// Draw a circle.
168174
#[inline]
169175
pub fn circle_2d(&mut self, position: Vec2, radius: f32, color: Color) {
170-
self.circle(position.extend(0.), Vec3::Z, radius, color);
176+
let positions = self
177+
.circle_inner(radius)
178+
.map(|vec2| (vec2 + position).extend(0.).to_array())
179+
.chain(iter::once([f32::NAN; 3]));
180+
181+
self.strip_positions.extend(positions);
182+
self.add_strip_color(color, (self.circle_segments + 1) as usize);
171183
}
172184

173185
/// Draw a rectangle.
@@ -181,17 +193,35 @@ impl DebugDraw {
181193
);
182194
}
183195

184-
/// Clear everything drawn up to this point, this frame.
185196
#[inline]
186-
pub fn clear(&mut self) {
187-
self.positions.clear();
188-
self.colors.clear();
197+
fn add_strip_color(&mut self, color: Color, amount: usize) {
198+
self.strip_colors.extend(
199+
iter::repeat(color.as_linear_rgba_f32())
200+
.take(amount)
201+
.chain(iter::once([f32::NAN; 4])),
202+
);
189203
}
190204

191-
/// Take the positions and colors data from `self` and overwrite the `mesh`'s vertex positions and colors.
192205
#[inline]
193-
pub(crate) fn update_mesh(&mut self, mesh: &mut Mesh) {
194-
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, mem::take(&mut self.positions));
195-
mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, mem::take(&mut self.colors));
206+
fn add_list_color(&mut self, color: Color, amount: usize) {
207+
self.list_colors
208+
.extend(iter::repeat(color.as_linear_rgba_f32()).take(amount));
209+
}
210+
211+
fn circle_inner(&self, radius: f32) -> impl Iterator<Item = Vec2> {
212+
let circle_segments = self.circle_segments;
213+
(0..(circle_segments + 1)).into_iter().map(move |i| {
214+
let angle = i as f32 * TAU / circle_segments as f32;
215+
Vec2::from(angle.sin_cos()) * radius
216+
})
217+
}
218+
219+
/// Clear everything drawn up to this point, this frame.
220+
#[inline]
221+
pub fn clear(&mut self) {
222+
self.list_positions.clear();
223+
self.list_colors.clear();
224+
self.strip_positions.clear();
225+
self.strip_colors.clear();
196226
}
197227
}

crates/bevy_debug_draw/src/lib.rs

Lines changed: 79 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
use std::mem;
2+
13
use bevy_app::{CoreStage, Plugin};
2-
use bevy_asset::{load_internal_asset, Assets, HandleUntyped};
4+
use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped};
35
use bevy_ecs::{
46
component::Component,
57
entity::Entity,
6-
query::With,
78
system::{Commands, Query, Res, ResMut, Resource},
89
};
910
use bevy_reflect::TypeUuid;
@@ -102,63 +103,99 @@ impl Default for DebugDrawConfig {
102103
}
103104
}
104105

105-
#[derive(Component)]
106-
struct DebugDrawMesh;
106+
#[derive(Component, Clone, Copy)]
107+
struct DebugDrawMesh {
108+
topology: PrimitiveTopology,
109+
}
107110

108111
fn update(
109112
config: Res<DebugDrawConfig>,
110113
mut debug_draw: ResMut<DebugDraw>,
111114
mut meshes: ResMut<Assets<Mesh>>,
112115
mut commands: Commands,
113116
) {
114-
let mesh = debug_draw
115-
.mesh_handle
116-
.as_ref()
117-
.and_then(|handle| meshes.get_mut(handle));
118-
match mesh {
119-
Some(mesh) => {
120-
if config.enabled {
121-
debug_draw.update_mesh(mesh);
122-
} else {
123-
debug_draw.clear();
124-
mesh.remove_attribute(Mesh::ATTRIBUTE_POSITION);
125-
mesh.remove_attribute(Mesh::ATTRIBUTE_COLOR);
117+
let mut f = |handle: &mut Option<Handle<Mesh>>,
118+
positions: &mut Vec<[f32; 3]>,
119+
colors: &mut Vec<[f32; 4]>,
120+
topology: PrimitiveTopology| {
121+
let mesh = handle.as_ref().and_then(|handle| meshes.get_mut(handle));
122+
match mesh {
123+
Some(mesh) => {
124+
if config.enabled {
125+
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, mem::take(positions));
126+
mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, mem::take(colors));
127+
} else {
128+
positions.clear();
129+
colors.clear();
130+
mesh.remove_attribute(Mesh::ATTRIBUTE_POSITION);
131+
mesh.remove_attribute(Mesh::ATTRIBUTE_COLOR);
132+
}
126133
}
127-
}
128-
None => {
129-
if config.enabled {
130-
let mut mesh = Mesh::new(PrimitiveTopology::LineList);
131-
debug_draw.update_mesh(&mut mesh);
132-
let mesh_handle = meshes.add(mesh);
133-
commands.spawn((
134-
SpatialBundle::VISIBLE_IDENTITY,
135-
DebugDrawMesh,
136-
#[cfg(feature = "bevy_pbr")]
137-
(
138-
mesh_handle.clone_weak(),
139-
NotShadowCaster,
140-
NotShadowReceiver,
141-
NoFrustumCulling,
142-
),
143-
#[cfg(feature = "bevy_sprite")]
144-
Mesh2dHandle(mesh_handle.clone_weak()),
145-
));
146-
debug_draw.mesh_handle = Some(mesh_handle);
147-
} else {
148-
debug_draw.clear();
134+
None => {
135+
if config.enabled {
136+
let mut mesh = Mesh::new(topology);
137+
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, mem::take(positions));
138+
mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, mem::take(colors));
139+
let mesh_handle = meshes.add(mesh);
140+
commands.spawn((
141+
SpatialBundle::VISIBLE_IDENTITY,
142+
DebugDrawMesh { topology },
143+
#[cfg(feature = "bevy_pbr")]
144+
(
145+
mesh_handle.clone_weak(),
146+
NotShadowCaster,
147+
NotShadowReceiver,
148+
NoFrustumCulling,
149+
),
150+
#[cfg(feature = "bevy_sprite")]
151+
Mesh2dHandle(mesh_handle.clone_weak()),
152+
));
153+
*handle = Some(mesh_handle);
154+
} else {
155+
positions.clear();
156+
colors.clear();
157+
}
149158
}
150159
}
151-
}
160+
};
161+
162+
let DebugDraw {
163+
list_mesh_handle,
164+
list_positions,
165+
list_colors,
166+
..
167+
} = &mut *debug_draw;
168+
169+
f(
170+
list_mesh_handle,
171+
list_positions,
172+
list_colors,
173+
PrimitiveTopology::LineList,
174+
);
175+
176+
let DebugDraw {
177+
strip_mesh_handle,
178+
strip_positions,
179+
strip_colors,
180+
..
181+
} = &mut *debug_draw;
182+
183+
f(
184+
strip_mesh_handle,
185+
strip_positions,
186+
strip_colors,
187+
PrimitiveTopology::LineStrip,
188+
);
152189
}
153190

154191
/// Move the [`DebugDrawMesh`] marker Component and the [`DebugDrawConfig`] Resource to the render context.
155192
fn extract(
156193
mut commands: Commands,
157-
query: Extract<Query<Entity, With<DebugDrawMesh>>>,
194+
query: Extract<Query<(Entity, &DebugDrawMesh)>>,
158195
config: Extract<Res<DebugDrawConfig>>,
159196
) {
160-
for entity in &query {
161-
commands.get_or_spawn(entity).insert(DebugDrawMesh);
197+
for (entity, debug_draw) in &query {
198+
commands.get_or_spawn(entity).insert(*debug_draw);
162199
}
163200

164201
if config.is_changed() {

crates/bevy_debug_draw/src/pipeline_2d.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use bevy_asset::Handle;
22
use bevy_core_pipeline::core_2d::Transparent2d;
33
use bevy_ecs::{
4-
query::With,
54
system::{Query, Res, ResMut, Resource},
65
world::{FromWorld, World},
76
};
@@ -70,7 +69,7 @@ impl SpecializedMeshPipeline for DebugLinePipeline {
7069
unclipped_depth: false,
7170
polygon_mode: PolygonMode::Fill,
7271
conservative: false,
73-
topology: PrimitiveTopology::LineList,
72+
topology: key.primitive_topology(),
7473
strip_index_format: None,
7574
},
7675
depth_stencil: None,
@@ -99,19 +98,19 @@ pub(crate) fn queue(
9998
mut specialized_pipelines: ResMut<SpecializedMeshPipelines<DebugLinePipeline>>,
10099
render_meshes: Res<RenderAssets<Mesh>>,
101100
msaa: Res<Msaa>,
102-
material_meshes: Query<&Mesh2dHandle, With<DebugDrawMesh>>,
101+
material_meshes: Query<(&Mesh2dHandle, &DebugDrawMesh)>,
103102
mut views: Query<(&VisibleEntities, &mut RenderPhase<Transparent2d>)>,
104103
) {
105104
for (view, mut phase) in &mut views {
106105
let draw_mesh2d = draw2d_functions.read().get_id::<DrawDebugLines>().unwrap();
107106
let msaa_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples);
108107

109108
for visible_entity in &view.entities {
110-
let Ok(mesh_handle) = material_meshes.get(*visible_entity) else { continue; };
109+
let Ok((mesh_handle, debug_draw)) = material_meshes.get(*visible_entity) else { continue; };
111110
let Some(mesh) = render_meshes.get(&mesh_handle.0) else { continue; };
112111

113112
let mesh_key =
114-
msaa_key | Mesh2dPipelineKey::from_primitive_topology(PrimitiveTopology::LineList);
113+
msaa_key | Mesh2dPipelineKey::from_primitive_topology(debug_draw.topology);
115114
let pipeline = specialized_pipelines
116115
.specialize(
117116
&mut pipeline_cache,

0 commit comments

Comments
 (0)