1
- use crate :: MeshPipeline ;
2
- use crate :: { DrawMesh , MeshPipelineKey , MeshUniform , SetMeshBindGroup , SetMeshViewBindGroup } ;
1
+ use crate :: { Material , MaterialPipeline , MaterialPipelineKey , MaterialPlugin } ;
3
2
use bevy_app:: Plugin ;
4
- use bevy_asset:: { load_internal_asset, Handle , HandleUntyped } ;
5
- use bevy_core_pipeline:: core_3d:: Opaque3d ;
6
- use bevy_ecs:: { prelude:: * , reflect:: ReflectComponent } ;
7
- use bevy_reflect:: std_traits:: ReflectDefault ;
8
- use bevy_reflect:: { Reflect , TypeUuid } ;
9
- use bevy_render:: Extract ;
3
+ use bevy_asset:: { load_internal_asset, Assets , Handle , HandleUntyped } ;
4
+ use bevy_ecs:: prelude:: * ;
5
+ use bevy_reflect:: { std_traits:: ReflectDefault , Reflect , TypeUuid } ;
10
6
use bevy_render:: {
11
7
extract_resource:: { ExtractResource , ExtractResourcePlugin } ,
12
8
mesh:: { Mesh , MeshVertexBufferLayout } ,
13
- render_asset:: RenderAssets ,
14
- render_phase:: { AddRenderCommand , DrawFunctions , RenderPhase , SetItemPipeline } ,
9
+ prelude:: { Color , Shader } ,
15
10
render_resource:: {
16
- PipelineCache , PolygonMode , RenderPipelineDescriptor , Shader , SpecializedMeshPipeline ,
17
- SpecializedMeshPipelineError , SpecializedMeshPipelines ,
11
+ AsBindGroup , PolygonMode , RenderPipelineDescriptor , ShaderRef , SpecializedMeshPipelineError ,
18
12
} ,
19
- view:: { ExtractedView , Msaa , VisibleEntities } ,
20
- RenderApp , RenderStage ,
21
13
} ;
22
- use bevy_utils:: tracing:: error;
23
14
24
15
pub const WIREFRAME_SHADER_HANDLE : HandleUntyped =
25
16
HandleUntyped :: weak_from_u64 ( Shader :: TYPE_UUID , 192598014480025766 ) ;
26
17
18
+ /// A [`Plugin`] that draws wireframes.
27
19
#[ derive( Debug , Default ) ]
28
20
pub struct WireframePlugin ;
29
-
30
21
impl Plugin for WireframePlugin {
31
22
fn build ( & self , app : & mut bevy_app:: App ) {
32
23
load_internal_asset ! (
@@ -37,139 +28,175 @@ impl Plugin for WireframePlugin {
37
28
) ;
38
29
39
30
app. register_type :: < Wireframe > ( )
31
+ . register_type :: < WireframeColor > ( )
40
32
. register_type :: < WireframeConfig > ( )
41
33
. init_resource :: < WireframeConfig > ( )
42
- . add_plugin ( ExtractResourcePlugin :: < WireframeConfig > :: default ( ) ) ;
43
-
44
- if let Ok ( render_app) = app. get_sub_app_mut ( RenderApp ) {
45
- render_app
46
- . add_render_command :: < Opaque3d , DrawWireframes > ( )
47
- . init_resource :: < WireframePipeline > ( )
48
- . init_resource :: < SpecializedMeshPipelines < WireframePipeline > > ( )
49
- . add_system_to_stage ( RenderStage :: Extract , extract_wireframes)
50
- . add_system_to_stage ( RenderStage :: Queue , queue_wireframes) ;
51
- }
52
- }
53
- }
54
-
55
- fn extract_wireframes ( mut commands : Commands , query : Extract < Query < Entity , With < Wireframe > > > ) {
56
- for entity in & query {
57
- commands. get_or_spawn ( entity) . insert ( Wireframe ) ;
34
+ . add_plugin ( ExtractResourcePlugin :: < WireframeConfig > :: default ( ) )
35
+ . add_plugin ( MaterialPlugin :: < WireframeMaterial > :: default ( ) )
36
+ . add_system ( apply_global)
37
+ . add_system ( apply_material)
38
+ . add_system ( wireframe_color_changed)
39
+ . add_system ( global_color_changed) ;
58
40
}
59
41
}
60
42
61
- /// Controls whether an entity should rendered in wireframe-mode if the [`WireframePlugin`] is enabled
43
+ /// Toggles wireframe rendering for any entity it is attached to.
44
+ /// To control the color of the wireframe, you need to use the [`WireframeColor`] component
45
+ ///
46
+ /// This requires the [`WireframePlugin`] to be enabled.
62
47
#[ derive( Component , Debug , Clone , Default , Reflect ) ]
63
48
#[ reflect( Component , Default ) ]
64
49
pub struct Wireframe ;
65
50
51
+ /// Sets the color of the [`Wireframe`] of the entity it is attached to.
52
+ /// If this component is present but there's no [`Wireframe`] component,
53
+ /// it will still affect the color of the wireframe when [`WireframeConfig`]`::global` is set.
54
+ ///
55
+ /// This overrides the [`WireframeConfig::color`].
56
+ #[ derive( Component , Debug , Clone , Default , Reflect ) ]
57
+ #[ reflect( Component , Default ) ]
58
+ pub struct WireframeColor {
59
+ pub color : Color ,
60
+ }
61
+
62
+ /// Configuration resource for [`WireframePlugin`].
66
63
#[ derive( Resource , Debug , Clone , Default , ExtractResource , Reflect ) ]
67
64
#[ reflect( Resource ) ]
68
65
pub struct WireframeConfig {
69
- /// Whether to show wireframes for all meshes. If `false`, only meshes with a [Wireframe] component will be rendered.
66
+ /// Whether to show wireframes for all meshes.
67
+ /// If `false`, only meshes with a [Wireframe] component will be rendered.
70
68
pub global : bool ,
69
+ /// If [`Self::global`] is set, any [`Entity`] that does not have a [`Wireframe`] component attached to it will have
70
+ /// wireframes in this color. Otherwise, this will be the fallback color for any entity that has a [`Wireframe`],
71
+ /// but no [`WireframeColor`].
72
+ pub color : Color ,
71
73
}
72
74
73
- #[ derive( Resource , Clone ) ]
74
- pub struct WireframePipeline {
75
- mesh_pipeline : MeshPipeline ,
76
- shader : Handle < Shader > ,
75
+ /// Apply the wireframe material to any mesh with a [`Wireframe`] component.
76
+ /// Uses [`WireframeConfig::color`] as a fallback if no [`WireframeColor`] component is found
77
+ #[ allow( clippy:: type_complexity) ]
78
+ fn apply_material (
79
+ mut commands : Commands ,
80
+ config : Res < WireframeConfig > ,
81
+ mut materials : ResMut < Assets < WireframeMaterial > > ,
82
+ wireframes : Query <
83
+ ( Entity , Option < & WireframeColor > ) ,
84
+ ( With < Wireframe > , Without < Handle < WireframeMaterial > > ) ,
85
+ > ,
86
+ ) {
87
+ for ( e, color) in & wireframes {
88
+ commands. entity ( e) . insert ( materials. add ( WireframeMaterial {
89
+ color : if let Some ( wireframe_color) = color {
90
+ wireframe_color. color
91
+ } else {
92
+ config. color
93
+ } ,
94
+ } ) ) ;
95
+ }
77
96
}
78
- impl FromWorld for WireframePipeline {
79
- fn from_world ( render_world : & mut World ) -> Self {
80
- WireframePipeline {
81
- mesh_pipeline : render_world. resource :: < MeshPipeline > ( ) . clone ( ) ,
82
- shader : WIREFRAME_SHADER_HANDLE . typed ( ) ,
83
- }
97
+
98
+ /// Updates the wireframe material when the color in [`WireframeColor`] changes
99
+ #[ allow( clippy:: type_complexity) ]
100
+ fn wireframe_color_changed (
101
+ mut materials : ResMut < Assets < WireframeMaterial > > ,
102
+ mut colors_changed : Query <
103
+ ( & mut Handle < WireframeMaterial > , & WireframeColor ) ,
104
+ ( With < Wireframe > , Changed < WireframeColor > ) ,
105
+ > ,
106
+ ) {
107
+ for ( mut handle, wireframe_color) in & mut colors_changed {
108
+ * handle = materials. add ( WireframeMaterial {
109
+ color : wireframe_color. color ,
110
+ } ) ;
84
111
}
85
112
}
86
113
87
- impl SpecializedMeshPipeline for WireframePipeline {
88
- type Key = MeshPipelineKey ;
114
+ /// Updates the wireframe material of all entities without a [`WireframeColor`] or without a [`Wireframe`] component
115
+ fn global_color_changed (
116
+ config : Res < WireframeConfig > ,
117
+ mut materials : ResMut < Assets < WireframeMaterial > > ,
118
+ mut wireframes : Query < & mut Handle < WireframeMaterial > , Without < WireframeColor > > ,
119
+ ) {
120
+ if !config. is_changed ( ) {
121
+ return ;
122
+ }
89
123
90
- fn specialize (
91
- & self ,
92
- key : Self :: Key ,
93
- layout : & MeshVertexBufferLayout ,
94
- ) -> Result < RenderPipelineDescriptor , SpecializedMeshPipelineError > {
95
- let mut descriptor = self . mesh_pipeline . specialize ( key, layout) ?;
96
- descriptor. vertex . shader = self . shader . clone_weak ( ) ;
97
- descriptor. fragment . as_mut ( ) . unwrap ( ) . shader = self . shader . clone_weak ( ) ;
98
- descriptor. primitive . polygon_mode = PolygonMode :: Line ;
99
- descriptor. depth_stencil . as_mut ( ) . unwrap ( ) . bias . slope_scale = 1.0 ;
100
- Ok ( descriptor)
124
+ for mut handle in & mut wireframes {
125
+ * handle = materials. add ( WireframeMaterial {
126
+ color : config. color ,
127
+ } ) ;
101
128
}
102
129
}
103
130
104
- #[ allow( clippy:: too_many_arguments) ]
105
- fn queue_wireframes (
106
- opaque_3d_draw_functions : Res < DrawFunctions < Opaque3d > > ,
107
- render_meshes : Res < RenderAssets < Mesh > > ,
108
- wireframe_config : Res < WireframeConfig > ,
109
- wireframe_pipeline : Res < WireframePipeline > ,
110
- mut pipelines : ResMut < SpecializedMeshPipelines < WireframePipeline > > ,
111
- mut pipeline_cache : ResMut < PipelineCache > ,
112
- msaa : Res < Msaa > ,
113
- mut material_meshes : ParamSet < (
114
- Query < ( Entity , & Handle < Mesh > , & MeshUniform ) > ,
115
- Query < ( Entity , & Handle < Mesh > , & MeshUniform ) , With < Wireframe > > ,
131
+ /// Applies or remove a wireframe material on any mesh without a [`Wireframe`] component.
132
+ #[ allow( clippy:: type_complexity) ]
133
+ fn apply_global (
134
+ mut commands : Commands ,
135
+ config : Res < WireframeConfig > ,
136
+ mut materials : ResMut < Assets < WireframeMaterial > > ,
137
+ mut q1 : ParamSet < (
138
+ Query <
139
+ Entity ,
140
+ (
141
+ With < Handle < Mesh > > ,
142
+ Without < Handle < WireframeMaterial > > ,
143
+ Without < Wireframe > ,
144
+ ) ,
145
+ > ,
146
+ Query <
147
+ Entity ,
148
+ (
149
+ With < Handle < Mesh > > ,
150
+ With < Handle < WireframeMaterial > > ,
151
+ Without < Wireframe > ,
152
+ ) ,
153
+ > ,
116
154
) > ,
117
- mut views : Query < ( & ExtractedView , & VisibleEntities , & mut RenderPhase < Opaque3d > ) > ,
155
+ mut is_global_applied : Local < bool > ,
118
156
) {
119
- let draw_custom = opaque_3d_draw_functions. read ( ) . id :: < DrawWireframes > ( ) ;
120
- let msaa_key = MeshPipelineKey :: from_msaa_samples ( msaa. samples ) ;
121
- for ( view, visible_entities, mut opaque_phase) in & mut views {
122
- let rangefinder = view. rangefinder3d ( ) ;
123
-
124
- let view_key = msaa_key | MeshPipelineKey :: from_hdr ( view. hdr ) ;
125
- let add_render_phase =
126
- |( entity, mesh_handle, mesh_uniform) : ( Entity , & Handle < Mesh > , & MeshUniform ) | {
127
- if let Some ( mesh) = render_meshes. get ( mesh_handle) {
128
- let key = view_key
129
- | MeshPipelineKey :: from_primitive_topology ( mesh. primitive_topology ) ;
130
- let pipeline_id = pipelines. specialize (
131
- & mut pipeline_cache,
132
- & wireframe_pipeline,
133
- key,
134
- & mesh. layout ,
135
- ) ;
136
- let pipeline_id = match pipeline_id {
137
- Ok ( id) => id,
138
- Err ( err) => {
139
- error ! ( "{}" , err) ;
140
- return ;
141
- }
142
- } ;
143
- opaque_phase. add ( Opaque3d {
144
- entity,
145
- pipeline : pipeline_id,
146
- draw_function : draw_custom,
147
- distance : rangefinder. distance ( & mesh_uniform. transform ) ,
148
- } ) ;
149
- }
150
- } ;
151
-
152
- if wireframe_config. global {
153
- let query = material_meshes. p0 ( ) ;
154
- visible_entities
155
- . entities
156
- . iter ( )
157
- . filter_map ( |visible_entity| query. get ( * visible_entity) . ok ( ) )
158
- . for_each ( add_render_phase) ;
159
- } else {
160
- let query = material_meshes. p1 ( ) ;
161
- visible_entities
162
- . entities
163
- . iter ( )
164
- . filter_map ( |visible_entity| query. get ( * visible_entity) . ok ( ) )
165
- . for_each ( add_render_phase) ;
157
+ if !config. is_changed ( ) {
158
+ return ;
159
+ }
160
+
161
+ if !* is_global_applied && config. global {
162
+ let global_material = materials. add ( WireframeMaterial {
163
+ color : config. color ,
164
+ } ) ;
165
+
166
+ for e in & mut q1. p0 ( ) {
167
+ commands. entity ( e) . insert ( global_material. clone ( ) ) ;
168
+ }
169
+
170
+ * is_global_applied = true ;
171
+ } else if * is_global_applied && !config. global {
172
+ for e in & mut q1. p1 ( ) {
173
+ commands. entity ( e) . remove :: < Handle < WireframeMaterial > > ( ) ;
166
174
}
175
+ * is_global_applied = false ;
167
176
}
168
177
}
169
178
170
- type DrawWireframes = (
171
- SetItemPipeline ,
172
- SetMeshViewBindGroup < 0 > ,
173
- SetMeshBindGroup < 1 > ,
174
- DrawMesh ,
175
- ) ;
179
+ /// The material used to render wireframes
180
+ #[ derive( Default , AsBindGroup , TypeUuid , Debug , Clone ) ]
181
+ #[ uuid = "9e694f70-9963-4418-8bc1-3474c66b13b8" ]
182
+ pub struct WireframeMaterial {
183
+ #[ uniform( 0 ) ]
184
+ pub color : Color ,
185
+ }
186
+
187
+ impl Material for WireframeMaterial {
188
+ fn fragment_shader ( ) -> ShaderRef {
189
+ WIREFRAME_SHADER_HANDLE . typed ( ) . into ( )
190
+ }
191
+
192
+ fn specialize (
193
+ _pipeline : & MaterialPipeline < Self > ,
194
+ descriptor : & mut RenderPipelineDescriptor ,
195
+ _layout : & MeshVertexBufferLayout ,
196
+ _key : MaterialPipelineKey < Self > ,
197
+ ) -> Result < ( ) , SpecializedMeshPipelineError > {
198
+ descriptor. primitive . polygon_mode = PolygonMode :: Line ;
199
+ descriptor. depth_stencil . as_mut ( ) . unwrap ( ) . bias . slope_scale = 1.0 ;
200
+ Ok ( ( ) )
201
+ }
202
+ }
0 commit comments