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