Wireframe Rendering Pipeline (#562)

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>
This commit is contained in:
Zhixing Zhang 2021-03-04 01:23:24 +00:00
parent 079b3ade89
commit d9fb61d474
7 changed files with 240 additions and 0 deletions

View file

@ -147,6 +147,10 @@ path = "examples/3d/texture.rs"
name = "update_gltf_scene" name = "update_gltf_scene"
path = "examples/3d/update_gltf_scene.rs" path = "examples/3d/update_gltf_scene.rs"
[[example]]
name = "wireframe"
path = "examples/3d/wireframe.rs"
[[example]] [[example]]
name = "z_sort_debug" name = "z_sort_debug"
path = "examples/3d/z_sort_debug.rs" path = "examples/3d/z_sort_debug.rs"

View file

@ -10,6 +10,7 @@ pub mod render_graph;
pub mod renderer; pub mod renderer;
pub mod shader; pub mod shader;
pub mod texture; pub mod texture;
pub mod wireframe;
use bevy_ecs::{IntoExclusiveSystem, IntoSystem, SystemStage}; use bevy_ecs::{IntoExclusiveSystem, IntoSystem, SystemStage};
use bevy_reflect::RegisterTypeBuilder; use bevy_reflect::RegisterTypeBuilder;

View file

@ -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);
}
}

View file

@ -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"),
))),
})
}
}

View file

@ -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);
}

View file

@ -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
examples/3d/wireframe.rs Normal file
View file

@ -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()
});
}