mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
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:
parent
079b3ade89
commit
d9fb61d474
7 changed files with 240 additions and 0 deletions
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
122
crates/bevy_render/src/wireframe/mod.rs
Normal file
122
crates/bevy_render/src/wireframe/mod.rs
Normal 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);
|
||||
}
|
||||
}
|
30
crates/bevy_render/src/wireframe/pipeline.rs
Normal file
30
crates/bevy_render/src/wireframe/pipeline.rs
Normal 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"),
|
||||
))),
|
||||
})
|
||||
}
|
||||
}
|
8
crates/bevy_render/src/wireframe/wireframe.frag
Normal file
8
crates/bevy_render/src/wireframe/wireframe.frag
Normal 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);
|
||||
}
|
16
crates/bevy_render/src/wireframe/wireframe.vert
Normal file
16
crates/bevy_render/src/wireframe/wireframe.vert
Normal 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
59
examples/3d/wireframe.rs
Normal 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()
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue