1
- use crate :: {
2
- DrawMesh , MeshPipeline , MeshPipelineKey , RenderMeshInstance , RenderMeshInstances ,
3
- SetMeshBindGroup , SetMeshViewBindGroup ,
4
- } ;
5
- use bevy_app:: Plugin ;
6
- use bevy_asset:: { load_internal_asset, Handle } ;
7
- use bevy_core_pipeline:: core_3d:: Opaque3d ;
8
- use bevy_derive:: { Deref , DerefMut } ;
9
- use bevy_ecs:: { prelude:: * , reflect:: ReflectComponent } ;
10
- use bevy_reflect:: { std_traits:: ReflectDefault , Reflect } ;
1
+ use crate :: { Material , MaterialPipeline , MaterialPipelineKey , MaterialPlugin } ;
2
+ use bevy_app:: { Plugin , Startup , Update } ;
3
+ use bevy_asset:: { load_internal_asset, Asset , Assets , Handle } ;
4
+ use bevy_ecs:: prelude:: * ;
5
+ use bevy_reflect:: { std_traits:: ReflectDefault , Reflect , TypePath , TypeUuid } ;
11
6
use bevy_render:: {
12
- extract_resource:: { ExtractResource , ExtractResourcePlugin } ,
7
+ extract_resource:: ExtractResource ,
13
8
mesh:: { Mesh , MeshVertexBufferLayout } ,
14
- render_asset:: RenderAssets ,
15
- render_phase:: { AddRenderCommand , DrawFunctions , RenderPhase , SetItemPipeline } ,
9
+ prelude:: Shader ,
16
10
render_resource:: {
17
- PipelineCache , PolygonMode , RenderPipelineDescriptor , Shader , SpecializedMeshPipeline ,
18
- SpecializedMeshPipelineError , SpecializedMeshPipelines ,
11
+ AsBindGroup , PolygonMode , RenderPipelineDescriptor , ShaderRef , SpecializedMeshPipelineError ,
19
12
} ,
20
- view:: { ExtractedView , Msaa , VisibleEntities } ,
21
- RenderApp , RenderSet ,
22
13
} ;
23
- use bevy_render:: { Extract , ExtractSchedule , Render } ;
24
- use bevy_utils:: { tracing:: error, EntityHashSet } ;
25
14
26
15
pub const WIREFRAME_SHADER_HANDLE : Handle < Shader > = Handle :: weak_from_u128 ( 192598014480025766 ) ;
27
16
17
+ /// A [`Plugin`] that draws wireframes.
18
+ ///
19
+ /// Wireframes currently do not work when using webgl or webgpu.
20
+ /// Supported rendering backends:
21
+ /// - DX12
22
+ /// - Vulkan
23
+ /// - Metal
24
+ ///
25
+ /// This is a native only feature.
28
26
#[ derive( Debug , Default ) ]
29
27
pub struct WireframePlugin ;
30
28
@@ -41,23 +39,12 @@ impl Plugin for WireframePlugin {
41
39
. register_type :: < NoWireframe > ( )
42
40
. register_type :: < WireframeConfig > ( )
43
41
. init_resource :: < WireframeConfig > ( )
44
- . add_plugins ( ( ExtractResourcePlugin :: < WireframeConfig > :: default ( ) , ) ) ;
45
-
46
- if let Ok ( render_app) = app. get_sub_app_mut ( RenderApp ) {
47
- render_app
48
- . add_render_command :: < Opaque3d , DrawWireframes > ( )
49
- . init_resource :: < SpecializedMeshPipelines < WireframePipeline > > ( )
50
- . init_resource :: < Wireframes > ( )
51
- . init_resource :: < NoWireframes > ( )
52
- . add_systems ( ExtractSchedule , extract_wireframes)
53
- . add_systems ( Render , queue_wireframes. in_set ( RenderSet :: QueueMeshes ) ) ;
54
- }
55
- }
56
-
57
- fn finish ( & self , app : & mut bevy_app:: App ) {
58
- if let Ok ( render_app) = app. get_sub_app_mut ( RenderApp ) {
59
- render_app. init_resource :: < WireframePipeline > ( ) ;
60
- }
42
+ . add_plugins ( MaterialPlugin :: < WireframeMaterial > :: default ( ) )
43
+ . add_systems ( Startup , setup_global_wireframe_material)
44
+ . add_systems (
45
+ Update ,
46
+ ( apply_global_wireframe_material, apply_wireframe_material) ,
47
+ ) ;
61
48
}
62
49
}
63
50
@@ -85,133 +72,103 @@ pub struct WireframeConfig {
85
72
pub global : bool ,
86
73
}
87
74
88
- #[ derive( Resource , Default , Deref , DerefMut ) ]
89
- pub struct Wireframes ( EntityHashSet < Entity > ) ;
90
-
91
- # [ derive ( Resource , Default , Deref , DerefMut ) ]
92
- pub struct NoWireframes ( EntityHashSet < Entity > ) ;
75
+ #[ derive( Resource ) ]
76
+ struct GlobalWireframeMaterial {
77
+ // This handle will be reused when the global config is enabled
78
+ handle : Handle < WireframeMaterial > ,
79
+ }
93
80
94
- fn extract_wireframes (
95
- mut wireframes : ResMut < Wireframes > ,
96
- mut no_wireframes : ResMut < NoWireframes > ,
97
- wireframe_query : Extract < Query < Entity , With < Wireframe > > > ,
98
- no_wireframe_query : Extract < Query < Entity , With < NoWireframe > > > ,
81
+ fn setup_global_wireframe_material (
82
+ mut commands : Commands ,
83
+ mut materials : ResMut < Assets < WireframeMaterial > > ,
99
84
) {
100
- wireframes . clear ( ) ;
101
- wireframes . extend ( wireframe_query . iter ( ) ) ;
102
- no_wireframes . clear ( ) ;
103
- no_wireframes . extend ( no_wireframe_query . iter ( ) ) ;
85
+ // Create the handle used for the global material
86
+ commands . insert_resource ( GlobalWireframeMaterial {
87
+ handle : materials . add ( WireframeMaterial { } ) ,
88
+ } ) ;
104
89
}
105
90
106
- #[ derive( Resource , Clone ) ]
107
- pub struct WireframePipeline {
108
- mesh_pipeline : MeshPipeline ,
109
- shader : Handle < Shader > ,
91
+ /// Applies or remove the wireframe material to any mesh with a [`Wireframe`] component.
92
+ fn apply_wireframe_material (
93
+ mut commands : Commands ,
94
+ mut materials : ResMut < Assets < WireframeMaterial > > ,
95
+ wireframes : Query < Entity , ( With < Wireframe > , Without < Handle < WireframeMaterial > > ) > ,
96
+ mut removed_wireframes : RemovedComponents < Wireframe > ,
97
+ ) {
98
+ for e in removed_wireframes. read ( ) {
99
+ if let Some ( mut commands) = commands. get_entity ( e) {
100
+ commands. remove :: < Handle < WireframeMaterial > > ( ) ;
101
+ }
102
+ }
103
+
104
+ let mut wireframes_to_spawn = vec ! [ ] ;
105
+ for e in & wireframes {
106
+ wireframes_to_spawn. push ( ( e, materials. add ( WireframeMaterial { } ) ) ) ;
107
+ }
108
+ commands. insert_or_spawn_batch ( wireframes_to_spawn) ;
110
109
}
111
- impl FromWorld for WireframePipeline {
112
- fn from_world ( render_world : & mut World ) -> Self {
113
- WireframePipeline {
114
- mesh_pipeline : render_world. resource :: < MeshPipeline > ( ) . clone ( ) ,
115
- shader : WIREFRAME_SHADER_HANDLE ,
110
+
111
+ /// Applies or removes a wireframe material on any mesh without a [`Wireframe`] component.
112
+ #[ allow( clippy:: type_complexity) ]
113
+ fn apply_global_wireframe_material (
114
+ mut commands : Commands ,
115
+ config : Res < WireframeConfig > ,
116
+ meshes_without_material : Query <
117
+ Entity ,
118
+ (
119
+ With < Handle < Mesh > > ,
120
+ Without < Wireframe > ,
121
+ Without < NoWireframe > ,
122
+ Without < Handle < WireframeMaterial > > ,
123
+ ) ,
124
+ > ,
125
+ meshes_with_global_material : Query <
126
+ Entity ,
127
+ (
128
+ With < Handle < Mesh > > ,
129
+ Without < Wireframe > ,
130
+ Without < NoWireframe > ,
131
+ With < Handle < WireframeMaterial > > ,
132
+ ) ,
133
+ > ,
134
+ global_material : Res < GlobalWireframeMaterial > ,
135
+ ) {
136
+ if !config. is_changed ( ) {
137
+ return ;
138
+ }
139
+
140
+ if config. global {
141
+ let mut material_to_spawn = vec ! [ ] ;
142
+ for e in & meshes_without_material {
143
+ // We only add the material handle but not the Wireframe component
144
+ // This makes it easy to detect which mesh is using the global material and which ones are user specified
145
+ material_to_spawn. push ( ( e, global_material. handle . clone ( ) ) ) ;
146
+ }
147
+ commands. insert_or_spawn_batch ( material_to_spawn) ;
148
+ } else if !config. global {
149
+ for e in & meshes_with_global_material {
150
+ commands. entity ( e) . remove :: < Handle < WireframeMaterial > > ( ) ;
116
151
}
117
152
}
118
153
}
119
154
120
- impl SpecializedMeshPipeline for WireframePipeline {
121
- type Key = MeshPipelineKey ;
155
+ #[ derive( Default , AsBindGroup , TypeUuid , TypePath , Debug , Clone , Asset ) ]
156
+ #[ uuid = "9e694f70-9963-4418-8bc1-3474c66b13b8" ]
157
+ struct WireframeMaterial { }
158
+
159
+ impl Material for WireframeMaterial {
160
+ fn fragment_shader ( ) -> ShaderRef {
161
+ WIREFRAME_SHADER_HANDLE . into ( )
162
+ }
122
163
123
164
fn specialize (
124
- & self ,
125
- key : Self :: Key ,
126
- layout : & MeshVertexBufferLayout ,
127
- ) -> Result < RenderPipelineDescriptor , SpecializedMeshPipelineError > {
128
- let mut descriptor = self . mesh_pipeline . specialize ( key, layout) ?;
129
- descriptor. vertex . shader = self . shader . clone_weak ( ) ;
130
- descriptor
131
- . vertex
132
- . shader_defs
133
- . push ( "MESH_BINDGROUP_1" . into ( ) ) ;
134
- descriptor. fragment . as_mut ( ) . unwrap ( ) . shader = self . shader . clone_weak ( ) ;
165
+ _pipeline : & MaterialPipeline < Self > ,
166
+ descriptor : & mut RenderPipelineDescriptor ,
167
+ _layout : & MeshVertexBufferLayout ,
168
+ _key : MaterialPipelineKey < Self > ,
169
+ ) -> Result < ( ) , SpecializedMeshPipelineError > {
135
170
descriptor. primitive . polygon_mode = PolygonMode :: Line ;
136
171
descriptor. depth_stencil . as_mut ( ) . unwrap ( ) . bias . slope_scale = 1.0 ;
137
- Ok ( descriptor)
138
- }
139
- }
140
-
141
- #[ allow( clippy:: too_many_arguments) ]
142
- fn queue_wireframes (
143
- opaque_3d_draw_functions : Res < DrawFunctions < Opaque3d > > ,
144
- render_meshes : Res < RenderAssets < Mesh > > ,
145
- render_mesh_instances : Res < RenderMeshInstances > ,
146
- wireframes : Res < Wireframes > ,
147
- no_wireframes : Res < NoWireframes > ,
148
- wireframe_config : Res < WireframeConfig > ,
149
- wireframe_pipeline : Res < WireframePipeline > ,
150
- mut pipelines : ResMut < SpecializedMeshPipelines < WireframePipeline > > ,
151
- pipeline_cache : Res < PipelineCache > ,
152
- msaa : Res < Msaa > ,
153
- mut views : Query < ( & ExtractedView , & VisibleEntities , & mut RenderPhase < Opaque3d > ) > ,
154
- ) {
155
- let draw_custom = opaque_3d_draw_functions. read ( ) . id :: < DrawWireframes > ( ) ;
156
- let msaa_key = MeshPipelineKey :: from_msaa_samples ( msaa. samples ( ) ) ;
157
- for ( view, visible_entities, mut opaque_phase) in & mut views {
158
- let rangefinder = view. rangefinder3d ( ) ;
159
-
160
- let view_key = msaa_key | MeshPipelineKey :: from_hdr ( view. hdr ) ;
161
- let add_render_phase = |phase_item : ( Entity , & RenderMeshInstance ) | {
162
- let ( entity, mesh_instance) = phase_item;
163
-
164
- let Some ( mesh) = render_meshes. get ( mesh_instance. mesh_asset_id ) else {
165
- return ;
166
- } ;
167
- let mut key =
168
- view_key | MeshPipelineKey :: from_primitive_topology ( mesh. primitive_topology ) ;
169
- if mesh. morph_targets . is_some ( ) {
170
- key |= MeshPipelineKey :: MORPH_TARGETS ;
171
- }
172
- let pipeline_id =
173
- pipelines. specialize ( & pipeline_cache, & wireframe_pipeline, key, & mesh. layout ) ;
174
- let pipeline_id = match pipeline_id {
175
- Ok ( id) => id,
176
- Err ( err) => {
177
- error ! ( "{}" , err) ;
178
- return ;
179
- }
180
- } ;
181
- opaque_phase. add ( Opaque3d {
182
- entity,
183
- pipeline : pipeline_id,
184
- draw_function : draw_custom,
185
- distance : rangefinder
186
- . distance_translation ( & mesh_instance. transforms . transform . translation ) ,
187
- batch_range : 0 ..1 ,
188
- dynamic_offset : None ,
189
- } ) ;
190
- } ;
191
-
192
- visible_entities
193
- . entities
194
- . iter ( )
195
- . filter_map ( |visible_entity| {
196
- if no_wireframes. get ( visible_entity) . is_some ( ) {
197
- return None ;
198
- }
199
-
200
- if wireframe_config. global || wireframes. get ( visible_entity) . is_some ( ) {
201
- render_mesh_instances
202
- . get ( visible_entity)
203
- . map ( |mesh_instance| ( * visible_entity, mesh_instance) )
204
- } else {
205
- None
206
- }
207
- } )
208
- . for_each ( add_render_phase) ;
172
+ Ok ( ( ) )
209
173
}
210
174
}
211
-
212
- type DrawWireframes = (
213
- SetItemPipeline ,
214
- SetMeshViewBindGroup < 0 > ,
215
- SetMeshBindGroup < 1 > ,
216
- DrawMesh ,
217
- ) ;
0 commit comments