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