Skip to content

Commit

Permalink
Wireframe Rendering Pipeline (#562)
Browse files Browse the repository at this point in the history
This PR implements wireframe rendering.

Usage:

This is now ready as soon as #1401 gets merged.


Usage:

```rust
    app
        .insert_resource(WgpuOptions {
            name: Some("3d_scene"),
            features: WgpuFeatures::NON_FILL_POLYGON_MODE,
            ..Default::default()
        }) // To enable the NON_FILL_POLYGON_MODE feature
        .add_plugin(WireframePlugin)
        .run();

```

Now we just need to add the Wireframe component on an entity, and it'll draw. its wireframe.


We can also enable wireframe drawing globally by setting the global property in the `WireframeConfig` resource to `true`.



Co-authored-by: Zhixing Zhang <me@neoto.xin>
  • Loading branch information
Neo-Zhixing and Neo-Zhixing committed Mar 4, 2021
1 parent 079b3ad commit d9fb61d
Show file tree
Hide file tree
Showing 7 changed files with 240 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ path = "examples/3d/texture.rs"
name = "update_gltf_scene"
path = "examples/3d/update_gltf_scene.rs"

[[example]]
name = "wireframe"
path = "examples/3d/wireframe.rs"

[[example]]
name = "z_sort_debug"
path = "examples/3d/z_sort_debug.rs"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod render_graph;
pub mod renderer;
pub mod shader;
pub mod texture;
pub mod wireframe;

use bevy_ecs::{IntoExclusiveSystem, IntoSystem, SystemStage};
use bevy_reflect::RegisterTypeBuilder;
Expand Down
122 changes: 122 additions & 0 deletions crates/bevy_render/src/wireframe/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
use crate::{
draw::DrawContext,
mesh::Indices,
pipeline::{PipelineDescriptor, PipelineSpecialization, RenderPipeline},
prelude::*,
shader::Shader,
};
use bevy_app::prelude::*;
use bevy_asset::{Assets, Handle, HandleUntyped};
use bevy_ecs::{IntoSystem, Mut, Query, QuerySet, Res, With};
use bevy_reflect::{Reflect, ReflectComponent, TypeUuid};
use bevy_utils::HashSet;

mod pipeline;

pub const WIREFRAME_PIPELINE_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(PipelineDescriptor::TYPE_UUID, 0x137c75ab7e9ad7f5);

#[derive(Debug, Default)]
pub struct WireframePlugin;

impl Plugin for WireframePlugin {
fn build(&self, app: &mut AppBuilder) {
app.init_resource::<WireframeConfig>()
.add_system_to_stage(crate::RenderStage::Draw, draw_wireframes_system.system());
let resources = app.resources();
let mut shaders = resources.get_mut::<Assets<Shader>>().unwrap();
let mut pipelines = resources.get_mut::<Assets<PipelineDescriptor>>().unwrap();
pipelines.set(
WIREFRAME_PIPELINE_HANDLE,
pipeline::build_wireframe_pipeline(&mut shaders),
);
}
}

#[derive(Debug, Clone, Reflect, Default)]
#[reflect(Component)]
pub struct Wireframe;

#[derive(Debug, Clone)]
pub struct WireframeConfig {
pub global: bool,
}

impl Default for WireframeConfig {
fn default() -> Self {
WireframeConfig { global: false }
}
}

pub fn draw_wireframes_system(
mut draw_context: DrawContext,
msaa: Res<Msaa>,
meshes: Res<Assets<Mesh>>,
wireframe_config: Res<WireframeConfig>,
mut query: QuerySet<(
Query<(&mut Draw, &mut RenderPipelines, &Handle<Mesh>, &Visible)>,
Query<(&mut Draw, &mut RenderPipelines, &Handle<Mesh>, &Visible), With<Wireframe>>,
)>,
) {
let iterator = |(mut draw, mut render_pipelines, mesh_handle, visible): (
Mut<Draw>,
Mut<RenderPipelines>,
&Handle<Mesh>,
&Visible,
)| {
if !visible.is_visible {
return;
}

// don't render if the mesh isn't loaded yet
let mesh = if let Some(mesh) = meshes.get(mesh_handle) {
mesh
} else {
return;
};

let mut render_pipeline = RenderPipeline::specialized(
WIREFRAME_PIPELINE_HANDLE.typed(),
PipelineSpecialization {
sample_count: msaa.samples,
strip_index_format: None,
shader_specialization: Default::default(),
primitive_topology: mesh.primitive_topology(),
dynamic_bindings: render_pipelines
.bindings
.iter_dynamic_bindings()
.map(|name| name.to_string())
.collect::<HashSet<String>>(),
vertex_buffer_layout: mesh.get_vertex_buffer_layout(),
},
);
render_pipeline.dynamic_bindings_generation =
render_pipelines.bindings.dynamic_bindings_generation();

draw_context
.set_pipeline(
&mut draw,
&render_pipeline.pipeline,
&render_pipeline.specialization,
)
.unwrap();
draw_context
.set_bind_groups_from_bindings(&mut draw, &mut [&mut render_pipelines.bindings])
.unwrap();
draw_context
.set_vertex_buffers_from_bindings(&mut draw, &[&render_pipelines.bindings])
.unwrap();

match mesh.indices() {
Some(Indices::U32(indices)) => draw.draw_indexed(0..indices.len() as u32, 0, 0..1),
Some(Indices::U16(indices)) => draw.draw_indexed(0..indices.len() as u32, 0, 0..1),
None => draw.draw(0..mesh.count_vertices() as u32, 0..1),
};
};

if wireframe_config.global {
query.q0_mut().iter_mut().for_each(iterator);
} else {
query.q1_mut().iter_mut().for_each(iterator);
}
}
30 changes: 30 additions & 0 deletions crates/bevy_render/src/wireframe/pipeline.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use crate::{
pipeline::{
CullMode, FrontFace, PipelineDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology,
},
shader::{Shader, ShaderStage, ShaderStages},
};
use bevy_asset::Assets;

pub(crate) fn build_wireframe_pipeline(shaders: &mut Assets<Shader>) -> PipelineDescriptor {
PipelineDescriptor {
name: Some("wireframe".into()),
primitive: PrimitiveState {
topology: PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: FrontFace::Ccw,
cull_mode: CullMode::None,
polygon_mode: PolygonMode::Line,
},
..PipelineDescriptor::default_config(ShaderStages {
vertex: shaders.add(Shader::from_glsl(
ShaderStage::Vertex,
include_str!("wireframe.vert"),
)),
fragment: Some(shaders.add(Shader::from_glsl(
ShaderStage::Fragment,
include_str!("wireframe.frag"),
))),
})
}
}
8 changes: 8 additions & 0 deletions crates/bevy_render/src/wireframe/wireframe.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#version 450

layout(location = 0) out vec4 o_Target;


void main() {
o_Target = vec4(1.0, 1.0, 1.0, 1.0);
}
16 changes: 16 additions & 0 deletions crates/bevy_render/src/wireframe/wireframe.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#version 450

layout(location = 0) in vec3 Vertex_Position;

layout(set = 0, binding = 0) uniform Camera {
mat4 ViewProj;
};

layout(set = 1, binding = 0) uniform Transform {
mat4 Model;
};

void main() {
vec3 v_Position = (Model * vec4(Vertex_Position, 1.0)).xyz;
gl_Position = ViewProj * vec4(v_Position, 1.0);
}
59 changes: 59 additions & 0 deletions examples/3d/wireframe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use bevy::prelude::*;
use bevy_internal::{
render::wireframe::{Wireframe, WireframeConfig, WireframePlugin},
wgpu::{WgpuFeature, WgpuFeatures, WgpuOptions},
};

fn main() {
App::build()
.insert_resource(Msaa { samples: 4 })
.insert_resource(WgpuOptions {
features: WgpuFeatures {
// The Wireframe requires NonFillPolygonMode feature
features: vec![WgpuFeature::NonFillPolygonMode],
},
..Default::default()
})
.add_plugins(DefaultPlugins)
.add_plugin(WireframePlugin)
.add_startup_system(setup.system())
.run();
}

/// set up a simple 3D scene
fn setup(
commands: &mut Commands,
mut wireframe_config: ResMut<WireframeConfig>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// To draw the wireframe on all entities, set this to 'true'
wireframe_config.global = false;
// add entities to the world
commands
// plane
.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })),
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
..Default::default()
})
// cube
.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
transform: Transform::from_xyz(0.0, 0.5, 0.0),
..Default::default()
})
.with(Wireframe) // This enables wireframe drawing on this entity
// light
.spawn(LightBundle {
transform: Transform::from_xyz(4.0, 8.0, 4.0),
..Default::default()
})
// camera
.spawn(PerspectiveCameraBundle {
transform: Transform::from_xyz(-2.0, 2.5, 5.0)
.looking_at(Vec3::default(), Vec3::unit_y()),
..Default::default()
});
}

0 comments on commit d9fb61d

Please sign in to comment.