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