1use crate::renderer::DrawPass;
2use crate::renderer::model::{Model, ModelUniform};
3use crate::types::Color;
4
5const VS_SRC: &str = include_str!("../../shaders/shape_pipeline_vs.wgsl");
6const FS_SRC: &str = include_str!("../../shaders/shape_pipeline_fs.wgsl");
7
8const SPRITE_VS_SRC: &str = include_str!("../../shaders/sprite_pipeline_vs.wgsl");
9const SPRITE_FS_SRC: &str = include_str!("../../shaders/sprite_pipeline_fs.wgsl");
10
11const WORLD_SPRITE_VS_SRC: &str = include_str!("../../shaders/world_sprite_pipeline_vs.wgsl");
12const WORLD_SPRITE_FS_SRC: &str = include_str!("../../shaders/world_sprite_pipeline_fs.wgsl");
13
14const WORLD_SPRITE_OUTLINE_VS_SRC: &str = include_str!("../../shaders/world_sprite_outline_vs.wgsl");
15
16const MODEL_VS_SRC: &str = include_str!("../../shaders/model_pipeline_vs.wgsl");
17const MODEL_FS_SRC: &str = include_str!("../../shaders/model_pipeline_fs.wgsl");
18
19const MODEL_OUTLINE_VS_SRC: &str = include_str!("../../shaders/model_outline_vs.wgsl");
20const MODEL_OUTLINE_FS_SRC: &str = include_str!("../../shaders/model_outline_fs.wgsl");
21
22pub(crate) const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
25
26pub(crate) fn overlay_depth_state() -> wgpu::DepthStencilState {
29 wgpu::DepthStencilState {
30 format: DEPTH_FORMAT,
31 depth_write_enabled: Some(false),
32 depth_compare: Some(wgpu::CompareFunction::Always),
33 stencil: wgpu::StencilState::default(),
34 bias: wgpu::DepthBiasState::default(),
35 }
36}
37
38pub(crate) fn world_depth_state() -> wgpu::DepthStencilState {
40 wgpu::DepthStencilState {
41 format: DEPTH_FORMAT,
42 depth_write_enabled: Some(true),
43 depth_compare: Some(wgpu::CompareFunction::Less),
44 stencil: wgpu::StencilState::default(),
45 bias: wgpu::DepthBiasState::default(),
46 }
47}
48
49pub(crate) fn outline_depth_state() -> wgpu::DepthStencilState {
53 wgpu::DepthStencilState {
54 format: DEPTH_FORMAT,
55 depth_write_enabled: Some(false),
56 depth_compare: Some(wgpu::CompareFunction::LessEqual),
57 stencil: wgpu::StencilState::default(),
58 bias: wgpu::DepthBiasState::default(),
59 }
60}
61
62trait Vertex {
63 fn desc() -> wgpu::VertexBufferLayout<'static>;
64}
65
66#[repr(C)]
68struct ImmediateVertex {
69 pos: [f32; 2],
70 color: [f32; 4],
71}
72
73impl Vertex for ImmediateVertex {
74 fn desc() -> wgpu::VertexBufferLayout<'static> {
75 wgpu::VertexBufferLayout {
76 array_stride: std::mem::size_of::<ImmediateVertex>() as u64,
77 step_mode: wgpu::VertexStepMode::Vertex,
78 attributes: &[
79 wgpu::VertexAttribute {
80 format: wgpu::VertexFormat::Float32x2,
81 offset: 0,
82 shader_location: 0,
83 },
84 wgpu::VertexAttribute {
85 format: wgpu::VertexFormat::Float32x4,
86 offset: std::mem::size_of::<f32>() as u64 * 2,
87 shader_location: 1,
88 },
89 ],
90 }
91 }
92}
93
94#[repr(C)]
96struct SpriteVertex {
97 pos: [f32; 2],
98 uv: [f32; 2],
99 color: [f32; 4],
100}
101
102impl Vertex for SpriteVertex {
103 fn desc() -> wgpu::VertexBufferLayout<'static> {
104 wgpu::VertexBufferLayout {
105 array_stride: std::mem::size_of::<SpriteVertex>() as u64,
106 step_mode: wgpu::VertexStepMode::Vertex,
107 attributes: &[
108 wgpu::VertexAttribute {
109 format: wgpu::VertexFormat::Float32x2,
110 offset: 0,
111 shader_location: 0,
112 },
113 wgpu::VertexAttribute {
114 format: wgpu::VertexFormat::Float32x2,
115 offset: std::mem::size_of::<f32>() as u64 * 2,
116 shader_location: 1,
117 },
118 wgpu::VertexAttribute {
119 format: wgpu::VertexFormat::Float32x4,
120 offset: std::mem::size_of::<f32>() as u64 * 4,
121 shader_location: 2,
122 },
123 ],
124 }
125 }
126}
127
128#[repr(C)]
134struct WorldSpriteVertex {
135 pos: [f32; 3],
136 uv: [f32; 2],
137 color: [f32; 4],
138 normal: [f32; 3],
139 highlight: [f32; 4],
140}
141
142impl Vertex for WorldSpriteVertex {
143 fn desc() -> wgpu::VertexBufferLayout<'static> {
144 wgpu::VertexBufferLayout {
145 array_stride: std::mem::size_of::<WorldSpriteVertex>() as u64,
146 step_mode: wgpu::VertexStepMode::Vertex,
147 attributes: &[
148 wgpu::VertexAttribute {
149 format: wgpu::VertexFormat::Float32x3,
150 offset: 0,
151 shader_location: 0,
152 },
153 wgpu::VertexAttribute {
154 format: wgpu::VertexFormat::Float32x2,
155 offset: std::mem::size_of::<f32>() as u64 * 3,
156 shader_location: 1,
157 },
158 wgpu::VertexAttribute {
159 format: wgpu::VertexFormat::Float32x4,
160 offset: std::mem::size_of::<f32>() as u64 * 5,
161 shader_location: 2,
162 },
163 wgpu::VertexAttribute {
164 format: wgpu::VertexFormat::Float32x3,
165 offset: std::mem::size_of::<f32>() as u64 * 9,
166 shader_location: 3,
167 },
168 wgpu::VertexAttribute {
169 format: wgpu::VertexFormat::Float32x4,
170 offset: std::mem::size_of::<f32>() as u64 * 12,
171 shader_location: 4,
172 },
173 ],
174 }
175 }
176}
177
178#[repr(C)]
182struct OutlineVertex {
183 pos: [f32; 3],
184}
185
186impl Vertex for OutlineVertex {
187 fn desc() -> wgpu::VertexBufferLayout<'static> {
188 wgpu::VertexBufferLayout {
189 array_stride: std::mem::size_of::<OutlineVertex>() as u64,
190 step_mode: wgpu::VertexStepMode::Vertex,
191 attributes: &[wgpu::VertexAttribute {
192 format: wgpu::VertexFormat::Float32x3,
193 offset: 0,
194 shader_location: 0,
195 }],
196 }
197 }
198}
199
200#[repr(C)]
202pub(crate) struct ModelVertex {
203 pub(crate) position: [f32; 3],
204 pub(crate) tex_coords: [f32; 2],
205 pub(crate) normal: [f32; 3],
206}
207
208impl Vertex for ModelVertex {
209 fn desc() -> wgpu::VertexBufferLayout<'static> {
210 wgpu::VertexBufferLayout {
211 array_stride: std::mem::size_of::<ModelVertex>() as u64,
212 step_mode: wgpu::VertexStepMode::Vertex,
213 attributes: &[
214 wgpu::VertexAttribute {
215 format: wgpu::VertexFormat::Float32x3,
216 offset: 0,
217 shader_location: 0,
218 },
219 wgpu::VertexAttribute {
220 format: wgpu::VertexFormat::Float32x2,
221 offset: std::mem::size_of::<f32>() as u64 * 3,
222 shader_location: 1,
223 },
224 wgpu::VertexAttribute {
225 format: wgpu::VertexFormat::Float32x3,
226 offset: std::mem::size_of::<f32>() as u64 * 5,
227 shader_location: 2,
228 },
229 ],
230 }
231 }
232}
233
234pub fn create_shape_pipeline(device: &wgpu::Device, format: wgpu::TextureFormat) -> wgpu::RenderPipeline {
235 let vs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
236 label: Some("Shape Shader VS"),
237 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(VS_SRC)),
238 });
239 let fs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
240 label: Some("Shape Shader FS"),
241 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(FS_SRC)),
242 });
243
244 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
245 label: Some("Shape Pipeline Layout"),
246 bind_group_layouts: &[],
247 immediate_size: 0,
248 });
249
250 let wgpu_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
251 label: Some("Shape Pipeline"),
252 layout: Some(&layout),
253 vertex: wgpu::VertexState {
254 module: &vs_module,
255 entry_point: Some("vs_main"),
256 buffers: &[Some(wgpu::VertexBufferLayout {
257 array_stride: std::mem::size_of::<ImmediateVertex>() as u64,
258 step_mode: wgpu::VertexStepMode::Vertex,
259 attributes: &[
260 wgpu::VertexAttribute {
261 format: wgpu::VertexFormat::Float32x2,
262 offset: 0,
263 shader_location: 0,
264 },
265 wgpu::VertexAttribute {
266 format: wgpu::VertexFormat::Float32x4,
267 offset: std::mem::size_of::<f32>() as u64 * 2,
268 shader_location: 1,
269 },
270 ],
271 })],
272 compilation_options: wgpu::PipelineCompilationOptions::default(),
273 },
274 primitive: wgpu::PrimitiveState {
275 topology: wgpu::PrimitiveTopology::TriangleList,
276 strip_index_format: None,
277 front_face: wgpu::FrontFace::Ccw,
278 cull_mode: None,
279 unclipped_depth: false,
280 polygon_mode: wgpu::PolygonMode::Fill,
281 conservative: false,
282 },
283 depth_stencil: Some(overlay_depth_state()),
284 multisample: wgpu::MultisampleState {
285 count: 1,
286 mask: !0,
287 alpha_to_coverage_enabled: false,
288 },
289 fragment: Some(wgpu::FragmentState {
290 module: &fs_module,
291 entry_point: Some("fs_main"),
292 targets: &[Some(wgpu::ColorTargetState {
293 format,
294 blend: Some(wgpu::BlendState {
295 color: wgpu::BlendComponent {
296 src_factor: wgpu::BlendFactor::SrcAlpha,
297 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
298 operation: wgpu::BlendOperation::Add,
299 },
300 alpha: wgpu::BlendComponent {
301 src_factor: wgpu::BlendFactor::SrcAlpha,
302 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
303 operation: wgpu::BlendOperation::Add,
304 },
305 }),
306 write_mask: wgpu::ColorWrites::ALL,
307 })],
308 compilation_options: wgpu::PipelineCompilationOptions::default(),
309 }),
310 multiview_mask: None,
311 cache: None,
312 });
313 wgpu_pipeline
314}
315
316pub fn create_sprite_pipeline(device: &wgpu::Device, format: wgpu::TextureFormat, texture_layout: &wgpu::BindGroupLayout) -> wgpu::RenderPipeline {
317 let vs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
318 label: Some("Sprite Shader VS"),
319 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(SPRITE_VS_SRC)),
320 });
321 let fs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
322 label: Some("Sprite Shader FS"),
323 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(SPRITE_FS_SRC)),
324 });
325
326 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
327 label: Some("Sprite Pipeline Layout"),
328 bind_group_layouts: &[Some(texture_layout)],
329 immediate_size: 0,
330 });
331
332 let wgpu_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
333 label: Some("Sprite Pipeline"),
334 layout: Some(&layout),
335 vertex: wgpu::VertexState {
336 module: &vs_module,
337 entry_point: Some("vs_main"),
338 buffers: &[Some(SpriteVertex::desc())],
339 compilation_options: wgpu::PipelineCompilationOptions::default(),
340 },
341 primitive: wgpu::PrimitiveState {
342 topology: wgpu::PrimitiveTopology::TriangleList,
343 strip_index_format: None,
344 front_face: wgpu::FrontFace::Ccw,
345 cull_mode: None,
346 unclipped_depth: false,
347 polygon_mode: wgpu::PolygonMode::Fill,
348 conservative: false,
349 },
350 depth_stencil: Some(overlay_depth_state()),
351 multisample: wgpu::MultisampleState {
352 count: 1,
353 mask: !0,
354 alpha_to_coverage_enabled: false,
355 },
356 fragment: Some(wgpu::FragmentState {
357 module: &fs_module,
358 entry_point: Some("fs_main"),
359 targets: &[Some(wgpu::ColorTargetState {
360 format,
361 blend: Some(wgpu::BlendState {
362 color: wgpu::BlendComponent {
363 src_factor: wgpu::BlendFactor::SrcAlpha,
364 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
365 operation: wgpu::BlendOperation::Add,
366 },
367 alpha: wgpu::BlendComponent {
368 src_factor: wgpu::BlendFactor::SrcAlpha,
369 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
370 operation: wgpu::BlendOperation::Add,
371 },
372 }),
373 write_mask: wgpu::ColorWrites::ALL,
374 })],
375 compilation_options: wgpu::PipelineCompilationOptions::default(),
376 }),
377 multiview_mask: None,
378 cache: None,
379 });
380 wgpu_pipeline
381}
382
383pub fn create_world_sprite_pipeline(
384 device: &wgpu::Device,
385 format: wgpu::TextureFormat,
386 texture_layout: &wgpu::BindGroupLayout,
387 camera_layout: &wgpu::BindGroupLayout,
388) -> wgpu::RenderPipeline {
389 let vs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
390 label: Some("World Sprite Shader VS"),
391 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(WORLD_SPRITE_VS_SRC)),
392 });
393 let fs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
394 label: Some("World Sprite Shader FS"),
395 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(WORLD_SPRITE_FS_SRC)),
396 });
397
398 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
399 label: Some("World Sprite Pipeline Layout"),
400 bind_group_layouts: &[Some(texture_layout), Some(camera_layout)],
401 immediate_size: 0,
402 });
403
404 let wgpu_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
405 label: Some("World Sprite Pipeline"),
406 layout: Some(&layout),
407 vertex: wgpu::VertexState {
408 module: &vs_module,
409 entry_point: Some("vs_main"),
410 buffers: &[Some(WorldSpriteVertex::desc())],
411 compilation_options: wgpu::PipelineCompilationOptions::default(),
412 },
413 primitive: wgpu::PrimitiveState {
414 topology: wgpu::PrimitiveTopology::TriangleList,
415 strip_index_format: None,
416 front_face: wgpu::FrontFace::Ccw,
417 cull_mode: None,
418 unclipped_depth: false,
419 polygon_mode: wgpu::PolygonMode::Fill,
420 conservative: false,
421 },
422 depth_stencil: Some(world_depth_state()),
423 multisample: wgpu::MultisampleState {
424 count: 1,
425 mask: !0,
426 alpha_to_coverage_enabled: false,
427 },
428 fragment: Some(wgpu::FragmentState {
429 module: &fs_module,
430 entry_point: Some("fs_main"),
431 targets: &[Some(wgpu::ColorTargetState {
432 format,
433 blend: Some(wgpu::BlendState {
434 color: wgpu::BlendComponent {
435 src_factor: wgpu::BlendFactor::SrcAlpha,
436 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
437 operation: wgpu::BlendOperation::Add,
438 },
439 alpha: wgpu::BlendComponent {
440 src_factor: wgpu::BlendFactor::SrcAlpha,
441 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
442 operation: wgpu::BlendOperation::Add,
443 },
444 }),
445 write_mask: wgpu::ColorWrites::ALL,
446 })],
447 compilation_options: wgpu::PipelineCompilationOptions::default(),
448 }),
449 multiview_mask: None,
450 cache: None,
451 });
452 wgpu_pipeline
453}
454
455pub fn create_world_sprite_outline_pipeline(
462 device: &wgpu::Device,
463 format: wgpu::TextureFormat,
464 camera_layout: &wgpu::BindGroupLayout,
465) -> wgpu::RenderPipeline {
466 let vs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
467 label: Some("World Sprite Outline Shader VS"),
468 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(WORLD_SPRITE_OUTLINE_VS_SRC)),
469 });
470 let fs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
471 label: Some("World Sprite Outline Shader FS"),
472 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(MODEL_OUTLINE_FS_SRC)),
473 });
474
475 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
476 label: Some("World Sprite Outline Pipeline Layout"),
477 bind_group_layouts: &[None, Some(camera_layout)],
478 immediate_size: 0,
479 });
480
481 let wgpu_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
482 label: Some("World Sprite Outline Pipeline"),
483 layout: Some(&layout),
484 vertex: wgpu::VertexState {
485 module: &vs_module,
486 entry_point: Some("vs_main"),
487 buffers: &[Some(OutlineVertex::desc())],
488 compilation_options: wgpu::PipelineCompilationOptions::default(),
489 },
490 primitive: wgpu::PrimitiveState {
491 topology: wgpu::PrimitiveTopology::TriangleList,
492 strip_index_format: None,
493 front_face: wgpu::FrontFace::Ccw,
494 cull_mode: None,
495 unclipped_depth: false,
496 polygon_mode: wgpu::PolygonMode::Fill,
497 conservative: false,
498 },
499 depth_stencil: Some(outline_depth_state()),
500 multisample: wgpu::MultisampleState {
501 count: 1,
502 mask: !0,
503 alpha_to_coverage_enabled: false,
504 },
505 fragment: Some(wgpu::FragmentState {
506 module: &fs_module,
507 entry_point: Some("fs_main"),
508 targets: &[Some(wgpu::ColorTargetState {
509 format,
510 blend: Some(wgpu::BlendState {
511 color: wgpu::BlendComponent {
512 src_factor: wgpu::BlendFactor::SrcAlpha,
513 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
514 operation: wgpu::BlendOperation::Add,
515 },
516 alpha: wgpu::BlendComponent {
517 src_factor: wgpu::BlendFactor::SrcAlpha,
518 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
519 operation: wgpu::BlendOperation::Add,
520 },
521 }),
522 write_mask: wgpu::ColorWrites::ALL,
523 })],
524 compilation_options: wgpu::PipelineCompilationOptions::default(),
525 }),
526 multiview_mask: None,
527 cache: None,
528 });
529 wgpu_pipeline
530}
531
532pub fn create_model_pipeline(
533 device: &wgpu::Device,
534 format: wgpu::TextureFormat,
535 texture_layout: &wgpu::BindGroupLayout,
536 camera_layout: &wgpu::BindGroupLayout,
537 model_transform_layout: &wgpu::BindGroupLayout,
538) -> wgpu::RenderPipeline {
539 let vs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
540 label: Some("Model Shader VS"),
541 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(MODEL_VS_SRC)),
542 });
543 let fs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
544 label: Some("Model Shader FS"),
545 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(MODEL_FS_SRC)),
546 });
547
548 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
549 label: Some("Model Pipeline Layout"),
550 bind_group_layouts: &[Some(texture_layout), Some(camera_layout), Some(model_transform_layout)],
551 immediate_size: 0,
552 });
553
554 let wgpu_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
555 label: Some("Model Pipeline"),
556 layout: Some(&layout),
557 vertex: wgpu::VertexState {
558 module: &vs_module,
559 entry_point: Some("vs_main"),
560 buffers: &[Some(ModelVertex::desc())],
561 compilation_options: wgpu::PipelineCompilationOptions::default(),
562 },
563 primitive: wgpu::PrimitiveState {
564 topology: wgpu::PrimitiveTopology::TriangleList,
565 strip_index_format: None,
566 front_face: wgpu::FrontFace::Ccw,
567 cull_mode: Some(wgpu::Face::Back),
568 unclipped_depth: false,
569 polygon_mode: wgpu::PolygonMode::Fill,
570 conservative: false,
571 },
572 depth_stencil: Some(world_depth_state()),
573 multisample: wgpu::MultisampleState {
574 count: 1,
575 mask: !0,
576 alpha_to_coverage_enabled: false,
577 },
578 fragment: Some(wgpu::FragmentState {
579 module: &fs_module,
580 entry_point: Some("fs_main"),
581 targets: &[Some(wgpu::ColorTargetState {
582 format,
583 blend: Some(wgpu::BlendState {
584 color: wgpu::BlendComponent {
585 src_factor: wgpu::BlendFactor::SrcAlpha,
586 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
587 operation: wgpu::BlendOperation::Add,
588 },
589 alpha: wgpu::BlendComponent {
590 src_factor: wgpu::BlendFactor::SrcAlpha,
591 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
592 operation: wgpu::BlendOperation::Add,
593 },
594 }),
595 write_mask: wgpu::ColorWrites::ALL,
596 })],
597 compilation_options: wgpu::PipelineCompilationOptions::default(),
598 }),
599 multiview_mask: None,
600 cache: None,
601 });
602 wgpu_pipeline
603}
604
605pub fn create_model_outline_pipeline(
611 device: &wgpu::Device,
612 format: wgpu::TextureFormat,
613 camera_layout: &wgpu::BindGroupLayout,
614 model_transform_layout: &wgpu::BindGroupLayout,
615) -> wgpu::RenderPipeline {
616 let vs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
617 label: Some("Model Outline Shader VS"),
618 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(MODEL_OUTLINE_VS_SRC)),
619 });
620 let fs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
621 label: Some("Model Outline Shader FS"),
622 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(MODEL_OUTLINE_FS_SRC)),
623 });
624
625 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
626 label: Some("Model Outline Pipeline Layout"),
627 bind_group_layouts: &[None, Some(camera_layout), Some(model_transform_layout)],
628 immediate_size: 0,
629 });
630
631 let wgpu_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
632 label: Some("Model Outline Pipeline"),
633 layout: Some(&layout),
634 vertex: wgpu::VertexState {
635 module: &vs_module,
636 entry_point: Some("vs_main"),
637 buffers: &[Some(ModelVertex::desc())],
638 compilation_options: wgpu::PipelineCompilationOptions::default(),
639 },
640 primitive: wgpu::PrimitiveState {
641 topology: wgpu::PrimitiveTopology::TriangleList,
642 strip_index_format: None,
643 front_face: wgpu::FrontFace::Ccw,
644 cull_mode: Some(wgpu::Face::Front),
645 unclipped_depth: false,
646 polygon_mode: wgpu::PolygonMode::Fill,
647 conservative: false,
648 },
649 depth_stencil: Some(outline_depth_state()),
650 multisample: wgpu::MultisampleState {
651 count: 1,
652 mask: !0,
653 alpha_to_coverage_enabled: false,
654 },
655 fragment: Some(wgpu::FragmentState {
656 module: &fs_module,
657 entry_point: Some("fs_main"),
658 targets: &[Some(wgpu::ColorTargetState {
659 format,
660 blend: Some(wgpu::BlendState {
661 color: wgpu::BlendComponent {
662 src_factor: wgpu::BlendFactor::SrcAlpha,
663 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
664 operation: wgpu::BlendOperation::Add,
665 },
666 alpha: wgpu::BlendComponent {
667 src_factor: wgpu::BlendFactor::SrcAlpha,
668 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
669 operation: wgpu::BlendOperation::Add,
670 },
671 }),
672 write_mask: wgpu::ColorWrites::ALL,
673 })],
674 compilation_options: wgpu::PipelineCompilationOptions::default(),
675 }),
676 multiview_mask: None,
677 cache: None,
678 });
679 wgpu_pipeline
680}
681
682impl DrawPass<'_> {
683 pub fn draw_rect(&mut self, x: f32, y: f32, w: f32, h: f32, thickness: f32, color: Color) {
684 let c = color.as_wgpu_color();
685 let rgba = [c.r as f32, c.g as f32, c.b as f32, c.a as f32];
686 let mut verts: Vec<ImmediateVertex> = Vec::new();
687
688 if thickness <= 0.0 {
689 let x2 = x + w;
690 let y2 = y + h;
691 let p0 = self.to_clip(x, y);
692 let p1 = self.to_clip(x2, y);
693 let p2 = self.to_clip(x, y2);
694 let p3 = self.to_clip(x2, y2);
695
696 verts.push(ImmediateVertex { pos: p0, color: rgba });
697 verts.push(ImmediateVertex { pos: p1, color: rgba });
698 verts.push(ImmediateVertex { pos: p2, color: rgba });
699 verts.push(ImmediateVertex { pos: p1, color: rgba });
700 verts.push(ImmediateVertex { pos: p3, color: rgba });
701 verts.push(ImmediateVertex { pos: p2, color: rgba });
702 } else {
703 let th = thickness;
704 for &(px, py) in &[
705 (x, y), (x + w, y), (x, y + th),
706 (x + w, y), (x + w, y + th), (x, y + th),
707 ] {
708 verts.push(ImmediateVertex { pos: self.to_clip(px, py), color: rgba });
709 }
710 for &(px, py) in &[
711 (x, y + h - th), (x + w, y + h - th), (x, y + h),
712 (x + w, y + h - th), (x + w, y + h), (x, y + h),
713 ] {
714 verts.push(ImmediateVertex { pos: self.to_clip(px, py), color: rgba });
715 }
716 for &(px, py) in &[
717 (x, y), (x + th, y), (x, y + h),
718 (x + th, y), (x + th, y + h), (x, y + h),
719 ] {
720 verts.push(ImmediateVertex { pos: self.to_clip(px, py), color: rgba });
721 }
722 for &(px, py) in &[
723 (x + w - th, y), (x + w, y), (x + w - th, y + h),
724 (x + w, y), (x + w, y + h), (x + w - th, y + h),
725 ] {
726 verts.push(ImmediateVertex { pos: self.to_clip(px, py), color: rgba });
727 }
728 }
729
730 self.upload_and_draw(&verts);
731 }
732
733 pub fn draw_line(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, thickness: f32, color: Color) {
734 let dx = x2 - x1;
735 let dy = y2 - y1;
736 let len = (dx * dx + dy * dy).sqrt();
737 if len == 0.0 {
738 return;
739 }
740
741 let c = color.as_wgpu_color();
742 let rgba = [c.r as f32, c.g as f32, c.b as f32, c.a as f32];
743
744 let hw = thickness * 0.5;
745 let px = -dy / len * hw;
746 let py = dx / len * hw;
747
748 let p0 = self.to_clip(x1 + px, y1 + py);
749 let p1 = self.to_clip(x1 - px, y1 - py);
750 let p2 = self.to_clip(x2 + px, y2 + py);
751 let p3 = self.to_clip(x2 - px, y2 - py);
752
753 let verts = vec![
754 ImmediateVertex { pos: p0, color: rgba },
755 ImmediateVertex { pos: p1, color: rgba },
756 ImmediateVertex { pos: p2, color: rgba },
757 ImmediateVertex { pos: p1, color: rgba },
758 ImmediateVertex { pos: p3, color: rgba },
759 ImmediateVertex { pos: p2, color: rgba },
760 ];
761
762 self.upload_and_draw(&verts);
763 }
764
765 pub fn draw_ellipse(&mut self, cx: f32, cy: f32, rx: f32, ry: f32, segments: u32, thickness: f32, color: Color) {
766 if segments < 3 {
767 return;
768 }
769
770 let c = color.as_wgpu_color();
771 let rgba = [c.r as f32, c.g as f32, c.b as f32, c.a as f32];
772
773 let step = std::f32::consts::TAU / segments as f32;
774 let mut verts = Vec::new();
775
776 if thickness <= 0.0 {
777 let center = self.to_clip(cx, cy);
778 for i in 0..segments {
779 let a1 = i as f32 * step;
780 let a2 = (i + 1) as f32 * step;
781 let p1 = self.to_clip(cx + rx * a1.cos(), cy + ry * a1.sin());
782 let p2 = self.to_clip(cx + rx * a2.cos(), cy + ry * a2.sin());
783 verts.push(ImmediateVertex { pos: center, color: rgba });
784 verts.push(ImmediateVertex { pos: p1, color: rgba });
785 verts.push(ImmediateVertex { pos: p2, color: rgba });
786 }
787 } else {
788 let hw = thickness * 0.5;
789 for i in 0..segments {
790 let a1 = i as f32 * step;
791 let a2 = (i + 1) as f32 * step;
792 let cos1 = a1.cos();
793 let sin1 = a1.sin();
794 let cos2 = a2.cos();
795 let sin2 = a2.sin();
796
797 let p_o1 = self.to_clip(cx + (rx + hw) * cos1, cy + (ry + hw) * sin1);
798 let p_i1 = self.to_clip(cx + (rx - hw) * cos1, cy + (ry - hw) * sin1);
799 let p_o2 = self.to_clip(cx + (rx + hw) * cos2, cy + (ry + hw) * sin2);
800 let p_i2 = self.to_clip(cx + (rx - hw) * cos2, cy + (ry - hw) * sin2);
801
802 verts.push(ImmediateVertex { pos: p_o1, color: rgba });
803 verts.push(ImmediateVertex { pos: p_i1, color: rgba });
804 verts.push(ImmediateVertex { pos: p_o2, color: rgba });
805 verts.push(ImmediateVertex { pos: p_i1, color: rgba });
806 verts.push(ImmediateVertex { pos: p_i2, color: rgba });
807 verts.push(ImmediateVertex { pos: p_o2, color: rgba });
808 }
809 }
810
811 self.upload_and_draw(&verts);
812 }
813
814 pub fn draw_world_line(&mut self, camera: &crate::camera::Camera, p0: glam::Vec3, p1: glam::Vec3, thickness: f32, color: Color) {
819 let view_proj = camera.build_view_projection_matrix();
820 let (Some(a), Some(b)) = (
821 Self::project_to_screen(view_proj, p0, self.screen_w, self.screen_h),
822 Self::project_to_screen(view_proj, p1, self.screen_w, self.screen_h),
823 ) else {
824 return;
825 };
826 self.draw_line(a.0, a.1, b.0, b.1, thickness, color);
827 }
828
829 fn project_to_screen(view_proj: glam::Mat4, point: glam::Vec3, screen_w: u32, screen_h: u32) -> Option<(f32, f32)> {
830 let clip = view_proj * point.extend(1.0);
831 if clip.w <= 1e-4 {
832 return None;
833 }
834 let ndc = clip.truncate() / clip.w;
835 let w = screen_w.max(1) as f32;
836 let h = screen_h.max(1) as f32;
837 Some(((ndc.x * 0.5 + 0.5) * w, (1.0 - (ndc.y * 0.5 + 0.5)) * h))
838 }
839
840 fn to_clip(&self, x: f32, y: f32) -> [f32; 2] {
841 let w = (self.screen_w.max(1)) as f32;
842 let h = (self.screen_h.max(1)) as f32;
843 [(x / w) * 2.0 - 1.0, -((y / h) * 2.0 - 1.0)]
844 }
845
846 fn upload_and_draw(&mut self, verts: &[ImmediateVertex]) {
847 if verts.is_empty() {
848 return;
849 }
850 let data = crate::util::slice_to_bytes(verts);
851 let buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
852 label: Some("Shape Vert Buffer"),
853 size: data.len() as u64,
854 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
855 mapped_at_creation: false,
856 });
857 self.queue.write_buffer(&buffer, 0, data);
858 self.pass.set_pipeline(&self.immediate_pipeline);
859 self.pass.set_vertex_buffer(0, buffer.slice(..data.len() as u64));
860 self.pass.draw(0..verts.len() as u32, 0..1);
861 }
862
863 pub fn draw_ui_sprite(
869 &mut self,
870 x: f32, y: f32, w: f32, h: f32,
871 src_x: f32, src_y: f32, src_w: f32, src_h: f32, tex_w: f32, tex_h: f32,
872 color: Color,
873 model: glam::Mat4,
874 bind_group: &wgpu::BindGroup,
875 ) {
876 let c = color.as_wgpu_color();
877 let rgba = [c.r as f32, c.g as f32, c.b as f32, c.a as f32];
878
879 let corner = |lx: f32, ly: f32| {
880 let p = model.transform_point3(glam::Vec3::new(lx, ly, 0.0));
881 self.to_clip(x + p.x, y + p.y)
882 };
883
884 let p0 = corner(0.0, 0.0);
885 let p1 = corner(w, 0.0);
886 let p2 = corner(0.0, h);
887 let p3 = corner(w, h);
888
889 let uv0 = [src_x / tex_w, src_y / tex_h];
890 let uv1 = [(src_x + src_w) / tex_w, src_y / tex_h];
891 let uv2 = [src_x / tex_w, (src_y + src_h) / tex_h];
892 let uv3 = [(src_x + src_w) / tex_w, (src_y + src_h) / tex_h];
893
894 let verts = vec![
895 SpriteVertex { pos: p0, uv: uv0, color: rgba },
896 SpriteVertex { pos: p1, uv: uv1, color: rgba },
897 SpriteVertex { pos: p2, uv: uv2, color: rgba },
898 SpriteVertex { pos: p1, uv: uv1, color: rgba },
899 SpriteVertex { pos: p3, uv: uv3, color: rgba },
900 SpriteVertex { pos: p2, uv: uv2, color: rgba },
901 ];
902
903 let data = crate::util::slice_to_bytes(&verts);
904 let buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
905 label: Some("Sprite Vert Buffer"),
906 size: data.len() as u64,
907 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
908 mapped_at_creation: false,
909 });
910 self.queue.write_buffer(&buffer, 0, data);
911 self.pass.set_pipeline(&self.sprite_pipeline);
912 self.pass.set_bind_group(0, bind_group, &[]);
913 self.pass.set_vertex_buffer(0, buffer.slice(..data.len() as u64));
914 self.pass.draw(0..verts.len() as u32, 0..1);
915 }
916
917 pub fn draw_world_sprite(
924 &mut self,
925 x: f32, y: f32, z: f32, w: f32, h: f32,
926 src_x: f32, src_y: f32, src_w: f32, src_h: f32, tex_w: f32, tex_h: f32,
927 color: Color,
928 highlight: Color,
929 model: glam::Mat4,
930 bind_group: &wgpu::BindGroup,
931 ) {
932 let c = color.as_wgpu_color();
933 let rgba = [c.r as f32, c.g as f32, c.b as f32, c.a as f32];
934 let hl = highlight.as_wgpu_color();
935 let highlight_rgba = [hl.r as f32, hl.g as f32, hl.b as f32, hl.a as f32];
936
937 let origin = glam::Vec3::new(x, y, z);
938 let corner = |lx: f32, ly: f32| {
939 let p = origin + model.transform_point3(glam::Vec3::new(lx, ly, 0.0));
940 [p.x, p.y, p.z]
941 };
942
943 let n = model.transform_vector3(glam::Vec3::Z).normalize_or_zero();
947 let normal = [n.x, n.y, n.z];
948
949 let p0 = corner(0.0, 0.0);
950 let p1 = corner(w, 0.0);
951 let p2 = corner(0.0, h);
952 let p3 = corner(w, h);
953
954 let u_left = src_x / tex_w;
957 let u_right = (src_x + src_w) / tex_w;
958 let v_top = src_y / tex_h;
959 let v_bottom = (src_y + src_h) / tex_h;
960
961 let uv0 = [u_left, v_bottom];
962 let uv1 = [u_right, v_bottom];
963 let uv2 = [u_left, v_top];
964 let uv3 = [u_right, v_top];
965
966 let verts = vec![
967 WorldSpriteVertex { pos: p0, uv: uv0, color: rgba, normal, highlight: highlight_rgba },
968 WorldSpriteVertex { pos: p1, uv: uv1, color: rgba, normal, highlight: highlight_rgba },
969 WorldSpriteVertex { pos: p2, uv: uv2, color: rgba, normal, highlight: highlight_rgba },
970 WorldSpriteVertex { pos: p1, uv: uv1, color: rgba, normal, highlight: highlight_rgba },
971 WorldSpriteVertex { pos: p3, uv: uv3, color: rgba, normal, highlight: highlight_rgba },
972 WorldSpriteVertex { pos: p2, uv: uv2, color: rgba, normal, highlight: highlight_rgba },
973 ];
974
975 let data = crate::util::slice_to_bytes(&verts);
976 let buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
977 label: Some("World Sprite Vert Buffer"),
978 size: data.len() as u64,
979 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
980 mapped_at_creation: false,
981 });
982 self.queue.write_buffer(&buffer, 0, data);
983 self.pass.set_pipeline(&self.world_sprite_pipeline);
984 self.pass.set_bind_group(0, bind_group, &[]);
985 self.pass.set_bind_group(1, self.camera_bind_group, &[]);
986 self.pass.set_vertex_buffer(0, buffer.slice(..data.len() as u64));
987 self.pass.draw(0..verts.len() as u32, 0..1);
988 }
989
990 pub fn draw_world_sprite_outline(&mut self, x: f32, y: f32, z: f32, w: f32, h: f32, model: glam::Mat4) {
998 const OUTLINE_MARGIN_FRACTION: f32 = 0.02;
999 let margin = OUTLINE_MARGIN_FRACTION * w.max(h);
1000
1001 let origin = glam::Vec3::new(x, y, z);
1002 let corner = |lx: f32, ly: f32| {
1003 let p = origin + model.transform_point3(glam::Vec3::new(lx, ly, 0.0));
1004 [p.x, p.y, p.z]
1005 };
1006
1007 let p0 = corner(-margin, -margin);
1008 let p1 = corner(w + margin, -margin);
1009 let p2 = corner(-margin, h + margin);
1010 let p3 = corner(w + margin, h + margin);
1011
1012 let verts = vec![
1013 OutlineVertex { pos: p0 },
1014 OutlineVertex { pos: p1 },
1015 OutlineVertex { pos: p2 },
1016 OutlineVertex { pos: p1 },
1017 OutlineVertex { pos: p3 },
1018 OutlineVertex { pos: p2 },
1019 ];
1020
1021 let data = crate::util::slice_to_bytes(&verts);
1022 let buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
1023 label: Some("World Sprite Outline Vert Buffer"),
1024 size: data.len() as u64,
1025 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
1026 mapped_at_creation: false,
1027 });
1028 self.queue.write_buffer(&buffer, 0, data);
1029 self.pass.set_pipeline(self.world_sprite_outline_pipeline);
1030 self.pass.set_bind_group(1, self.camera_bind_group, &[]);
1031 self.pass.set_vertex_buffer(0, buffer.slice(..data.len() as u64));
1032 self.pass.draw(0..verts.len() as u32, 0..1);
1033 }
1034
1035 pub fn draw_model(&mut self, model: &Model) {
1038 self.write_model_uniform(model);
1039
1040 self.pass.set_pipeline(self.model_pipeline);
1041 self.pass.set_bind_group(1, self.camera_bind_group, &[]);
1042 self.pass.set_bind_group(2, &model.transform_bind_group, &[]);
1043
1044 for mesh in &model.meshes {
1045 let material = &model.materials[mesh.material];
1046 self.pass.set_bind_group(0, &material.diffuse_texture.bind_group, &[]);
1047 self.pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1048 self.pass.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
1049 self.pass.draw_indexed(0..mesh.num_indices, 0, 0..1);
1050 }
1051 }
1052
1053 pub fn draw_model_outline(&mut self, model: &Model) {
1057 self.write_model_uniform(model);
1058
1059 self.pass.set_pipeline(self.model_outline_pipeline);
1060 self.pass.set_bind_group(1, self.camera_bind_group, &[]);
1061 self.pass.set_bind_group(2, &model.transform_bind_group, &[]);
1062
1063 for mesh in &model.meshes {
1064 self.pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
1065 self.pass.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
1066 self.pass.draw_indexed(0..mesh.num_indices, 0, 0..1);
1067 }
1068 }
1069
1070 fn write_model_uniform(&self, model: &Model) {
1071 let highlight = model.highlight.as_wgpu_color();
1072 let uniform = ModelUniform {
1073 model: model.transform.to_cols_array_2d(),
1074 highlight: [highlight.r as f32, highlight.g as f32, highlight.b as f32, highlight.a as f32],
1075 };
1076 self.queue.write_buffer(&model.transform_buffer, 0, crate::util::slice_to_bytes(&[uniform]));
1077 }
1078}