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:: { Assets , Handle , HandleUntyped } ;
5
6
use bevy_core_pipeline:: Opaque3d ;
6
- use bevy_ecs:: { prelude:: * , reflect:: ReflectComponent } ;
7
+ use bevy_ecs:: {
8
+ prelude:: * ,
9
+ reflect:: ReflectComponent ,
10
+ system:: { lifetimeless:: * , SystemParamItem } ,
11
+ } ;
12
+ use bevy_math:: Vec4 ;
7
13
use bevy_reflect:: { Reflect , TypeUuid } ;
8
- use bevy_render:: render_resource:: PolygonMode ;
9
14
use bevy_render:: {
15
+ color:: Color ,
10
16
mesh:: Mesh ,
11
17
render_asset:: RenderAssets ,
12
- render_phase:: { AddRenderCommand , DrawFunctions , RenderPhase , SetItemPipeline } ,
13
- render_resource:: { RenderPipelineCache , Shader , SpecializedPipeline , SpecializedPipelines } ,
18
+ render_phase:: {
19
+ AddRenderCommand , DrawFunctions , EntityRenderCommand , RenderCommandResult , RenderPhase ,
20
+ SetItemPipeline , TrackedRenderPass ,
21
+ } ,
22
+ render_resource:: {
23
+ std140:: AsStd140 , BindGroup , BindGroupDescriptor , BindGroupEntry , BindGroupLayout ,
24
+ BindGroupLayoutDescriptor , BindGroupLayoutEntry , BindingType , BufferBindingType ,
25
+ BufferSize , DynamicUniformVec , PolygonMode , RenderPipelineCache , Shader , ShaderStages ,
26
+ SpecializedPipeline , SpecializedPipelines ,
27
+ } ,
28
+ renderer:: { RenderDevice , RenderQueue } ,
14
29
view:: { ExtractedView , Msaa } ,
15
30
RenderApp , RenderStage ,
16
31
} ;
@@ -34,10 +49,13 @@ impl Plugin for WireframePlugin {
34
49
if let Ok ( render_app) = app. get_sub_app_mut ( RenderApp ) {
35
50
render_app
36
51
. add_render_command :: < Opaque3d , DrawWireframes > ( )
52
+ . init_resource :: < GlobalWireframeMeta > ( )
37
53
. init_resource :: < WireframePipeline > ( )
38
54
. init_resource :: < SpecializedPipelines < WireframePipeline > > ( )
39
55
. add_system_to_stage ( RenderStage :: Extract , extract_wireframes)
40
56
. add_system_to_stage ( RenderStage :: Extract , extract_wireframe_config)
57
+ . add_system_to_stage ( RenderStage :: Prepare , prepare_wireframes)
58
+ . add_system_to_stage ( RenderStage :: Queue , queue_wireframes_bind_group)
41
59
. add_system_to_stage ( RenderStage :: Queue , queue_wireframes) ;
42
60
}
43
61
}
@@ -49,31 +67,179 @@ fn extract_wireframe_config(mut commands: Commands, wireframe_config: Res<Wirefr
49
67
}
50
68
}
51
69
52
- fn extract_wireframes ( mut commands : Commands , query : Query < Entity , With < Wireframe > > ) {
53
- for entity in query. iter ( ) {
54
- commands. get_or_spawn ( entity) . insert ( Wireframe ) ;
70
+ fn extract_wireframes ( mut commands : Commands , query : Query < ( Entity , & Wireframe ) > ) {
71
+ for ( entity, wireframe ) in query. iter ( ) {
72
+ commands. get_or_spawn ( entity) . insert ( wireframe . clone ( ) ) ;
55
73
}
56
74
}
57
75
58
- /// Controls whether an entity should rendered in wireframe-mode if the [`WireframePlugin`] is enabled
59
- #[ derive( Component , Debug , Clone , Default , Reflect ) ]
76
+ fn prepare_wireframes (
77
+ mut commands : Commands ,
78
+ config : Res < WireframeConfig > ,
79
+ render_device : Res < RenderDevice > ,
80
+ render_queue : Res < RenderQueue > ,
81
+ mut wireframe_meta : ResMut < GlobalWireframeMeta > ,
82
+ global_query : Query < ( Entity , Option < & Wireframe > ) , ( With < Handle < Mesh > > , With < MeshUniform > ) > ,
83
+ wireframe_query : Query <
84
+ ( Entity , Option < & Wireframe > ) ,
85
+ ( With < Handle < Mesh > > , With < MeshUniform > , With < Wireframe > ) ,
86
+ > ,
87
+ ) {
88
+ wireframe_meta. uniforms . clear ( ) ;
89
+ wireframe_meta. uniforms . push ( WireframeUniform {
90
+ color : config. default_color . as_linear_rgba_f32 ( ) . into ( ) ,
91
+ } ) ;
92
+
93
+ let add_wireframe_uniform = |( entity, wireframe) : ( Entity , Option < & Wireframe > ) | {
94
+ let custom_color = wireframe. map ( |wireframe| wireframe. color ) ;
95
+ let uniform_offset = WireframeUniformOffset ( if let Some ( custom_color) = custom_color {
96
+ wireframe_meta. uniforms . push ( WireframeUniform {
97
+ color : custom_color. as_linear_rgba_f32 ( ) . into ( ) ,
98
+ } )
99
+ } else {
100
+ 0
101
+ } ) ;
102
+ commands. entity ( entity) . insert ( uniform_offset) ;
103
+ } ;
104
+
105
+ if config. global {
106
+ global_query. for_each ( add_wireframe_uniform) ;
107
+ } else {
108
+ wireframe_query. for_each ( add_wireframe_uniform) ;
109
+ }
110
+
111
+ wireframe_meta
112
+ . uniforms
113
+ . write_buffer ( & render_device, & render_queue) ;
114
+ }
115
+
116
+ /// Internal [`WireframePlugin`] resource.
117
+ /// [`GlobalWireframeBindGroup`] stores the [`BindGroup`] of wireframe data that is used on the GPU side.
118
+ struct GlobalWireframeBindGroup {
119
+ bind_group : BindGroup ,
120
+ }
121
+
122
+ fn queue_wireframes_bind_group (
123
+ mut commands : Commands ,
124
+ render_device : Res < RenderDevice > ,
125
+ meta : Res < GlobalWireframeMeta > ,
126
+ bind_group : Option < Res < GlobalWireframeBindGroup > > ,
127
+ ) {
128
+ if bind_group. is_none ( ) {
129
+ commands. insert_resource ( GlobalWireframeBindGroup {
130
+ bind_group : render_device. create_bind_group ( & BindGroupDescriptor {
131
+ entries : & [ BindGroupEntry {
132
+ binding : 0 ,
133
+ resource : meta. uniforms . binding ( ) . unwrap ( ) ,
134
+ } ] ,
135
+ label : Some ( "wireframe_bind_group" ) ,
136
+ layout : & meta. bind_group_layout ,
137
+ } ) ,
138
+ } ) ;
139
+ }
140
+ }
141
+
142
+ /// Controls per-entity wireframe rendering settings (if the [`WireframePlugin`] is enabled)
143
+ #[ derive( Component , Debug , Clone , Reflect ) ]
60
144
#[ reflect( Component ) ]
61
- pub struct Wireframe ;
145
+ pub struct Wireframe {
146
+ /// The color of this wireframe.
147
+ /// For the [`Entity`] that has this [`Wireframe`] component, this color overrides any that was set in [`WireframeConfig::default_color`].
148
+ pub color : Color ,
149
+ }
150
+
151
+ impl Default for Wireframe {
152
+ fn default ( ) -> Self {
153
+ Self {
154
+ color : Color :: WHITE ,
155
+ }
156
+ }
157
+ }
62
158
63
- #[ derive( Debug , Clone , Default ) ]
159
+ /// Configuration for [`WireframePlugin`].
160
+ #[ derive( Debug , Clone ) ]
64
161
pub struct WireframeConfig {
65
- /// Whether to show wireframes for all meshes. If `false`, only meshes with a [Wireframe] component will be rendered.
162
+ /// Whether to show wireframes for all meshes. If `false`, only meshes with a [` Wireframe` ] component will be rendered.
66
163
pub global : bool ,
164
+ /// The default color for wireframes. If [`Self::global`] is set, any [`Entity`] that does not have a [`Wireframe`]
165
+ /// component attached to it will have wireframes in this color.
166
+ pub default_color : Color ,
67
167
}
68
168
69
- pub struct WireframePipeline {
169
+ impl Default for WireframeConfig {
170
+ fn default ( ) -> Self {
171
+ Self {
172
+ global : false ,
173
+ default_color : Color :: WHITE ,
174
+ }
175
+ }
176
+ }
177
+
178
+ /// Internal [`WireframePlugin`] component.
179
+ /// [`WireframeUniformOffset`] holds the offset of a [`WireframeUniform`] in the [`GlobalWireframeMeta::uniforms`].
180
+ #[ derive( Component , Copy , Clone , Debug , Default ) ]
181
+ #[ repr( transparent) ]
182
+ struct WireframeUniformOffset ( u32 ) ;
183
+
184
+ /// Internal [`WireframePlugin`] state.
185
+ /// [`WireframeUniform`] is the GPU representation of a [`Wireframe`].
186
+ #[ derive( Debug , AsStd140 ) ]
187
+ struct WireframeUniform {
188
+ color : Vec4 ,
189
+ }
190
+
191
+ /// Internal [`WireframePlugin`] resource.
192
+ /// This is the data required for rendering [`Wireframe`]s.
193
+ #[ derive( Component ) ]
194
+ struct GlobalWireframeMeta {
195
+ uniforms : DynamicUniformVec < WireframeUniform > ,
196
+ bind_group_layout : BindGroupLayout ,
197
+ }
198
+
199
+ impl FromWorld for GlobalWireframeMeta {
200
+ fn from_world ( world : & mut World ) -> Self {
201
+ let render_device = world. get_resource :: < RenderDevice > ( ) . unwrap ( ) ;
202
+
203
+ let bind_group_layout =
204
+ render_device. create_bind_group_layout ( & BindGroupLayoutDescriptor {
205
+ entries : & [ BindGroupLayoutEntry {
206
+ binding : 0 ,
207
+ visibility : ShaderStages :: FRAGMENT ,
208
+ ty : BindingType :: Buffer {
209
+ ty : BufferBindingType :: Uniform ,
210
+ has_dynamic_offset : true ,
211
+ min_binding_size : BufferSize :: new (
212
+ WireframeUniform :: std140_size_static ( ) as u64
213
+ ) ,
214
+ } ,
215
+ count : None ,
216
+ } ] ,
217
+ label : Some ( "wireframe_bind_group_layout" ) ,
218
+ } ) ;
219
+
220
+ Self {
221
+ uniforms : Default :: default ( ) ,
222
+ bind_group_layout,
223
+ }
224
+ }
225
+ }
226
+
227
+ /// Internal [`WireframePlugin`] resource.
228
+ /// [`WireframePipeline`] is the specialized rendering pipeline for wireframes.
229
+ struct WireframePipeline {
70
230
mesh_pipeline : MeshPipeline ,
231
+ wireframe_bind_group_layout : BindGroupLayout ,
71
232
shader : Handle < Shader > ,
72
233
}
73
234
impl FromWorld for WireframePipeline {
74
235
fn from_world ( render_world : & mut World ) -> Self {
75
236
WireframePipeline {
76
237
mesh_pipeline : render_world. get_resource :: < MeshPipeline > ( ) . unwrap ( ) . clone ( ) ,
238
+ wireframe_bind_group_layout : render_world
239
+ . get_resource :: < GlobalWireframeMeta > ( )
240
+ . unwrap ( )
241
+ . bind_group_layout
242
+ . clone ( ) ,
77
243
shader : WIREFRAME_SHADER_HANDLE . typed ( ) ,
78
244
}
79
245
}
@@ -86,6 +252,11 @@ impl SpecializedPipeline for WireframePipeline {
86
252
let mut descriptor = self . mesh_pipeline . specialize ( key) ;
87
253
descriptor. vertex . shader = self . shader . clone_weak ( ) ;
88
254
descriptor. fragment . as_mut ( ) . unwrap ( ) . shader = self . shader . clone_weak ( ) ;
255
+ descriptor
256
+ . layout
257
+ . as_mut ( )
258
+ . unwrap ( )
259
+ . push ( self . wireframe_bind_group_layout . clone ( ) ) ;
89
260
descriptor. primitive . polygon_mode = PolygonMode :: Line ;
90
261
descriptor. depth_stencil . as_mut ( ) . unwrap ( ) . bias . slope_scale = 1.0 ;
91
262
descriptor
@@ -142,9 +313,35 @@ fn queue_wireframes(
142
313
}
143
314
}
144
315
316
+ /// Internal [`WireframePlugin`] render command.
317
+ /// [`SetWireframeBindGroup`]`<bindgroup index>` binds the [`GlobalWireframeBindGroup`] there.
318
+ struct SetWireframeBindGroup < const I : usize > ;
319
+ impl < const I : usize > EntityRenderCommand for SetWireframeBindGroup < I > {
320
+ type Param = (
321
+ SRes < GlobalWireframeBindGroup > ,
322
+ SQuery < Read < WireframeUniformOffset > , With < Handle < Mesh > > > ,
323
+ ) ;
324
+ #[ inline]
325
+ fn render < ' w > (
326
+ _view : Entity ,
327
+ item : Entity ,
328
+ ( global_wireframe_bind_group, view_query) : SystemParamItem < ' w , ' _ , Self :: Param > ,
329
+ pass : & mut TrackedRenderPass < ' w > ,
330
+ ) -> RenderCommandResult {
331
+ let wireframe_uniform_offset = view_query. get ( item) . unwrap ( ) ;
332
+ pass. set_bind_group (
333
+ I ,
334
+ & global_wireframe_bind_group. into_inner ( ) . bind_group ,
335
+ & [ wireframe_uniform_offset. 0 ] ,
336
+ ) ;
337
+ RenderCommandResult :: Success
338
+ }
339
+ }
340
+
145
341
type DrawWireframes = (
146
342
SetItemPipeline ,
147
343
SetMeshViewBindGroup < 0 > ,
148
344
SetMeshBindGroup < 1 > ,
345
+ SetWireframeBindGroup < 2 > ,
149
346
DrawMesh ,
150
347
) ;
0 commit comments