Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debug Draw #1625

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ bevy_winit = ["bevy_internal/bevy_winit"]
trace_chrome = ["bevy_internal/trace_chrome"]
trace = ["bevy_internal/trace"]
wgpu_trace = ["bevy_internal/wgpu_trace"]
debug_draw = ["bevy_internal/bevy_debug_draw"]

# Image format support for texture loading (PNG and HDR are enabled by default)
hdr = ["bevy_internal/hdr"]
Expand Down Expand Up @@ -119,6 +120,11 @@ path = "examples/2d/texture_atlas.rs"
name = "3d_scene"
path = "examples/3d/3d_scene.rs"

[[example]]
name = "debug_draw_3d"
path = "examples/3d/debug_draw_3d.rs"
required-features = ["debug_draw"]

[[example]]
name = "load_gltf"
path = "examples/3d/load_gltf.rs"
Expand Down
28 changes: 28 additions & 0 deletions crates/bevy_debug_draw/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "bevy_debug_draw"
version = "0.4.0"
edition = "2018"
authors = [
"Bevy Contributors <bevyengine@gmail.com>",
"Fabian Krauser <The5_1@outlook.com>",
]
description = "Provides immediate mode debug drawing for the Bevy Engine"
homepage = "https://bevyengine.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT"
keywords = ["bevy"]


[dependencies]
# bevy
bevy_render = { path = "../bevy_render", version = "0.4.0" }
bevy_app = { path = "../bevy_app", version = "0.4.0" }
bevy_asset = { path = "../bevy_asset", version = "0.4.0" }
bevy_core = { path = "../bevy_core", version = "0.4.0" }
bevy_log = { path = "../bevy_log", version = "0.4.0" }
bevy_derive = { path = "../bevy_derive", version = "0.4.0" }
bevy_ecs = { path = "../bevy_ecs", version = "0.4.0" }
bevy_math = { path = "../bevy_math", version = "0.4.0" }
bevy_reflect = { path = "../bevy_reflect", version = "0.4.0", features = ["bevy"] }
bevy_transform = { path = "../bevy_transform", version = "0.4.0" }
bevy_utils = { path = "../bevy_utils", version = "0.4.0" }
211 changes: 211 additions & 0 deletions crates/bevy_debug_draw/src/debug_draw_3d.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
use bevy_app::{AppBuilder, CoreStage, Plugin};
use bevy_asset::{AddAsset, Assets, Handle};
use bevy_ecs::{
prelude::{Commands, Query, ResMut, With},
schedule::*,
system::*,
};
use bevy_log::info;
use bevy_math::*;
use bevy_reflect::TypeUuid;
use bevy_render::{
mesh::*,
pipeline::{CullMode, PipelineDescriptor, PrimitiveTopology, RenderPipeline},
prelude::{Color, MeshBundle, RenderPipelines},
render_graph::{base, AssetRenderResourcesNode, RenderGraph},
renderer::RenderResources,
shader::{Shader, ShaderStage, ShaderStages},
};
use bevy_transform::{
components::{GlobalTransform, Transform},
TransformSystem,
};

use crate::gizmo::CoordinateGizmo;
/// Bevy immediate mode debug drawing:
/// This crate introduces a DebugDraw3D resource which provides functions such as `draw_line(start, end, color)`
/// Whenever such a draw_line function is called, a set of vertices is added to the DebugDraw3D objects data.
/// At the end of the frame the internal data is copied into a mesh entity for drawing and then cleared from the DebugDraw3D object.
/// With this, no persistent line drawing is possible and lines have to be added every frame (hence immediate mode).
/// For convenience a system called `debug_draw_all_gizmos` is provided that draws a coordinate gizmo for any `GlobalTransform`.
///
/// ToDo:
/// * Add more convenience functions such as `draw_arrow(start, end, head_size, color)`, `draw_circle(origin, radius, axis, color)`, `draw_aabb(min,max,color)`.
/// * Modify the shader and access the depth buffer and perform hidden-line rendering rather than a binary depth test for better line visualization.
/// * Add the `debug_draw_all_gizmos` system to the plugin, using a parameter to turn it on or off.
/// * Add transparent triangle drawing (useful to visually project a line down on a plane) and matching utility functions.
/// * Add timed or persistent drawing: This requires storing `Line` structs containing a lifetime rather than directly pushing to an array.
/// * Even though this is a debug feature, there current approach may likely not be the most performant solution and optimizations/refactoring should be applied.

pub struct DebugDrawPlugin;
impl Plugin for DebugDrawPlugin {
fn build(&self, app: &mut AppBuilder) {
app.add_asset::<DebugDraw3DMaterial>()
.init_resource::<DebugDraw3D>()
.add_startup_system(setup_debug_draw_3d.system())
.add_system_to_stage(
CoreStage::PostUpdate,
update_debug_draw_3d
.system()
.after(TransformSystem::TransformPropagate),
);
}
}

/// DebugDraw3D Resource providing functions for immediate mode drawing
pub struct DebugDraw3D {
// The mesh data is held as plain arrays here
// If we wish to extend to more than just lines we may need multiple pairs that will later be ass, e.g. vertices_line and vertices_triangle
vertices: Vec<[f32; 3]>,
colors: Vec<[f32; 4]>,
dirty: bool,
clear: bool,
}

impl Default for DebugDraw3D {
fn default() -> Self {
DebugDraw3D {
vertices: Default::default(),
colors: Default::default(),
dirty: true,
clear: true,
}
}
}

impl DebugDraw3D {
pub fn draw_line(&mut self, start: Vec3, end: Vec3, color: Color) {
self.vertices.push(start.into());
self.vertices.push(end.into());
self.colors.push(color.into());
self.colors.push(color.into());
self.set_dirty();
}

/// Turning this off results in lines not being cleared anymore.
/// You will have to use clear() manually.
pub fn automatic(&mut self, clear: bool) {
self.clear = clear;
}

/// You do not have to call this in automatic mode.
pub fn clear(&mut self) {
self.vertices.clear();
self.colors.clear();
}

fn set_dirty(&mut self) {
self.dirty = true;
}

fn reset(&mut self) {
if self.clear {
self.clear();
}
self.dirty = false;
}
}
/// This component marks the internal entity that does the mesh drawing.
#[derive(Default)]
struct DebugDraw3DComponent;

/// The Material holding the shader for debug drawing
#[derive(RenderResources, Default, TypeUuid)]
#[uuid = "188f0f97-60b2-476a-a749-7a0103adeeba"]
pub struct DebugDraw3DMaterial;

///This system sets up the entity holding the actual mesh for drawing as well as the render pipeline step for the shader.
fn setup_debug_draw_3d(
mut commands: Commands,
mut pipelines: ResMut<Assets<PipelineDescriptor>>,
mut shaders: ResMut<Assets<Shader>>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<DebugDraw3DMaterial>>,
mut render_graph: ResMut<RenderGraph>,
) {
// Crate a shader Pipeline
let mut p = PipelineDescriptor::default_config(ShaderStages {
vertex: shaders.add(Shader::from_glsl(
ShaderStage::Vertex,
include_str!("shaders/debugDrawLine.vert"),
)),
fragment: Some(shaders.add(Shader::from_glsl(
ShaderStage::Fragment,
include_str!("shaders/debugDrawLine.frag"),
))),
});
p.primitive.topology = PrimitiveTopology::LineList;
p.primitive.cull_mode = CullMode::None;
let pipeline_handle = pipelines.add(p);
let pipeline = RenderPipelines::from_pipelines(vec![RenderPipeline::new(pipeline_handle)]);

// add the material to the pipeline
render_graph.add_system_node(
"debug_draw_3d",
AssetRenderResourcesNode::<DebugDraw3DMaterial>::new(false),
);
// connect that node stage the MAIN_PASS node
render_graph
.add_node_edge("debug_draw_3d", base::node::MAIN_PASS)
.unwrap();

let material_instance = materials.add(DebugDraw3DMaterial {});

// Spawn a entity that will do the debug drawing with its mesh
commands
.spawn(MeshBundle {
mesh: meshes.add(Mesh::from(CoordinateGizmo { size: 1.0 })),
render_pipelines: pipeline, //we have to tell our mesh what pipeline to render in...
transform: Transform::from_translation(Vec3::ZERO),
..Default::default()
})
.with(material_instance)
.with(DebugDraw3DComponent::default());

info!("Loaded debug lines plugin.");
}

/// This system updates the debug draw Entity with the data from
fn update_debug_draw_3d(
mut debug_draw: ResMut<DebugDraw3D>,
mut meshes: ResMut<Assets<Mesh>>,
query: Query<&Handle<Mesh>, With<DebugDraw3DComponent>>,
) {
if !debug_draw.dirty {
return;
} else {
for mesh in query.iter() {
if let Some(mesh) = meshes.get_mut(mesh) {
mesh.set_attribute(
Mesh::ATTRIBUTE_POSITION,
VertexAttributeValues::Float3(debug_draw.vertices.clone()),
);
mesh.set_attribute(
"Vertex_Color",
VertexAttributeValues::Float4(debug_draw.colors.clone()),
);
}
}
}
debug_draw.reset();
}

pub fn debug_draw_all_gizmos(mut debug_draw: ResMut<DebugDraw3D>, query: Query<&GlobalTransform>) {
for transform in query.iter() {
debug_draw.draw_line(
transform.translation,
transform.translation + transform.local_x(),
Color::RED,
);
debug_draw.draw_line(
transform.translation,
transform.translation + transform.local_y(),
Color::GREEN,
);
debug_draw.draw_line(
transform.translation,
transform.translation + transform.local_z(),
Color::BLUE,
);
}
}
45 changes: 45 additions & 0 deletions crates/bevy_debug_draw/src/gizmo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use bevy_render::{mesh::*, pipeline::PrimitiveTopology};

pub struct CoordinateGizmo {
pub size: f32,
}

impl CoordinateGizmo {
pub fn new(size: f32) -> CoordinateGizmo {
CoordinateGizmo { size }
}
}

impl Default for CoordinateGizmo {
fn default() -> Self {
CoordinateGizmo { size: 1.0 }
}
}

impl From<CoordinateGizmo> for Mesh {
fn from(shape: CoordinateGizmo) -> Self {
let mut mesh = Mesh::new(PrimitiveTopology::LineList);
let vertices = vec![
[0.0, 0.0, 0.0],
[shape.size, 0.0, 0.0],
[0.0, 0.0, 0.0],
[0.0, shape.size, 0.0],
[0.0, 0.0, 0.0],
[0.0, 0.0, shape.size],
];
mesh.set_attribute(
Mesh::ATTRIBUTE_POSITION,
VertexAttributeValues::Float3(vertices),
);
let colors = vec![
[1.0, 0.0, 0.0, 1.0],
[1.0, 0.0, 0.0, 1.0],
[0.0, 1.0, 0.0, 1.0],
[0.0, 1.0, 0.0, 1.0],
[0.0, 0.0, 1.0, 1.0],
[0.0, 0.0, 1.0, 1.0],
];
mesh.set_attribute("Vertex_Color", VertexAttributeValues::Float4(colors));
mesh
}
}
2 changes: 2 additions & 0 deletions crates/bevy_debug_draw/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod debug_draw_3d;
pub mod gizmo;
6 changes: 6 additions & 0 deletions crates/bevy_debug_draw/src/shaders/debugDrawLine.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#version 450
layout(location = 0) in vec4 vColor;
layout(location = 0) out vec4 o_Target;
void main() {
o_Target = vColor;
}
15 changes: 15 additions & 0 deletions crates/bevy_debug_draw/src/shaders/debugDrawLine.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#version 450
layout(location = 0) in vec3 Vertex_Position;
layout(location = 1) in vec4 Vertex_Color;
layout(location = 0) out vec4 vColor;

layout(set = 0, binding = 0) uniform Camera {
mat4 ViewProj;
};
layout(set = 1, binding = 0) uniform Transform {
mat4 Model;
};
void main() {
vColor = Vertex_Color;
gl_Position = ViewProj * Model * vec4(Vertex_Position, 1.0);
}
1 change: 1 addition & 0 deletions crates/bevy_internal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ bevy_audio = { path = "../bevy_audio", optional = true, version = "0.4.0" }
bevy_gltf = { path = "../bevy_gltf", optional = true, version = "0.4.0" }
bevy_pbr = { path = "../bevy_pbr", optional = true, version = "0.4.0" }
bevy_render = { path = "../bevy_render", optional = true, version = "0.4.0" }
bevy_debug_draw = { path = "../bevy_debug_draw", optional = true, version = "0.4.0" }
bevy_dynamic_plugin = { path = "../bevy_dynamic_plugin", optional = true, version = "0.4.0" }
bevy_sprite = { path = "../bevy_sprite", optional = true, version = "0.4.0" }
bevy_text = { path = "../bevy_text", optional = true, version = "0.4.0" }
Expand Down
6 changes: 6 additions & 0 deletions crates/bevy_internal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ pub mod render {
pub use bevy_render::*;
}

#[cfg(feature = "bevy_debug_draw")]
pub mod debug_draw {
//! Immediate mode debug drawing, like a visual println.
pub use bevy_debug_draw::*;
}

#[cfg(feature = "bevy_sprite")]
pub mod sprite {
//! Items for sprites, rects, texture atlases, etc.
Expand Down
Loading