Skip to content

Commit d9fb61d

Browse files
committed
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 <[email protected]>
1 parent 079b3ad commit d9fb61d

File tree

7 files changed

+240
-0
lines changed

7 files changed

+240
-0
lines changed

Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@ path = "examples/3d/texture.rs"
147147
name = "update_gltf_scene"
148148
path = "examples/3d/update_gltf_scene.rs"
149149

150+
[[example]]
151+
name = "wireframe"
152+
path = "examples/3d/wireframe.rs"
153+
150154
[[example]]
151155
name = "z_sort_debug"
152156
path = "examples/3d/z_sort_debug.rs"

crates/bevy_render/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub mod render_graph;
1010
pub mod renderer;
1111
pub mod shader;
1212
pub mod texture;
13+
pub mod wireframe;
1314

1415
use bevy_ecs::{IntoExclusiveSystem, IntoSystem, SystemStage};
1516
use bevy_reflect::RegisterTypeBuilder;
+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
use crate::{
2+
draw::DrawContext,
3+
mesh::Indices,
4+
pipeline::{PipelineDescriptor, PipelineSpecialization, RenderPipeline},
5+
prelude::*,
6+
shader::Shader,
7+
};
8+
use bevy_app::prelude::*;
9+
use bevy_asset::{Assets, Handle, HandleUntyped};
10+
use bevy_ecs::{IntoSystem, Mut, Query, QuerySet, Res, With};
11+
use bevy_reflect::{Reflect, ReflectComponent, TypeUuid};
12+
use bevy_utils::HashSet;
13+
14+
mod pipeline;
15+
16+
pub const WIREFRAME_PIPELINE_HANDLE: HandleUntyped =
17+
HandleUntyped::weak_from_u64(PipelineDescriptor::TYPE_UUID, 0x137c75ab7e9ad7f5);
18+
19+
#[derive(Debug, Default)]
20+
pub struct WireframePlugin;
21+
22+
impl Plugin for WireframePlugin {
23+
fn build(&self, app: &mut AppBuilder) {
24+
app.init_resource::<WireframeConfig>()
25+
.add_system_to_stage(crate::RenderStage::Draw, draw_wireframes_system.system());
26+
let resources = app.resources();
27+
let mut shaders = resources.get_mut::<Assets<Shader>>().unwrap();
28+
let mut pipelines = resources.get_mut::<Assets<PipelineDescriptor>>().unwrap();
29+
pipelines.set(
30+
WIREFRAME_PIPELINE_HANDLE,
31+
pipeline::build_wireframe_pipeline(&mut shaders),
32+
);
33+
}
34+
}
35+
36+
#[derive(Debug, Clone, Reflect, Default)]
37+
#[reflect(Component)]
38+
pub struct Wireframe;
39+
40+
#[derive(Debug, Clone)]
41+
pub struct WireframeConfig {
42+
pub global: bool,
43+
}
44+
45+
impl Default for WireframeConfig {
46+
fn default() -> Self {
47+
WireframeConfig { global: false }
48+
}
49+
}
50+
51+
pub fn draw_wireframes_system(
52+
mut draw_context: DrawContext,
53+
msaa: Res<Msaa>,
54+
meshes: Res<Assets<Mesh>>,
55+
wireframe_config: Res<WireframeConfig>,
56+
mut query: QuerySet<(
57+
Query<(&mut Draw, &mut RenderPipelines, &Handle<Mesh>, &Visible)>,
58+
Query<(&mut Draw, &mut RenderPipelines, &Handle<Mesh>, &Visible), With<Wireframe>>,
59+
)>,
60+
) {
61+
let iterator = |(mut draw, mut render_pipelines, mesh_handle, visible): (
62+
Mut<Draw>,
63+
Mut<RenderPipelines>,
64+
&Handle<Mesh>,
65+
&Visible,
66+
)| {
67+
if !visible.is_visible {
68+
return;
69+
}
70+
71+
// don't render if the mesh isn't loaded yet
72+
let mesh = if let Some(mesh) = meshes.get(mesh_handle) {
73+
mesh
74+
} else {
75+
return;
76+
};
77+
78+
let mut render_pipeline = RenderPipeline::specialized(
79+
WIREFRAME_PIPELINE_HANDLE.typed(),
80+
PipelineSpecialization {
81+
sample_count: msaa.samples,
82+
strip_index_format: None,
83+
shader_specialization: Default::default(),
84+
primitive_topology: mesh.primitive_topology(),
85+
dynamic_bindings: render_pipelines
86+
.bindings
87+
.iter_dynamic_bindings()
88+
.map(|name| name.to_string())
89+
.collect::<HashSet<String>>(),
90+
vertex_buffer_layout: mesh.get_vertex_buffer_layout(),
91+
},
92+
);
93+
render_pipeline.dynamic_bindings_generation =
94+
render_pipelines.bindings.dynamic_bindings_generation();
95+
96+
draw_context
97+
.set_pipeline(
98+
&mut draw,
99+
&render_pipeline.pipeline,
100+
&render_pipeline.specialization,
101+
)
102+
.unwrap();
103+
draw_context
104+
.set_bind_groups_from_bindings(&mut draw, &mut [&mut render_pipelines.bindings])
105+
.unwrap();
106+
draw_context
107+
.set_vertex_buffers_from_bindings(&mut draw, &[&render_pipelines.bindings])
108+
.unwrap();
109+
110+
match mesh.indices() {
111+
Some(Indices::U32(indices)) => draw.draw_indexed(0..indices.len() as u32, 0, 0..1),
112+
Some(Indices::U16(indices)) => draw.draw_indexed(0..indices.len() as u32, 0, 0..1),
113+
None => draw.draw(0..mesh.count_vertices() as u32, 0..1),
114+
};
115+
};
116+
117+
if wireframe_config.global {
118+
query.q0_mut().iter_mut().for_each(iterator);
119+
} else {
120+
query.q1_mut().iter_mut().for_each(iterator);
121+
}
122+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use crate::{
2+
pipeline::{
3+
CullMode, FrontFace, PipelineDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology,
4+
},
5+
shader::{Shader, ShaderStage, ShaderStages},
6+
};
7+
use bevy_asset::Assets;
8+
9+
pub(crate) fn build_wireframe_pipeline(shaders: &mut Assets<Shader>) -> PipelineDescriptor {
10+
PipelineDescriptor {
11+
name: Some("wireframe".into()),
12+
primitive: PrimitiveState {
13+
topology: PrimitiveTopology::TriangleList,
14+
strip_index_format: None,
15+
front_face: FrontFace::Ccw,
16+
cull_mode: CullMode::None,
17+
polygon_mode: PolygonMode::Line,
18+
},
19+
..PipelineDescriptor::default_config(ShaderStages {
20+
vertex: shaders.add(Shader::from_glsl(
21+
ShaderStage::Vertex,
22+
include_str!("wireframe.vert"),
23+
)),
24+
fragment: Some(shaders.add(Shader::from_glsl(
25+
ShaderStage::Fragment,
26+
include_str!("wireframe.frag"),
27+
))),
28+
})
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#version 450
2+
3+
layout(location = 0) out vec4 o_Target;
4+
5+
6+
void main() {
7+
o_Target = vec4(1.0, 1.0, 1.0, 1.0);
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#version 450
2+
3+
layout(location = 0) in vec3 Vertex_Position;
4+
5+
layout(set = 0, binding = 0) uniform Camera {
6+
mat4 ViewProj;
7+
};
8+
9+
layout(set = 1, binding = 0) uniform Transform {
10+
mat4 Model;
11+
};
12+
13+
void main() {
14+
vec3 v_Position = (Model * vec4(Vertex_Position, 1.0)).xyz;
15+
gl_Position = ViewProj * vec4(v_Position, 1.0);
16+
}

examples/3d/wireframe.rs

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
use bevy::prelude::*;
2+
use bevy_internal::{
3+
render::wireframe::{Wireframe, WireframeConfig, WireframePlugin},
4+
wgpu::{WgpuFeature, WgpuFeatures, WgpuOptions},
5+
};
6+
7+
fn main() {
8+
App::build()
9+
.insert_resource(Msaa { samples: 4 })
10+
.insert_resource(WgpuOptions {
11+
features: WgpuFeatures {
12+
// The Wireframe requires NonFillPolygonMode feature
13+
features: vec![WgpuFeature::NonFillPolygonMode],
14+
},
15+
..Default::default()
16+
})
17+
.add_plugins(DefaultPlugins)
18+
.add_plugin(WireframePlugin)
19+
.add_startup_system(setup.system())
20+
.run();
21+
}
22+
23+
/// set up a simple 3D scene
24+
fn setup(
25+
commands: &mut Commands,
26+
mut wireframe_config: ResMut<WireframeConfig>,
27+
mut meshes: ResMut<Assets<Mesh>>,
28+
mut materials: ResMut<Assets<StandardMaterial>>,
29+
) {
30+
// To draw the wireframe on all entities, set this to 'true'
31+
wireframe_config.global = false;
32+
// add entities to the world
33+
commands
34+
// plane
35+
.spawn(PbrBundle {
36+
mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })),
37+
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
38+
..Default::default()
39+
})
40+
// cube
41+
.spawn(PbrBundle {
42+
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
43+
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
44+
transform: Transform::from_xyz(0.0, 0.5, 0.0),
45+
..Default::default()
46+
})
47+
.with(Wireframe) // This enables wireframe drawing on this entity
48+
// light
49+
.spawn(LightBundle {
50+
transform: Transform::from_xyz(4.0, 8.0, 4.0),
51+
..Default::default()
52+
})
53+
// camera
54+
.spawn(PerspectiveCameraBundle {
55+
transform: Transform::from_xyz(-2.0, 2.5, 5.0)
56+
.looking_at(Vec3::default(), Vec3::unit_y()),
57+
..Default::default()
58+
});
59+
}

0 commit comments

Comments
 (0)