Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_gizmos/src/pipeline_2d.rs
6595 views
1
use crate::{
2
config::{GizmoLineJoint, GizmoLineStyle, GizmoMeshConfig},
3
init_line_gizmo_uniform_bind_group_layout, line_gizmo_vertex_buffer_layouts,
4
line_joint_gizmo_vertex_buffer_layouts, DrawLineGizmo, DrawLineJointGizmo, GizmoRenderSystems,
5
GpuLineGizmo, LineGizmoUniformBindgroupLayout, SetLineGizmoBindGroup,
6
};
7
use bevy_app::{App, Plugin};
8
use bevy_asset::{load_embedded_asset, AssetServer, Handle};
9
use bevy_camera::visibility::RenderLayers;
10
use bevy_core_pipeline::core_2d::{Transparent2d, CORE_2D_DEPTH_FORMAT};
11
12
use bevy_ecs::{
13
prelude::Entity,
14
resource::Resource,
15
schedule::IntoScheduleConfigs,
16
system::{Commands, Query, Res, ResMut},
17
};
18
use bevy_image::BevyDefault as _;
19
use bevy_math::FloatOrd;
20
use bevy_render::{
21
render_asset::{prepare_assets, RenderAssets},
22
render_phase::{
23
AddRenderCommand, DrawFunctions, PhaseItemExtraIndex, SetItemPipeline,
24
ViewSortedRenderPhases,
25
},
26
render_resource::*,
27
view::{ExtractedView, Msaa, ViewTarget},
28
Render, RenderApp, RenderSystems,
29
};
30
use bevy_render::{sync_world::MainEntity, RenderStartup};
31
use bevy_shader::Shader;
32
use bevy_sprite_render::{
33
init_mesh_2d_pipeline, Mesh2dPipeline, Mesh2dPipelineKey, SetMesh2dViewBindGroup,
34
};
35
use bevy_utils::default;
36
use tracing::error;
37
38
pub struct LineGizmo2dPlugin;
39
40
impl Plugin for LineGizmo2dPlugin {
41
fn build(&self, app: &mut App) {
42
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
43
return;
44
};
45
46
render_app
47
.add_render_command::<Transparent2d, DrawLineGizmo2d>()
48
.add_render_command::<Transparent2d, DrawLineGizmo2dStrip>()
49
.add_render_command::<Transparent2d, DrawLineJointGizmo2d>()
50
.init_resource::<SpecializedRenderPipelines<LineGizmoPipeline>>()
51
.init_resource::<SpecializedRenderPipelines<LineJointGizmoPipeline>>()
52
.configure_sets(
53
Render,
54
GizmoRenderSystems::QueueLineGizmos2d
55
.in_set(RenderSystems::Queue)
56
.ambiguous_with(bevy_sprite_render::queue_sprites)
57
.ambiguous_with(
58
bevy_sprite_render::queue_material2d_meshes::<
59
bevy_sprite_render::ColorMaterial,
60
>,
61
),
62
)
63
.add_systems(
64
RenderStartup,
65
init_line_gizmo_pipelines
66
.after(init_line_gizmo_uniform_bind_group_layout)
67
.after(init_mesh_2d_pipeline),
68
)
69
.add_systems(
70
Render,
71
(queue_line_gizmos_2d, queue_line_joint_gizmos_2d)
72
.in_set(GizmoRenderSystems::QueueLineGizmos2d)
73
.after(prepare_assets::<GpuLineGizmo>),
74
);
75
}
76
}
77
78
#[derive(Clone, Resource)]
79
struct LineGizmoPipeline {
80
mesh_pipeline: Mesh2dPipeline,
81
uniform_layout: BindGroupLayout,
82
shader: Handle<Shader>,
83
}
84
85
fn init_line_gizmo_pipelines(
86
mut commands: Commands,
87
mesh_2d_pipeline: Res<Mesh2dPipeline>,
88
uniform_bind_group_layout: Res<LineGizmoUniformBindgroupLayout>,
89
asset_server: Res<AssetServer>,
90
) {
91
commands.insert_resource(LineGizmoPipeline {
92
mesh_pipeline: mesh_2d_pipeline.clone(),
93
uniform_layout: uniform_bind_group_layout.layout.clone(),
94
shader: load_embedded_asset!(asset_server.as_ref(), "lines.wgsl"),
95
});
96
commands.insert_resource(LineJointGizmoPipeline {
97
mesh_pipeline: mesh_2d_pipeline.clone(),
98
uniform_layout: uniform_bind_group_layout.layout.clone(),
99
shader: load_embedded_asset!(asset_server.as_ref(), "line_joints.wgsl"),
100
});
101
}
102
103
#[derive(PartialEq, Eq, Hash, Clone)]
104
struct LineGizmoPipelineKey {
105
mesh_key: Mesh2dPipelineKey,
106
strip: bool,
107
line_style: GizmoLineStyle,
108
}
109
110
impl SpecializedRenderPipeline for LineGizmoPipeline {
111
type Key = LineGizmoPipelineKey;
112
113
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
114
let format = if key.mesh_key.contains(Mesh2dPipelineKey::HDR) {
115
ViewTarget::TEXTURE_FORMAT_HDR
116
} else {
117
TextureFormat::bevy_default()
118
};
119
120
let shader_defs = vec![
121
#[cfg(feature = "webgl")]
122
"SIXTEEN_BYTE_ALIGNMENT".into(),
123
];
124
125
let layout = vec![
126
self.mesh_pipeline.view_layout.clone(),
127
self.uniform_layout.clone(),
128
];
129
130
let fragment_entry_point = match key.line_style {
131
GizmoLineStyle::Solid => "fragment_solid",
132
GizmoLineStyle::Dotted => "fragment_dotted",
133
GizmoLineStyle::Dashed { .. } => "fragment_dashed",
134
};
135
136
RenderPipelineDescriptor {
137
vertex: VertexState {
138
shader: self.shader.clone(),
139
shader_defs: shader_defs.clone(),
140
buffers: line_gizmo_vertex_buffer_layouts(key.strip),
141
..default()
142
},
143
fragment: Some(FragmentState {
144
shader: self.shader.clone(),
145
shader_defs,
146
entry_point: Some(fragment_entry_point.into()),
147
targets: vec![Some(ColorTargetState {
148
format,
149
blend: Some(BlendState::ALPHA_BLENDING),
150
write_mask: ColorWrites::ALL,
151
})],
152
}),
153
layout,
154
depth_stencil: Some(DepthStencilState {
155
format: CORE_2D_DEPTH_FORMAT,
156
depth_write_enabled: false,
157
depth_compare: CompareFunction::Always,
158
stencil: StencilState {
159
front: StencilFaceState::IGNORE,
160
back: StencilFaceState::IGNORE,
161
read_mask: 0,
162
write_mask: 0,
163
},
164
bias: DepthBiasState {
165
constant: 0,
166
slope_scale: 0.0,
167
clamp: 0.0,
168
},
169
}),
170
multisample: MultisampleState {
171
count: key.mesh_key.msaa_samples(),
172
mask: !0,
173
alpha_to_coverage_enabled: false,
174
},
175
label: Some("LineGizmo Pipeline 2D".into()),
176
..default()
177
}
178
}
179
}
180
181
#[derive(Clone, Resource)]
182
struct LineJointGizmoPipeline {
183
mesh_pipeline: Mesh2dPipeline,
184
uniform_layout: BindGroupLayout,
185
shader: Handle<Shader>,
186
}
187
188
#[derive(PartialEq, Eq, Hash, Clone)]
189
struct LineJointGizmoPipelineKey {
190
mesh_key: Mesh2dPipelineKey,
191
joints: GizmoLineJoint,
192
}
193
194
impl SpecializedRenderPipeline for LineJointGizmoPipeline {
195
type Key = LineJointGizmoPipelineKey;
196
197
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
198
let format = if key.mesh_key.contains(Mesh2dPipelineKey::HDR) {
199
ViewTarget::TEXTURE_FORMAT_HDR
200
} else {
201
TextureFormat::bevy_default()
202
};
203
204
let shader_defs = vec![
205
#[cfg(feature = "webgl")]
206
"SIXTEEN_BYTE_ALIGNMENT".into(),
207
];
208
209
let layout = vec![
210
self.mesh_pipeline.view_layout.clone(),
211
self.uniform_layout.clone(),
212
];
213
214
if key.joints == GizmoLineJoint::None {
215
error!("There is no entry point for line joints with GizmoLineJoints::None. Please consider aborting the drawing process before reaching this stage.");
216
};
217
218
let entry_point = match key.joints {
219
GizmoLineJoint::Miter => "vertex_miter",
220
GizmoLineJoint::Round(_) => "vertex_round",
221
GizmoLineJoint::None | GizmoLineJoint::Bevel => "vertex_bevel",
222
};
223
224
RenderPipelineDescriptor {
225
vertex: VertexState {
226
shader: self.shader.clone(),
227
entry_point: Some(entry_point.into()),
228
shader_defs: shader_defs.clone(),
229
buffers: line_joint_gizmo_vertex_buffer_layouts(),
230
},
231
fragment: Some(FragmentState {
232
shader: self.shader.clone(),
233
shader_defs,
234
targets: vec![Some(ColorTargetState {
235
format,
236
blend: Some(BlendState::ALPHA_BLENDING),
237
write_mask: ColorWrites::ALL,
238
})],
239
..default()
240
}),
241
layout,
242
primitive: PrimitiveState::default(),
243
depth_stencil: Some(DepthStencilState {
244
format: CORE_2D_DEPTH_FORMAT,
245
depth_write_enabled: false,
246
depth_compare: CompareFunction::Always,
247
stencil: StencilState {
248
front: StencilFaceState::IGNORE,
249
back: StencilFaceState::IGNORE,
250
read_mask: 0,
251
write_mask: 0,
252
},
253
bias: DepthBiasState {
254
constant: 0,
255
slope_scale: 0.0,
256
clamp: 0.0,
257
},
258
}),
259
multisample: MultisampleState {
260
count: key.mesh_key.msaa_samples(),
261
mask: !0,
262
alpha_to_coverage_enabled: false,
263
},
264
label: Some("LineJointGizmo Pipeline 2D".into()),
265
..default()
266
}
267
}
268
}
269
270
type DrawLineGizmo2d = (
271
SetItemPipeline,
272
SetMesh2dViewBindGroup<0>,
273
SetLineGizmoBindGroup<1>,
274
DrawLineGizmo<false>,
275
);
276
type DrawLineGizmo2dStrip = (
277
SetItemPipeline,
278
SetMesh2dViewBindGroup<0>,
279
SetLineGizmoBindGroup<1>,
280
DrawLineGizmo<true>,
281
);
282
type DrawLineJointGizmo2d = (
283
SetItemPipeline,
284
SetMesh2dViewBindGroup<0>,
285
SetLineGizmoBindGroup<1>,
286
DrawLineJointGizmo,
287
);
288
289
fn queue_line_gizmos_2d(
290
draw_functions: Res<DrawFunctions<Transparent2d>>,
291
pipeline: Res<LineGizmoPipeline>,
292
mut pipelines: ResMut<SpecializedRenderPipelines<LineGizmoPipeline>>,
293
pipeline_cache: Res<PipelineCache>,
294
line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>,
295
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
296
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
297
mut views: Query<(&ExtractedView, &Msaa, Option<&RenderLayers>)>,
298
) {
299
let draw_function = draw_functions.read().get_id::<DrawLineGizmo2d>().unwrap();
300
let draw_function_strip = draw_functions
301
.read()
302
.get_id::<DrawLineGizmo2dStrip>()
303
.unwrap();
304
305
for (view, msaa, render_layers) in &mut views {
306
let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity)
307
else {
308
continue;
309
};
310
311
let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples())
312
| Mesh2dPipelineKey::from_hdr(view.hdr);
313
314
let render_layers = render_layers.unwrap_or_default();
315
for (entity, main_entity, config) in &line_gizmos {
316
if !config.render_layers.intersects(render_layers) {
317
continue;
318
}
319
320
let Some(line_gizmo) = line_gizmo_assets.get(&config.handle) else {
321
continue;
322
};
323
324
if line_gizmo.list_vertex_count > 0 {
325
let pipeline = pipelines.specialize(
326
&pipeline_cache,
327
&pipeline,
328
LineGizmoPipelineKey {
329
mesh_key,
330
strip: false,
331
line_style: config.line_style,
332
},
333
);
334
transparent_phase.add(Transparent2d {
335
entity: (entity, *main_entity),
336
draw_function,
337
pipeline,
338
sort_key: FloatOrd(f32::INFINITY),
339
batch_range: 0..1,
340
extra_index: PhaseItemExtraIndex::None,
341
extracted_index: usize::MAX,
342
indexed: false,
343
});
344
}
345
346
if line_gizmo.strip_vertex_count >= 2 {
347
let pipeline = pipelines.specialize(
348
&pipeline_cache,
349
&pipeline,
350
LineGizmoPipelineKey {
351
mesh_key,
352
strip: true,
353
line_style: config.line_style,
354
},
355
);
356
transparent_phase.add(Transparent2d {
357
entity: (entity, *main_entity),
358
draw_function: draw_function_strip,
359
pipeline,
360
sort_key: FloatOrd(f32::INFINITY),
361
batch_range: 0..1,
362
extra_index: PhaseItemExtraIndex::None,
363
extracted_index: usize::MAX,
364
indexed: false,
365
});
366
}
367
}
368
}
369
}
370
fn queue_line_joint_gizmos_2d(
371
draw_functions: Res<DrawFunctions<Transparent2d>>,
372
pipeline: Res<LineJointGizmoPipeline>,
373
mut pipelines: ResMut<SpecializedRenderPipelines<LineJointGizmoPipeline>>,
374
pipeline_cache: Res<PipelineCache>,
375
line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>,
376
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
377
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
378
mut views: Query<(&ExtractedView, &Msaa, Option<&RenderLayers>)>,
379
) {
380
let draw_function = draw_functions
381
.read()
382
.get_id::<DrawLineJointGizmo2d>()
383
.unwrap();
384
385
for (view, msaa, render_layers) in &mut views {
386
let Some(transparent_phase) = transparent_render_phases.get_mut(&view.retained_view_entity)
387
else {
388
continue;
389
};
390
391
let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples())
392
| Mesh2dPipelineKey::from_hdr(view.hdr);
393
394
let render_layers = render_layers.unwrap_or_default();
395
for (entity, main_entity, config) in &line_gizmos {
396
if !config.render_layers.intersects(render_layers) {
397
continue;
398
}
399
400
let Some(line_gizmo) = line_gizmo_assets.get(&config.handle) else {
401
continue;
402
};
403
404
if line_gizmo.strip_vertex_count < 3 || config.line_joints == GizmoLineJoint::None {
405
continue;
406
}
407
408
let pipeline = pipelines.specialize(
409
&pipeline_cache,
410
&pipeline,
411
LineJointGizmoPipelineKey {
412
mesh_key,
413
joints: config.line_joints,
414
},
415
);
416
transparent_phase.add(Transparent2d {
417
entity: (entity, *main_entity),
418
draw_function,
419
pipeline,
420
sort_key: FloatOrd(f32::INFINITY),
421
batch_range: 0..1,
422
extra_index: PhaseItemExtraIndex::None,
423
extracted_index: usize::MAX,
424
indexed: false,
425
});
426
}
427
}
428
}
429
430