1
- use crate :: MeshPipeline ;
2
- use crate :: { DrawMesh , MeshPipelineKey , MeshUniform , SetMeshBindGroup , SetMeshViewBindGroup } ;
1
+ use crate :: {
2
+ DrawMesh , MeshPipeline , MeshPipelineKey , MeshUniform , SetMeshBindGroup , SetMeshViewBindGroup ,
3
+ } ;
3
4
use bevy_app:: Plugin ;
4
5
use bevy_asset:: { load_internal_asset, Handle , HandleUntyped } ;
5
6
use bevy_core_pipeline:: Opaque3d ;
6
- use bevy_ecs:: { prelude:: * , reflect:: ReflectComponent } ;
7
- use bevy_reflect:: std_traits:: ReflectDefault ;
7
+ use bevy_ecs:: {
8
+ prelude:: * ,
9
+ reflect:: ReflectComponent ,
10
+ system:: { lifetimeless:: * , SystemParamItem } ,
11
+ } ;
12
+ use bevy_math:: Vec4 ;
8
13
use bevy_reflect:: { Reflect , TypeUuid } ;
9
14
use bevy_render:: {
15
+ color:: Color ,
10
16
mesh:: { Mesh , MeshVertexBufferLayout } ,
11
17
render_asset:: RenderAssets ,
12
- render_phase:: { AddRenderCommand , DrawFunctions , RenderPhase , SetItemPipeline } ,
18
+ render_component:: { ExtractComponent , ExtractComponentPlugin } ,
19
+ render_phase:: {
20
+ AddRenderCommand , DrawFunctions , EntityRenderCommand , RenderCommandResult , RenderPhase ,
21
+ SetItemPipeline , TrackedRenderPass ,
22
+ } ,
13
23
render_resource:: {
14
- PipelineCache , PolygonMode , RenderPipelineDescriptor , Shader , SpecializedMeshPipeline ,
15
- SpecializedMeshPipelineError , SpecializedMeshPipelines ,
24
+ std140:: AsStd140 , BindGroup , BindGroupDescriptor , BindGroupEntry , BindGroupLayout ,
25
+ BindGroupLayoutDescriptor , BindGroupLayoutEntry , BindingType , BufferBindingType ,
26
+ BufferSize , DynamicUniformVec ,
16
27
} ,
28
+ render_resource:: {
29
+ PipelineCache , PolygonMode , RenderPipelineDescriptor , Shader , ShaderStages ,
30
+ SpecializedMeshPipeline , SpecializedMeshPipelineError , SpecializedMeshPipelines ,
31
+ } ,
32
+ renderer:: { RenderDevice , RenderQueue } ,
17
33
view:: { ExtractedView , Msaa , VisibleEntities } ,
18
34
RenderApp , RenderStage ,
19
35
} ;
@@ -22,6 +38,7 @@ use bevy_utils::tracing::error;
22
38
pub const WIREFRAME_SHADER_HANDLE : HandleUntyped =
23
39
HandleUntyped :: weak_from_u64 ( Shader :: TYPE_UUID , 192598014480025766 ) ;
24
40
41
+ /// A [`Plugin`] that draws wireframes.
25
42
#[ derive( Debug , Default ) ]
26
43
pub struct WireframePlugin ;
27
44
@@ -36,13 +53,16 @@ impl Plugin for WireframePlugin {
36
53
37
54
app. init_resource :: < WireframeConfig > ( ) ;
38
55
56
+ app. add_plugin ( ExtractComponentPlugin :: < Wireframe > :: extract_visible ( ) ) ;
39
57
if let Ok ( render_app) = app. get_sub_app_mut ( RenderApp ) {
40
58
render_app
41
59
. add_render_command :: < Opaque3d , DrawWireframes > ( )
60
+ . init_resource :: < GlobalWireframeMeta > ( )
42
61
. init_resource :: < WireframePipeline > ( )
43
62
. init_resource :: < SpecializedMeshPipelines < WireframePipeline > > ( )
44
- . add_system_to_stage ( RenderStage :: Extract , extract_wireframes)
45
63
. add_system_to_stage ( RenderStage :: Extract , extract_wireframe_config)
64
+ . add_system_to_stage ( RenderStage :: Prepare , prepare_wireframes)
65
+ . add_system_to_stage ( RenderStage :: Queue , queue_wireframes_bind_group)
46
66
. add_system_to_stage ( RenderStage :: Queue , queue_wireframes) ;
47
67
}
48
68
}
@@ -54,31 +74,186 @@ fn extract_wireframe_config(mut commands: Commands, wireframe_config: Res<Wirefr
54
74
}
55
75
}
56
76
57
- fn extract_wireframes ( mut commands : Commands , query : Query < Entity , With < Wireframe > > ) {
58
- for entity in query. iter ( ) {
59
- commands. get_or_spawn ( entity) . insert ( Wireframe ) ;
77
+ #[ allow( clippy:: type_complexity) ]
78
+ fn prepare_wireframes (
79
+ mut commands : Commands ,
80
+ config : Res < WireframeConfig > ,
81
+ render_device : Res < RenderDevice > ,
82
+ render_queue : Res < RenderQueue > ,
83
+ mut wireframe_meta : ResMut < GlobalWireframeMeta > ,
84
+ global_query : Query < ( Entity , Option < & Wireframe > ) , ( With < Handle < Mesh > > , With < MeshUniform > ) > ,
85
+ wireframe_query : Query <
86
+ ( Entity , Option < & Wireframe > ) ,
87
+ ( With < Handle < Mesh > > , With < MeshUniform > , With < Wireframe > ) ,
88
+ > ,
89
+ ) {
90
+ wireframe_meta. uniforms . clear ( ) ;
91
+ wireframe_meta. uniforms . push ( WireframeUniform {
92
+ color : config. default_color . as_linear_rgba_f32 ( ) . into ( ) ,
93
+ } ) ;
94
+
95
+ let add_wireframe_uniform = |( entity, wireframe) : ( Entity , Option < & Wireframe > ) | {
96
+ let custom_color = wireframe. and_then ( |wireframe| wireframe. color ) ;
97
+ let uniform_offset = WireframeUniformOffset ( if let Some ( custom_color) = custom_color {
98
+ wireframe_meta. uniforms . push ( WireframeUniform {
99
+ color : custom_color. as_linear_rgba_f32 ( ) . into ( ) ,
100
+ } )
101
+ } else {
102
+ 0
103
+ } ) ;
104
+ commands. entity ( entity) . insert ( uniform_offset) ;
105
+ } ;
106
+
107
+ if config. on_all_meshes {
108
+ global_query. for_each ( add_wireframe_uniform) ;
109
+ } else {
110
+ wireframe_query. for_each ( add_wireframe_uniform) ;
111
+ }
112
+
113
+ wireframe_meta
114
+ . uniforms
115
+ . write_buffer ( & render_device, & render_queue) ;
116
+ }
117
+
118
+ /// Stores the [`BindGroup`] of wireframe data that is used on the GPU side.
119
+ ///
120
+ /// Internal [`WireframePlugin`] resource.
121
+ struct GlobalWireframeBindGroup {
122
+ bind_group : BindGroup ,
123
+ }
124
+
125
+ fn queue_wireframes_bind_group (
126
+ mut commands : Commands ,
127
+ render_device : Res < RenderDevice > ,
128
+ meta : Res < GlobalWireframeMeta > ,
129
+ bind_group : Option < Res < GlobalWireframeBindGroup > > ,
130
+ ) {
131
+ if bind_group. is_none ( ) {
132
+ commands. insert_resource ( GlobalWireframeBindGroup {
133
+ bind_group : render_device. create_bind_group ( & BindGroupDescriptor {
134
+ entries : & [ BindGroupEntry {
135
+ binding : 0 ,
136
+ resource : meta. uniforms . binding ( ) . unwrap ( ) ,
137
+ } ] ,
138
+ label : Some ( "wireframe_bind_group" ) ,
139
+ layout : & meta. bind_group_layout ,
140
+ } ) ,
141
+ } ) ;
60
142
}
61
143
}
62
144
63
- /// Controls whether an entity should rendered in wireframe-mode if the [`WireframePlugin`] is enabled
64
- #[ derive( Component , Debug , Clone , Default , Reflect ) ]
65
- #[ reflect( Component , Default ) ]
66
- pub struct Wireframe ;
145
+ /// Controls per-entity wireframe rendering settings (if the [`WireframePlugin`] is enabled)
146
+ #[ derive( Component , Debug , Default , Copy , Clone , Reflect ) ]
147
+ #[ reflect( Component ) ]
148
+ pub struct Wireframe {
149
+ /// The color of this wireframe.
150
+ ///
151
+ /// If `None`, the [`Entity`] wireframe's color will be [`WireframeConfig::default_color`].
152
+ pub color : Option < Color > ,
153
+ }
154
+
155
+ impl ExtractComponent for Wireframe {
156
+ type Query = & ' static Wireframe ;
157
+
158
+ type Filter = ( ) ;
159
+
160
+ #[ inline]
161
+ fn extract_component ( item : bevy_ecs:: query:: QueryItem < Self :: Query > ) -> Self {
162
+ * item
163
+ }
164
+ }
67
165
68
- #[ derive( Debug , Clone , Default ) ]
166
+ /// Configuration resource for [`WireframePlugin`].
167
+ #[ derive( Debug , Clone ) ]
69
168
pub struct WireframeConfig {
70
- /// Whether to show wireframes for all meshes. If `false`, only meshes with a [Wireframe] component will be rendered.
71
- pub global : bool ,
169
+ /// Whether to show wireframes for all meshes. If `false`, only meshes with a [`Wireframe`] component will be rendered.
170
+ pub on_all_meshes : bool ,
171
+ /// The default color for wireframes.
172
+ ///
173
+ /// If [`Self::on_all_meshes`] is set, any [`Entity`] that does not have a [`Wireframe`] component attached to it will have
174
+ /// wireframes in this color. Otherwise, this will be the fallback color for any [`Wireframe`]s that does have a `None`
175
+ /// [`Wireframe::color`].
176
+ pub default_color : Color ,
72
177
}
73
178
74
- pub struct WireframePipeline {
179
+ impl Default for WireframeConfig {
180
+ fn default ( ) -> Self {
181
+ Self {
182
+ on_all_meshes : false ,
183
+ default_color : Color :: WHITE ,
184
+ }
185
+ }
186
+ }
187
+
188
+ /// Holds the offset of a [`WireframeUniform`] in the [`GlobalWireframeMeta::uniforms`].
189
+ ///
190
+ /// Internal [`WireframePlugin`] component.
191
+ #[ derive( Component , Copy , Clone , Debug , Default ) ]
192
+ #[ repr( transparent) ]
193
+ struct WireframeUniformOffset ( u32 ) ;
194
+
195
+ /// [`WireframeUniform`] is the GPU representation of a [`Wireframe`].
196
+ ///
197
+ /// Internal [`WireframePlugin`] state.
198
+ #[ derive( Debug , AsStd140 ) ]
199
+ struct WireframeUniform {
200
+ color : Vec4 ,
201
+ }
202
+
203
+ /// The data required for rendering [`Wireframe`]s.
204
+ ///
205
+ /// Internal [`WireframePlugin`] resource.
206
+ #[ derive( Component ) ]
207
+ struct GlobalWireframeMeta {
208
+ uniforms : DynamicUniformVec < WireframeUniform > ,
209
+ bind_group_layout : BindGroupLayout ,
210
+ }
211
+
212
+ impl FromWorld for GlobalWireframeMeta {
213
+ fn from_world ( world : & mut World ) -> Self {
214
+ let render_device = world. get_resource :: < RenderDevice > ( ) . unwrap ( ) ;
215
+
216
+ let bind_group_layout =
217
+ render_device. create_bind_group_layout ( & BindGroupLayoutDescriptor {
218
+ entries : & [ BindGroupLayoutEntry {
219
+ binding : 0 ,
220
+ visibility : ShaderStages :: FRAGMENT ,
221
+ ty : BindingType :: Buffer {
222
+ ty : BufferBindingType :: Uniform ,
223
+ has_dynamic_offset : true ,
224
+ min_binding_size : BufferSize :: new (
225
+ WireframeUniform :: std140_size_static ( ) as u64
226
+ ) ,
227
+ } ,
228
+ count : None ,
229
+ } ] ,
230
+ label : Some ( "wireframe_bind_group_layout" ) ,
231
+ } ) ;
232
+
233
+ Self {
234
+ uniforms : Default :: default ( ) ,
235
+ bind_group_layout,
236
+ }
237
+ }
238
+ }
239
+
240
+ /// [`WireframePipeline`] is the specialized rendering pipeline for wireframes.
241
+ ///
242
+ /// Internal [`WireframePlugin`] resource.
243
+ struct WireframePipeline {
75
244
mesh_pipeline : MeshPipeline ,
245
+ wireframe_bind_group_layout : BindGroupLayout ,
76
246
shader : Handle < Shader > ,
77
247
}
78
248
impl FromWorld for WireframePipeline {
79
249
fn from_world ( render_world : & mut World ) -> Self {
80
250
WireframePipeline {
81
251
mesh_pipeline : render_world. resource :: < MeshPipeline > ( ) . clone ( ) ,
252
+ wireframe_bind_group_layout : render_world
253
+ . get_resource :: < GlobalWireframeMeta > ( )
254
+ . unwrap ( )
255
+ . bind_group_layout
256
+ . clone ( ) ,
82
257
shader : WIREFRAME_SHADER_HANDLE . typed ( ) ,
83
258
}
84
259
}
@@ -95,6 +270,11 @@ impl SpecializedMeshPipeline for WireframePipeline {
95
270
let mut descriptor = self . mesh_pipeline . specialize ( key, layout) ?;
96
271
descriptor. vertex . shader = self . shader . clone_weak ( ) ;
97
272
descriptor. fragment . as_mut ( ) . unwrap ( ) . shader = self . shader . clone_weak ( ) ;
273
+ descriptor
274
+ . layout
275
+ . as_mut ( )
276
+ . unwrap ( )
277
+ . push ( self . wireframe_bind_group_layout . clone ( ) ) ;
98
278
descriptor. primitive . polygon_mode = PolygonMode :: Line ;
99
279
descriptor. depth_stencil . as_mut ( ) . unwrap ( ) . bias . slope_scale = 1.0 ;
100
280
Ok ( descriptor)
@@ -153,13 +333,8 @@ fn queue_wireframes(
153
333
}
154
334
} ;
155
335
156
- if wireframe_config. global {
157
- let query = material_meshes. p0 ( ) ;
158
- visible_entities
159
- . entities
160
- . iter ( )
161
- . filter_map ( |visible_entity| query. get ( * visible_entity) . ok ( ) )
162
- . for_each ( add_render_phase) ;
336
+ if wireframe_config. on_all_meshes {
337
+ material_meshes. p0 ( ) . iter ( ) . for_each ( add_render_phase) ;
163
338
} else {
164
339
let query = material_meshes. p1 ( ) ;
165
340
visible_entities
@@ -171,9 +346,36 @@ fn queue_wireframes(
171
346
}
172
347
}
173
348
349
+ /// [`SetWireframeBindGroup`]`<bindgroup index>` binds the [`GlobalWireframeBindGroup`] there.
350
+ ///
351
+ /// Internal [`WireframePlugin`] render command.
352
+ struct SetWireframeBindGroup < const I : usize > ;
353
+ impl < const I : usize > EntityRenderCommand for SetWireframeBindGroup < I > {
354
+ type Param = (
355
+ SRes < GlobalWireframeBindGroup > ,
356
+ SQuery < Read < WireframeUniformOffset > , With < Handle < Mesh > > > ,
357
+ ) ;
358
+ #[ inline]
359
+ fn render < ' w > (
360
+ _view : Entity ,
361
+ item : Entity ,
362
+ ( global_wireframe_bind_group, view_query) : SystemParamItem < ' w , ' _ , Self :: Param > ,
363
+ pass : & mut TrackedRenderPass < ' w > ,
364
+ ) -> RenderCommandResult {
365
+ let wireframe_uniform_offset = view_query. get ( item) . unwrap ( ) ;
366
+ pass. set_bind_group (
367
+ I ,
368
+ & global_wireframe_bind_group. into_inner ( ) . bind_group ,
369
+ & [ wireframe_uniform_offset. 0 ] ,
370
+ ) ;
371
+ RenderCommandResult :: Success
372
+ }
373
+ }
374
+
174
375
type DrawWireframes = (
175
376
SetItemPipeline ,
176
377
SetMeshViewBindGroup < 0 > ,
177
378
SetMeshBindGroup < 1 > ,
379
+ SetWireframeBindGroup < 2 > ,
178
380
DrawMesh ,
179
381
) ;
0 commit comments