1mod gfx;
6mod gfx_impl;
7pub mod plugin;
8pub mod prelude;
9
10use int_math::{URect, UVec2, Vec2, Vec3};
11use limnus_assets::Assets;
12use limnus_assets::prelude::{Asset, Id, WeakId};
13use limnus_resource::prelude::Resource;
14use limnus_wgpu_math::{Matrix4, OrthoInfo, Vec4};
15use mireforge_font::Font;
16use mireforge_font::FontRef;
17use mireforge_font::WeakFontRef;
18use mireforge_render::prelude::*;
19use mireforge_wgpu::create_nearest_sampler;
20use mireforge_wgpu_sprites::{
21 ShaderInfo, SpriteInfo, SpriteInstanceUniform, create_texture_and_sampler_bind_group_ex,
22 create_texture_and_sampler_group_layout,
23};
24use monotonic_time_rs::Millis;
25use std::cmp::Ordering;
26use std::fmt::{Debug, Display, Formatter};
27use std::mem::swap;
28use std::sync::Arc;
29use tracing::{debug, trace};
30use wgpu::{
31 BindGroup, BindGroupLayout, Buffer, CommandEncoder, Device, RenderPipeline, TextureFormat,
32 TextureView,
33};
34
35pub type MaterialRef = Arc<Material>;
36
37pub type WeakMaterialRef = Arc<Material>;
38
39pub type TextureRef = Id<Texture>;
40pub type WeakTextureRef = WeakId<Texture>;
41
42pub trait FrameLookup {
43 fn lookup(&self, frame: u16) -> (&MaterialRef, URect);
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct FixedAtlas {
48 pub material: MaterialRef,
49 pub texture_size: UVec2,
50 pub one_cell_size: UVec2,
51 pub cell_count_size: UVec2,
52}
53
54impl FixedAtlas {
55 #[must_use]
58 pub fn new(one_cell_size: UVec2, texture_size: UVec2, material_ref: MaterialRef) -> Self {
59 let cell_count_size = UVec2::new(
60 texture_size.x / one_cell_size.x,
61 texture_size.y / one_cell_size.y,
62 );
63
64 assert_ne!(cell_count_size.x, 0, "illegal texture and one cell size");
65
66 Self {
67 material: material_ref,
68 texture_size,
69 one_cell_size,
70 cell_count_size,
71 }
72 }
73}
74
75impl FrameLookup for FixedAtlas {
76 fn lookup(&self, frame: u16) -> (&MaterialRef, URect) {
77 let x = frame % self.cell_count_size.x;
78 let y = frame / self.cell_count_size.x;
79
80 (
81 &self.material,
82 URect::new(
83 x * self.one_cell_size.x,
84 y * self.one_cell_size.y,
85 self.one_cell_size.x,
86 self.one_cell_size.y,
87 ),
88 )
89 }
90}
91
92#[derive(Debug)]
93pub struct NineSliceAndMaterial {
94 pub slices: Slices,
95 pub material_ref: MaterialRef,
96}
97
98#[derive(Debug, PartialEq, Eq)]
99pub struct FontAndMaterial {
100 pub font_ref: FontRef,
101 pub material_ref: MaterialRef,
102}
103
104fn to_wgpu_color(c: Color) -> wgpu::Color {
105 let f = c.to_f64();
106 wgpu::Color {
107 r: f.0,
108 g: f.1,
109 b: f.2,
110 a: f.3,
111 }
112}
113
114#[derive(Debug)]
115struct RenderItem {
116 position: Vec3,
117 material_ref: MaterialRef,
118
119 renderable: Renderable,
120}
121
122#[derive(Debug)]
123pub struct Text {
124 text: String,
125 font_ref: WeakFontRef,
126 color: Color,
127}
128
129#[derive(Debug)]
130enum Renderable {
131 Sprite(Sprite),
132 QuadColor(QuadColor),
133 NineSlice(NineSlice),
134 TileMap(TileMap),
135 Text(Text),
136 Mask(UVec2, Color),
137}
138
139#[derive(Resource)]
140pub struct Render {
141 virtual_surface_texture_view: TextureView,
142 virtual_surface_texture: wgpu::Texture,
143 virtual_to_surface_bind_group: BindGroup,
144 index_buffer: Buffer, vertex_buffer: Buffer, sampler: wgpu::Sampler,
147 virtual_to_screen_shader_info: ShaderInfo,
148 pub normal_sprite_pipeline: ShaderInfo,
149 pub quad_shader_info: ShaderInfo,
150 pub mask_shader_info: ShaderInfo,
151 pub light_shader_info: ShaderInfo,
152 physical_surface_size: UVec2,
153 viewport_strategy: ViewportStrategy,
154 virtual_surface_size: UVec2,
155 camera_bind_group: BindGroup,
157 #[allow(unused)]
158 camera_buffer: Buffer,
159
160 texture_sampler_bind_group_layout: BindGroupLayout,
162
163 quad_matrix_and_uv_instance_buffer: Buffer,
165
166 device: Arc<wgpu::Device>,
167 queue: Arc<wgpu::Queue>, items: Vec<RenderItem>,
171 origin: Vec2,
173
174 batch_offsets: Vec<(WeakMaterialRef, u32, u32)>,
176 viewport: URect,
177 clear_color: wgpu::Color,
178 screen_clear_color: wgpu::Color,
179 last_render_at: Millis,
180 scale: f32,
181 surface_texture_format: TextureFormat,
182
183 debug_tick: u64,
184}
185
186impl Render {}
187
188impl Debug for Render {
189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190 write!(f, "Render")
191 }
192}
193
194impl Render {
195 #[must_use]
196 pub fn new(
197 device: Arc<wgpu::Device>,
198 queue: Arc<wgpu::Queue>, surface_texture_format: wgpu::TextureFormat,
200 physical_size: UVec2,
201 virtual_surface_size: UVec2,
202 now: Millis,
203 ) -> Self {
204 let sprite_info = SpriteInfo::new(
205 &device,
206 surface_texture_format,
207 create_view_uniform_view_projection_matrix(physical_size),
208 );
209
210 let (virtual_surface_texture, virtual_surface_texture_view, virtual_to_surface_bind_group) =
211 Self::create_virtual_texture(&device, surface_texture_format, virtual_surface_size);
212
213 Self {
214 device,
215 queue,
216 surface_texture_format,
217 items: Vec::new(),
218 virtual_to_screen_shader_info: sprite_info.virtual_to_screen_shader_info,
220 virtual_surface_texture,
221 virtual_surface_texture_view,
222 virtual_to_surface_bind_group,
223 sampler: sprite_info.sampler,
224 normal_sprite_pipeline: sprite_info.sprite_shader_info,
225 quad_shader_info: sprite_info.quad_shader_info,
226 mask_shader_info: sprite_info.mask_shader_info,
227 light_shader_info: sprite_info.light_shader_info,
228 texture_sampler_bind_group_layout: sprite_info.sprite_texture_sampler_bind_group_layout,
229 index_buffer: sprite_info.index_buffer,
230 vertex_buffer: sprite_info.vertex_buffer,
231 quad_matrix_and_uv_instance_buffer: sprite_info.quad_matrix_and_uv_instance_buffer,
232 camera_bind_group: sprite_info.camera_bind_group,
233 batch_offsets: Vec::new(),
234 camera_buffer: sprite_info.camera_uniform_buffer,
235 viewport: Self::viewport_from_integer_scale(physical_size, virtual_surface_size),
236 clear_color: to_wgpu_color(Color::from_f32(0.008, 0.015, 0.008, 1.0)),
237 screen_clear_color: to_wgpu_color(Color::from_f32(0.018, 0.025, 0.018, 1.0)),
238 origin: Vec2::new(0, 0),
239 last_render_at: now,
240 physical_surface_size: physical_size,
241 viewport_strategy: ViewportStrategy::FitIntegerScaling,
242 virtual_surface_size,
243 scale: 1.0,
244 debug_tick: 0,
245 }
246 }
247
248 pub fn create_virtual_texture(
249 device: &Device,
250 surface_texture_format: TextureFormat,
251 virtual_surface_size: UVec2,
252 ) -> (wgpu::Texture, TextureView, BindGroup) {
253 let virtual_surface_texture = device.create_texture(&wgpu::TextureDescriptor {
255 label: Some("Render Texture"),
256 size: wgpu::Extent3d {
257 width: virtual_surface_size.x as u32,
258 height: virtual_surface_size.y as u32,
259 depth_or_array_layers: 1,
260 },
261 mip_level_count: 1,
262 sample_count: 1,
263 dimension: wgpu::TextureDimension::D2,
264 format: surface_texture_format, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
266 view_formats: &[],
267 });
268
269 let virtual_surface_texture_view =
270 virtual_surface_texture.create_view(&wgpu::TextureViewDescriptor::default());
271
272 let virtual_to_screen_sampler =
273 create_nearest_sampler(&device, "nearest sampler for virtual to screen");
274 let virtual_to_screen_layout =
275 create_texture_and_sampler_group_layout(&device, "virtual to screen layout");
276 let virtual_to_surface_bind_group = create_texture_and_sampler_bind_group_ex(
277 &device,
278 &virtual_to_screen_layout,
279 &virtual_surface_texture_view,
280 &virtual_to_screen_sampler,
281 "virtual to screen bind group",
282 );
283
284 (
285 virtual_surface_texture,
286 virtual_surface_texture_view,
287 virtual_to_surface_bind_group,
288 )
289 }
290
291 pub const fn set_now(&mut self, now: Millis) {
292 self.last_render_at = now;
293 }
294
295 pub const fn virtual_surface_size_with_scaling(&self) -> UVec2 {
296 match self.viewport_strategy {
297 ViewportStrategy::FitIntegerScaling | ViewportStrategy::FitFloatScaling => {
298 self.virtual_surface_size
299 }
300 ViewportStrategy::MatchPhysicalSize => self.physical_surface_size,
301 }
302 }
303
304 pub const fn physical_surface_size(&self) -> UVec2 {
305 self.physical_surface_size
306 }
307
308 pub const fn viewport(&self) -> URect {
309 self.viewport
310 }
311
312 #[inline]
313 fn push_sprite(&mut self, position: Vec3, material: &MaterialRef, sprite: Sprite) {
314 self.items.push(RenderItem {
315 position,
316 material_ref: material.clone(),
317 renderable: Renderable::Sprite(sprite),
318 });
319 }
320
321 pub fn push_mask(
322 &mut self,
323 position: Vec3,
324 size: UVec2,
325 color: Color,
326 alpha_masked: &MaterialRef,
327 ) {
328 self.items.push(RenderItem {
329 position,
330 material_ref: alpha_masked.clone(),
331 renderable: Renderable::Mask(size, color),
332 });
333 }
334
335 pub fn push_mask_create_material(
336 &mut self,
337 position: Vec3,
338 primary_texture: TextureRef,
339 alpha_texture: TextureRef,
340 texture_offset: UVec2,
341 color: Color,
342 ) {
343 let masked_material = Material {
344 base: MaterialBase {},
345 kind: MaterialKind::AlphaMasker {
346 primary_texture,
347 alpha_texture,
348 },
349 };
350
351 let masked_material_ref = Arc::new(masked_material);
352
353 self.items.push(RenderItem {
354 position,
355 material_ref: masked_material_ref.clone(),
356 renderable: Renderable::Mask(texture_offset, color),
357 });
358 }
359
360 pub fn push_nine_slice(
361 &mut self,
362 position: Vec3,
363 size: UVec2,
364 color: Color,
365 nine_slice_and_material: &NineSliceAndMaterial,
366 ) {
367 let nine_slice_info = NineSlice {
368 size,
369 slices: nine_slice_and_material.slices,
370 color,
371 origin_in_atlas: UVec2::new(0, 0),
372 size_inside_atlas: None,
373 };
374
375 self.items.push(RenderItem {
376 position,
377 material_ref: nine_slice_and_material.material_ref.clone(),
378 renderable: Renderable::NineSlice(nine_slice_info),
379 });
380 }
381
382 #[must_use]
383 pub fn viewport_from_integer_scale(physical_size: UVec2, virtual_size: UVec2) -> URect {
384 let scale_factor = (physical_size.x / virtual_size.x)
385 .min(physical_size.y / virtual_size.y)
386 .max(1);
387
388 let ideal_viewport_size = virtual_size * scale_factor;
389
390 let final_viewport_size =
391 if physical_size.x < ideal_viewport_size.x || physical_size.y < ideal_viewport_size.y {
392 physical_size
393 } else {
394 ideal_viewport_size
395 };
396
397 let border_size = physical_size - final_viewport_size;
398
399 let offset = border_size / 2;
400
401 URect::new(
402 offset.x,
403 offset.y,
404 final_viewport_size.x,
405 final_viewport_size.y,
406 )
407 }
408
409 #[must_use]
410 pub fn viewport_from_float_scale(physical_size: UVec2, virtual_size: UVec2) -> URect {
411 let window_aspect = physical_size.x as f32 / physical_size.y as f32;
412 let virtual_aspect = virtual_size.x as f32 / virtual_size.y as f32;
413
414 if physical_size.x < virtual_size.x || physical_size.y < virtual_size.y {
415 return URect::new(0, 0, physical_size.x, physical_size.y);
416 }
417
418 let mut float_scale = if window_aspect > virtual_aspect {
419 physical_size.y as f32 / virtual_size.y as f32
420 } else {
421 physical_size.x as f32 / virtual_size.x as f32
422 };
423
424 if float_scale < 0.01 {
425 float_scale = 0.01;
426 }
427
428 let viewport_actual_size = UVec2::new(
429 (virtual_size.x as f32 * float_scale) as u16,
430 (virtual_size.y as f32 * float_scale) as u16,
431 );
432
433 let border_size = physical_size - viewport_actual_size;
434
435 let offset = border_size / 2;
436
437 URect::new(
438 offset.x,
439 offset.y,
440 viewport_actual_size.x,
441 viewport_actual_size.y,
442 )
443 }
444
445 pub fn resize(&mut self, physical_size: UVec2) {
446 self.physical_surface_size = physical_size;
447 }
448
449 pub fn resize_virtual(&mut self, virtual_surface_size: UVec2) {
450 if virtual_surface_size == self.virtual_surface_size {
451 return;
452 }
453 self.virtual_surface_size = virtual_surface_size;
454 debug!(?virtual_surface_size, "virtual surface changed");
455 let (virtual_surface_texture, virtual_surface_texture_view, virtual_to_surface_bind_group) =
456 Self::create_virtual_texture(
457 &self.device,
458 self.surface_texture_format,
459 virtual_surface_size,
460 );
461 self.virtual_surface_texture = virtual_surface_texture;
462 self.virtual_surface_texture_view = virtual_surface_texture_view;
463 self.virtual_to_surface_bind_group = virtual_to_surface_bind_group;
464 }
465
466 pub fn sprite_atlas(&mut self, position: Vec3, atlas_rect: URect, material_ref: &MaterialRef) {
467 self.push_sprite(
468 position,
469 material_ref,
470 Sprite {
471 params: SpriteParams {
472 texture_pos: atlas_rect.position,
473 texture_size: atlas_rect.size,
474 ..Default::default()
475 },
476 },
477 );
478 }
479
480 pub fn sprite_atlas_frame(&mut self, position: Vec3, frame: u16, atlas: &impl FrameLookup) {
481 let (material_ref, atlas_rect) = atlas.lookup(frame);
482 self.push_sprite(
483 position,
484 material_ref,
485 Sprite {
486 params: SpriteParams {
487 texture_pos: atlas_rect.position,
488 texture_size: atlas_rect.size,
489 ..Default::default()
490 },
491 },
492 );
493 }
494
495 pub fn sprite_atlas_frame_ex(
496 &mut self,
497 position: Vec3,
498 frame: u16,
499 atlas: &impl FrameLookup,
500 mut params: SpriteParams,
501 ) {
502 let (material_ref, atlas_rect) = atlas.lookup(frame);
503 params.texture_pos = atlas_rect.position;
504 params.texture_size = atlas_rect.size;
505 self.push_sprite(position, material_ref, Sprite { params });
506 }
507
508 pub fn draw_sprite(&mut self, position: Vec3, material: &MaterialRef) {
509 self.push_sprite(
510 position,
511 material,
512 Sprite {
513 params: SpriteParams::default(),
514 },
515 );
516 }
517
518 pub fn draw_sprite_ex(&mut self, position: Vec3, material: &MaterialRef, params: SpriteParams) {
519 self.push_sprite(position, material, Sprite { params });
520 }
521
522 pub fn nine_slice(
523 &mut self,
524 position: Vec3,
525 size: UVec2,
526 color: Color,
527 nine_slice_and_material: &NineSliceAndMaterial,
528 ) {
529 self.push_nine_slice(position, size, color, nine_slice_and_material);
530 }
531
532 pub fn draw_quad(&mut self, position: Vec3, size: UVec2, color: Color) {
533 let material = Material {
534 base: MaterialBase {},
535 kind: MaterialKind::Quad,
536 };
537
538 self.items.push(RenderItem {
539 position,
540 material_ref: MaterialRef::from(material),
541 renderable: Renderable::QuadColor(QuadColor { size, color }),
542 });
543 }
544
545 #[allow(clippy::too_many_arguments)]
546 pub fn draw_nine_slice(
547 &mut self,
548 position: Vec3,
549 size: UVec2,
550 slices: Slices,
551 material_ref: &MaterialRef,
552 color: Color,
553 ) {
554 self.items.push(RenderItem {
555 position,
556 material_ref: material_ref.clone(),
557 renderable: Renderable::NineSlice(NineSlice {
558 size,
559 slices,
560 color,
561 origin_in_atlas: UVec2::new(0, 0),
562 size_inside_atlas: None,
563 }),
564 });
565 }
566
567 pub const fn clear_color(&self) -> wgpu::Color {
568 self.clear_color
569 }
570
571 fn calculate_texture_coords_mul_add(atlas_rect: URect, texture_size: UVec2) -> Vec4 {
573 let x = atlas_rect.position.x as f32 / texture_size.x as f32;
574 let y = atlas_rect.position.y as f32 / texture_size.y as f32;
575 let width = atlas_rect.size.x as f32 / texture_size.x as f32;
576 let height = atlas_rect.size.y as f32 / texture_size.y as f32;
577 Vec4([width, height, x, y])
578 }
579
580 fn order_render_items_in_batches(&mut self) -> Vec<Vec<&RenderItem>> {
581 let mut material_batches: Vec<Vec<&RenderItem>> = Vec::new();
582 let mut current_batch: Vec<&RenderItem> = Vec::new();
583 let mut current_material: Option<MaterialRef> = None;
584
585 for render_item in &self.items {
586 if Some(&render_item.material_ref) != current_material.as_ref() {
587 if !current_batch.is_empty() {
588 material_batches.push(current_batch.clone());
589 current_batch.clear();
590 }
591 current_material = Some(render_item.material_ref.clone());
592 }
593 current_batch.push(render_item);
594 }
595
596 if !current_batch.is_empty() {
597 material_batches.push(current_batch);
598 }
599
600 material_batches
601 }
602
603 #[must_use]
604 pub fn quad_helper_uniform(
605 position: Vec3,
606 quad_size: UVec2,
607 render_atlas: URect,
608 color: Color,
609
610 current_texture_size: UVec2,
611 ) -> SpriteInstanceUniform {
612 let model_matrix = Matrix4::from_translation(position.x as f32, position.y as f32, 0.0)
613 * Matrix4::from_scale(quad_size.x as f32, quad_size.y as f32, 1.0);
614
615 let tex_coords_mul_add =
616 Self::calculate_texture_coords_mul_add(render_atlas, current_texture_size);
617
618 let rotation_value = 0;
619
620 SpriteInstanceUniform::new(
621 model_matrix,
622 tex_coords_mul_add,
623 rotation_value,
624 Vec4(color.to_f32_slice()),
625 )
626 }
627
628 #[allow(clippy::too_many_lines)]
631 pub fn write_vertex_indices_and_uv_to_buffer(
632 &mut self,
633 textures: &Assets<Texture>,
634 fonts: &Assets<Font>,
635 ) {
636 const FLIP_X_MASK: u32 = 0b0000_0100;
637 const FLIP_Y_MASK: u32 = 0b0000_1000;
638
639 let batches = self.sort_and_put_in_batches();
640
641 let mut quad_matrix_and_uv: Vec<SpriteInstanceUniform> = Vec::new();
642 let mut batch_vertex_ranges: Vec<(MaterialRef, u32, u32)> = Vec::new();
643
644 for render_items in batches {
645 let quad_len_before = quad_matrix_and_uv.len() as u32;
646
647 let weak_material_ref = render_items
649 .first()
650 .map(|item| {
651 let material_ref: MaterialRef = item.material_ref.clone();
653 material_ref
654 })
655 .expect("Render items batch was empty");
656
657 if !weak_material_ref.is_complete(textures) {
658 trace!(?weak_material_ref, "material is not complete yet");
660 continue;
661 }
662 let material = weak_material_ref.clone();
663
664 let maybe_texture_ref = material.primary_texture();
665 let maybe_texture = maybe_texture_ref.and_then(|found_primary_texture_ref| {
666 let found_primary_texture = textures.get(&found_primary_texture_ref);
667 found_primary_texture
668 });
669
670 for render_item in render_items {
671 match &render_item.renderable {
672 Renderable::Sprite(sprite) => {
673 let current_texture_size = maybe_texture.unwrap().texture_size;
674
675 let params = &sprite.params;
676 let mut size = params.texture_size;
677 if size.x == 0 && size.y == 0 {
678 size = current_texture_size;
679 }
680
681 let render_atlas = URect {
682 position: params.texture_pos,
683 size,
684 };
685
686 match params.rotation {
687 Rotation::Degrees90 | Rotation::Degrees270 => {
688 swap(&mut size.x, &mut size.y);
689 }
690 _ => {}
691 }
692
693 let model_matrix = Matrix4::from_translation(
694 render_item.position.x as f32,
695 render_item.position.y as f32,
696 0.0,
697 ) * Matrix4::from_scale(
698 (size.x * params.scale as u16) as f32,
699 (size.y * params.scale as u16) as f32,
700 1.0,
701 );
702
703 let tex_coords_mul_add = Self::calculate_texture_coords_mul_add(
704 render_atlas,
705 current_texture_size,
706 );
707
708 let mut rotation_value = match params.rotation {
709 Rotation::Degrees0 => 0,
710 Rotation::Degrees90 => 1,
711 Rotation::Degrees180 => 2,
712 Rotation::Degrees270 => 3,
713 };
714
715 if params.flip_x {
716 rotation_value |= FLIP_X_MASK;
717 }
718 if params.flip_y {
719 rotation_value |= FLIP_Y_MASK;
720 }
721
722 let quad_instance = SpriteInstanceUniform::new(
723 model_matrix,
724 tex_coords_mul_add,
725 rotation_value,
726 Vec4(params.color.to_f32_slice()),
727 );
728 quad_matrix_and_uv.push(quad_instance);
729 }
730
731 Renderable::Mask(texture_offset, color) => {
732 let current_texture_size = maybe_texture.unwrap().texture_size;
733 let params = SpriteParams {
734 texture_size: current_texture_size,
735 texture_pos: *texture_offset,
736 scale: 1,
737 rotation: Rotation::default(),
738 flip_x: false,
739 flip_y: false,
740 pivot: Vec2 { x: 0, y: 0 },
741 color: *color,
742 };
743
744 let mut size = params.texture_size;
745 if size.x == 0 && size.y == 0 {
746 size = current_texture_size;
747 }
748
749 let render_atlas = URect {
750 position: params.texture_pos,
751 size,
752 };
753
754 let model_matrix = Matrix4::from_translation(
755 render_item.position.x as f32,
756 render_item.position.y as f32,
757 0.0,
758 ) * Matrix4::from_scale(
759 (size.x * params.scale as u16) as f32,
760 (size.y * params.scale as u16) as f32,
761 1.0,
762 );
763
764 let tex_coords_mul_add = Self::calculate_texture_coords_mul_add(
765 render_atlas,
766 current_texture_size,
767 );
768
769 let mut rotation_value = match params.rotation {
770 Rotation::Degrees0 => 0,
771 Rotation::Degrees90 => 1,
772 Rotation::Degrees180 => 2,
773 Rotation::Degrees270 => 3,
774 };
775
776 if params.flip_x {
777 rotation_value |= FLIP_X_MASK;
778 }
779 if params.flip_y {
780 rotation_value |= FLIP_Y_MASK;
781 }
782
783 let quad_instance = SpriteInstanceUniform::new(
784 model_matrix,
785 tex_coords_mul_add,
786 rotation_value,
787 Vec4(params.color.to_f32_slice()),
788 );
789 quad_matrix_and_uv.push(quad_instance);
790 }
791
792 Renderable::NineSlice(nine_slice) => {
793 let current_texture_size = maybe_texture.unwrap().texture_size;
794 Self::prepare_nine_slice(
795 nine_slice,
796 render_item.position,
797 &mut quad_matrix_and_uv,
798 current_texture_size,
799 );
800 }
801
802 Renderable::QuadColor(quad) => {
803 let model_matrix =
804 Matrix4::from_translation(
805 render_item.position.x as f32,
806 render_item.position.y as f32,
807 0.0,
808 ) * Matrix4::from_scale(quad.size.x as f32, quad.size.y as f32, 1.0);
809
810 let tex_coords_mul_add = Vec4([
811 0.0, 0.0, 0.0, 0.0,
814 ]);
815 let rotation_value = 0;
816
817 let quad_instance = SpriteInstanceUniform::new(
818 model_matrix,
819 tex_coords_mul_add,
820 rotation_value,
821 Vec4(quad.color.to_f32_slice()),
822 );
823 quad_matrix_and_uv.push(quad_instance);
824 }
825
826 Renderable::Text(text) => {
827 let current_texture_size = maybe_texture.unwrap().texture_size;
828 let result = fonts.get_weak(text.font_ref);
829 if result.is_none() {
830 continue;
831 }
832 let font = result.unwrap();
833
834 let glyphs = font.draw(&text.text);
835 for glyph in glyphs {
836 let pos = render_item.position + Vec3::from(glyph.relative_position);
837 let texture_size = glyph.texture_rectangle.size;
838 let model_matrix =
839 Matrix4::from_translation(pos.x as f32, pos.y as f32, 0.0)
840 * Matrix4::from_scale(
841 texture_size.x as f32,
842 texture_size.y as f32,
843 1.0,
844 );
845 let tex_coords_mul_add = Self::calculate_texture_coords_mul_add(
846 glyph.texture_rectangle,
847 current_texture_size,
848 );
849
850 let quad_instance = SpriteInstanceUniform::new(
851 model_matrix,
852 tex_coords_mul_add,
853 0,
854 Vec4(text.color.to_f32_slice()),
855 );
856 quad_matrix_and_uv.push(quad_instance);
857 }
858 }
859
860 Renderable::TileMap(tile_map) => {
861 for (index, tile) in tile_map.tiles.iter().enumerate() {
862 let cell_pos_x = (index as u16 % tile_map.tiles_data_grid_size.x)
863 * tile_map.one_cell_size.x
864 * tile_map.scale as u16;
865 let cell_pos_y = (index as u16 / tile_map.tiles_data_grid_size.x)
866 * tile_map.one_cell_size.y
867 * tile_map.scale as u16;
868 let cell_x = *tile % tile_map.cell_count_size.x;
869 let cell_y = *tile / tile_map.cell_count_size.x;
870
871 let tex_x = cell_x * tile_map.one_cell_size.x;
872 let tex_y = cell_y * tile_map.one_cell_size.x;
873
874 let cell_texture_area = URect::new(
875 tex_x,
876 tex_y,
877 tile_map.one_cell_size.x,
878 tile_map.one_cell_size.y,
879 );
880
881 let cell_model_matrix = Matrix4::from_translation(
882 (render_item.position.x + cell_pos_x as i16) as f32,
883 (render_item.position.y + cell_pos_y as i16) as f32,
884 0.0,
885 ) * Matrix4::from_scale(
886 (tile_map.one_cell_size.x * tile_map.scale as u16) as f32,
887 (tile_map.one_cell_size.y * tile_map.scale as u16) as f32,
888 1.0,
889 );
890
891 let current_texture_size = maybe_texture.unwrap().texture_size;
892 let cell_tex_coords_mul_add = Self::calculate_texture_coords_mul_add(
893 cell_texture_area,
894 current_texture_size,
895 );
896
897 let quad_instance = SpriteInstanceUniform::new(
898 cell_model_matrix,
899 cell_tex_coords_mul_add,
900 0,
901 Vec4([1.0, 1.0, 1.0, 1.0]),
902 );
903 quad_matrix_and_uv.push(quad_instance);
904 }
905 }
906 }
907 }
908
909 let quad_count = quad_matrix_and_uv.len() as u32 - quad_len_before;
910 batch_vertex_ranges.push((weak_material_ref, quad_len_before, quad_count));
911 }
912
913 self.queue.write_buffer(
915 &self.quad_matrix_and_uv_instance_buffer,
916 0,
917 bytemuck::cast_slice(&quad_matrix_and_uv),
918 );
919
920 self.batch_offsets = batch_vertex_ranges;
921 }
922
923 #[allow(clippy::too_many_lines)]
924 #[inline]
925 pub fn prepare_nine_slice(
926 nine_slice: &NineSlice,
927 position_offset: Vec3,
928 quad_matrix_and_uv: &mut Vec<SpriteInstanceUniform>,
929 current_texture_size: UVec2,
930 ) {
931 let color = nine_slice.color;
932 let world_window_size = nine_slice.size;
933 let slices = &nine_slice.slices;
934 let atlas_origin = nine_slice.origin_in_atlas;
935 let texture_window_size = nine_slice.size_inside_atlas.unwrap_or(current_texture_size);
936
937 let world_edge_width = nine_slice.size.x - slices.left - slices.right;
938 let world_edge_height = nine_slice.size.y - slices.top - slices.bottom;
939 let texture_edge_width = texture_window_size.x - slices.left - slices.right;
940 let texture_edge_height = texture_window_size.y - slices.top - slices.bottom;
941
942 let lower_left_pos = Vec3::new(position_offset.x, position_offset.y, 0);
945 let corner_size = UVec2::new(slices.left, slices.bottom);
946 let lower_left_quad_size = UVec2::new(corner_size.x, corner_size.y);
948 let lower_left_atlas = URect::new(
949 atlas_origin.x,
950 atlas_origin.y + texture_window_size.y - slices.bottom, corner_size.x,
952 corner_size.y,
953 );
954 let lower_left_quad = Self::quad_helper_uniform(
955 lower_left_pos,
956 lower_left_quad_size,
957 lower_left_atlas,
958 color,
959 current_texture_size,
960 );
961 quad_matrix_and_uv.push(lower_left_quad);
962
963 let lower_side_position =
965 Vec3::new(position_offset.x + slices.left as i16, position_offset.y, 0);
966 let lower_side_world_quad_size = UVec2::new(world_edge_width, slices.bottom);
969 let lower_side_texture_size = UVec2::new(texture_edge_width, slices.bottom);
970 let lower_side_atlas = URect::new(
972 atlas_origin.x + slices.left,
973 atlas_origin.y + texture_window_size.y - slices.bottom, lower_side_texture_size.x,
975 lower_side_texture_size.y,
976 );
977 let lower_side_quad = Self::quad_helper_uniform(
978 lower_side_position,
979 lower_side_world_quad_size,
980 lower_side_atlas,
981 color,
982 current_texture_size,
983 );
984 quad_matrix_and_uv.push(lower_side_quad);
985
986 let lower_right_pos = Vec3::new(
988 position_offset.x + (world_window_size.x - slices.right) as i16,
989 position_offset.y,
990 0,
991 );
992 let lower_right_corner_size = UVec2::new(slices.right, slices.bottom);
993 let lower_right_atlas = URect::new(
994 atlas_origin.x + texture_window_size.x - slices.right,
995 atlas_origin.y + texture_window_size.y - slices.bottom, lower_right_corner_size.x,
997 lower_right_corner_size.y,
998 );
999 let lower_right_quad = Self::quad_helper_uniform(
1000 lower_right_pos,
1001 lower_right_corner_size,
1002 lower_right_atlas,
1003 color,
1004 current_texture_size,
1005 );
1006 quad_matrix_and_uv.push(lower_right_quad);
1007
1008 let left_edge_pos = Vec3::new(
1010 position_offset.x,
1011 position_offset.y + slices.bottom as i16,
1012 0,
1013 );
1014 let left_edge_world_quad_size = UVec2::new(slices.left, world_edge_height);
1015 let left_edge_texture_size = UVec2::new(slices.left, texture_edge_height);
1016 let left_edge_atlas = URect::new(
1017 atlas_origin.x,
1018 atlas_origin.y + slices.top, left_edge_texture_size.x,
1020 left_edge_texture_size.y,
1021 );
1022 let left_edge_quad = Self::quad_helper_uniform(
1023 left_edge_pos,
1024 left_edge_world_quad_size,
1025 left_edge_atlas,
1026 color,
1027 current_texture_size,
1028 );
1029 quad_matrix_and_uv.push(left_edge_quad);
1030
1031 let base_center_x = atlas_origin.x + slices.left;
1037 let base_center_y = atlas_origin.y + slices.top;
1038
1039 let repeat_x_count = (world_edge_width as f32 / texture_edge_width as f32).ceil() as usize;
1041 let repeat_y_count =
1042 (world_edge_height as f32 / texture_edge_height as f32).ceil() as usize;
1043
1044 for y in 0..repeat_y_count {
1045 for x in 0..repeat_x_count {
1046 let this_quad_width =
1047 if x == repeat_x_count - 1 && world_edge_width % texture_edge_width != 0 {
1048 world_edge_width % texture_edge_width
1049 } else {
1050 texture_edge_width
1051 };
1052
1053 let this_quad_height =
1054 if y == repeat_y_count - 1 && world_edge_height % texture_edge_height != 0 {
1055 world_edge_height % texture_edge_height
1056 } else {
1057 texture_edge_height
1058 };
1059
1060 let quad_pos = Vec3::new(
1061 position_offset.x + slices.left as i16 + (x as u16 * texture_edge_width) as i16,
1062 position_offset.y
1063 + slices.bottom as i16
1064 + (y as u16 * texture_edge_height) as i16,
1065 0,
1066 );
1067
1068 let texture_x = base_center_x;
1069
1070 let texture_y = if y == repeat_y_count - 1 && this_quad_height < texture_edge_height
1071 {
1072 base_center_y + (texture_edge_height - this_quad_height)
1073 } else {
1074 base_center_y
1075 };
1076
1077 let this_texture_region =
1078 URect::new(texture_x, texture_y, this_quad_width, this_quad_height);
1079
1080 let center_quad = Self::quad_helper_uniform(
1081 quad_pos,
1082 UVec2::new(this_quad_width, this_quad_height),
1083 this_texture_region,
1084 color,
1085 current_texture_size,
1086 );
1087
1088 quad_matrix_and_uv.push(center_quad);
1089 }
1090 }
1091 let right_edge_pos = Vec3::new(
1095 position_offset.x + (world_window_size.x - slices.right) as i16,
1096 position_offset.y + slices.bottom as i16,
1097 0,
1098 );
1099 let right_edge_world_quad_size = UVec2::new(slices.right, world_edge_height);
1100 let right_edge_texture_size = UVec2::new(slices.right, texture_edge_height);
1101 let right_edge_atlas = URect::new(
1102 atlas_origin.x + texture_window_size.x - slices.right,
1103 atlas_origin.y + slices.top, right_edge_texture_size.x,
1105 right_edge_texture_size.y,
1106 );
1107
1108 let right_edge_quad = Self::quad_helper_uniform(
1109 right_edge_pos,
1110 right_edge_world_quad_size,
1111 right_edge_atlas,
1112 color,
1113 current_texture_size,
1114 );
1115 quad_matrix_and_uv.push(right_edge_quad);
1116
1117 let top_left_pos = Vec3::new(
1119 position_offset.x,
1120 position_offset.y + (world_window_size.y - slices.top) as i16,
1121 0,
1122 );
1123 let top_left_corner_size = UVec2::new(slices.left, slices.top);
1124 let top_left_atlas = URect::new(
1125 atlas_origin.x,
1126 atlas_origin.y, top_left_corner_size.x,
1128 top_left_corner_size.y,
1129 );
1130 let top_left_quad = Self::quad_helper_uniform(
1131 top_left_pos,
1132 top_left_corner_size,
1133 top_left_atlas,
1134 color,
1135 current_texture_size,
1136 );
1137 quad_matrix_and_uv.push(top_left_quad);
1138
1139 let top_edge_pos = Vec3::new(
1141 position_offset.x + slices.left as i16,
1142 position_offset.y + (world_window_size.y - slices.top) as i16,
1143 0,
1144 );
1145 let top_edge_world_quad_size = UVec2::new(world_edge_width, slices.top);
1146 let top_edge_texture_size = UVec2::new(texture_edge_width, slices.top);
1147 let top_edge_atlas = URect::new(
1148 atlas_origin.x + slices.left,
1149 atlas_origin.y, top_edge_texture_size.x,
1151 top_edge_texture_size.y,
1152 );
1153 let top_edge_quad = Self::quad_helper_uniform(
1154 top_edge_pos,
1155 top_edge_world_quad_size,
1156 top_edge_atlas,
1157 color,
1158 current_texture_size,
1159 );
1160 quad_matrix_and_uv.push(top_edge_quad);
1161
1162 let top_right_pos = Vec3::new(
1164 position_offset.x + (world_window_size.x - slices.right) as i16,
1165 position_offset.y + (world_window_size.y - slices.top) as i16,
1166 0,
1167 );
1168 let top_right_corner_size = UVec2::new(slices.right, slices.top);
1169 let top_right_atlas = URect::new(
1170 atlas_origin.x + texture_window_size.x - slices.right,
1171 atlas_origin.y, top_right_corner_size.x,
1173 top_right_corner_size.y,
1174 );
1175 let top_right_quad = Self::quad_helper_uniform(
1176 top_right_pos,
1177 top_right_corner_size,
1178 top_right_atlas,
1179 color,
1180 current_texture_size,
1181 );
1182 quad_matrix_and_uv.push(top_right_quad);
1183 }
1184
1185 fn sort_and_put_in_batches(&mut self) -> Vec<Vec<&RenderItem>> {
1186 sort_render_items_by_z_and_material(&mut self.items);
1187
1188 self.order_render_items_in_batches()
1189 }
1190
1191 #[allow(clippy::too_many_lines)]
1194 pub fn render(
1195 &mut self,
1196 command_encoder: &mut CommandEncoder,
1197 display_surface_texture_view: &TextureView,
1198 textures: &Assets<Texture>,
1200 fonts: &Assets<Font>,
1201 now: Millis,
1202 ) {
1203 self.debug_tick += 1;
1204 trace!("start render()");
1205 self.last_render_at = now;
1206
1207 self.set_viewport_and_view_projection_matrix();
1208
1209 self.write_vertex_indices_and_uv_to_buffer(textures, fonts);
1210
1211 self.render_batches_to_virtual_texture(command_encoder, textures);
1212
1213 self.render_virtual_texture_to_display(command_encoder, display_surface_texture_view);
1214 }
1215
1216 pub fn set_viewport_and_view_projection_matrix(&mut self) {
1217 let view_proj_matrix = create_view_projection_matrix_from_virtual(
1218 self.virtual_surface_size.x,
1219 self.virtual_surface_size.y,
1220 );
1221
1222 let scale_matrix = Matrix4::from_scale(self.scale, self.scale, 0.0);
1223 let origin_translation_matrix =
1224 Matrix4::from_translation(-self.origin.x as f32, -self.origin.y as f32, 0.0);
1225
1226 let total_matrix = scale_matrix * view_proj_matrix * origin_translation_matrix;
1227
1228 self.queue.write_buffer(
1230 &self.camera_buffer,
1231 0,
1232 bytemuck::cast_slice(&[total_matrix]),
1233 );
1234 }
1235
1236 pub fn render_batches_to_virtual_texture(
1237 &mut self,
1238 command_encoder: &mut CommandEncoder,
1239 textures: &Assets<Texture>,
1240 ) {
1241 let mut render_pass = command_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1242 label: Some("Game Render Pass"),
1243 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1244 view: &self.virtual_surface_texture_view,
1245 resolve_target: None,
1246 ops: wgpu::Operations {
1247 load: wgpu::LoadOp::Clear(self.clear_color),
1248 store: wgpu::StoreOp::Store,
1249 },
1250 })],
1251 depth_stencil_attachment: None,
1252 timestamp_writes: None,
1253 occlusion_query_set: None,
1254 });
1255
1256 render_pass.set_viewport(
1257 0.0,
1258 0.0,
1259 self.virtual_surface_size.x as f32,
1260 self.virtual_surface_size.y as f32,
1261 0.0,
1262 1.0,
1263 );
1264
1265 render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
1267 render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
1268
1269 render_pass.set_vertex_buffer(1, self.quad_matrix_and_uv_instance_buffer.slice(..));
1271
1272 let num_indices = mireforge_wgpu_sprites::INDICES.len() as u32;
1273
1274 let mut current_pipeline: Option<&MaterialKind> = None;
1275
1276 for &(ref weak_material_ref, start, count) in &self.batch_offsets {
1277 let wgpu_material = weak_material_ref;
1278
1279 let pipeline_kind = &wgpu_material.kind;
1280
1281 if current_pipeline != Some(pipeline_kind) {
1282 let pipeline = match pipeline_kind {
1283 MaterialKind::NormalSprite { .. } => &self.normal_sprite_pipeline.pipeline,
1284 MaterialKind::Quad => &self.quad_shader_info.pipeline,
1285 MaterialKind::AlphaMasker { .. } => &self.mask_shader_info.pipeline,
1286 MaterialKind::LightAdd { .. } => &self.light_shader_info.pipeline,
1287 };
1288 render_pass.set_pipeline(pipeline);
1290 current_pipeline = Some(pipeline_kind);
1293 render_pass.set_bind_group(0, &self.camera_bind_group, &[]);
1294 }
1295
1296 match &wgpu_material.kind {
1297 MaterialKind::NormalSprite { primary_texture }
1298 | MaterialKind::LightAdd { primary_texture } => {
1299 let texture = textures.get(primary_texture).unwrap();
1300 render_pass.set_bind_group(1, &texture.texture_and_sampler_bind_group, &[]);
1302 }
1303 MaterialKind::AlphaMasker {
1304 primary_texture,
1305 alpha_texture,
1306 } => {
1307 let real_diffuse_texture = textures.get(primary_texture).unwrap();
1308 let alpha_texture = textures.get(alpha_texture).unwrap();
1309 render_pass.set_bind_group(
1310 1,
1311 &real_diffuse_texture.texture_and_sampler_bind_group,
1312 &[],
1313 );
1314 render_pass.set_bind_group(
1315 2,
1316 &alpha_texture.texture_and_sampler_bind_group,
1317 &[],
1318 );
1319 }
1320 MaterialKind::Quad => {
1321 }
1323 }
1324
1325 trace!(material=%weak_material_ref, start=%start, count=%count, %num_indices, "draw instanced");
1327 render_pass.draw_indexed(0..num_indices, 0, start..(start + count));
1328 }
1329 self.items.clear();
1330 }
1331
1332 pub fn render_virtual_texture_to_display(
1333 &mut self,
1334 command_encoder: &mut CommandEncoder,
1335 display_surface_texture_view: &TextureView,
1336 ) {
1337 let mut render_pass = command_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1338 label: Some("Screen Render Pass"),
1339 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1340 view: display_surface_texture_view,
1341 resolve_target: None,
1342 ops: wgpu::Operations {
1343 load: wgpu::LoadOp::Clear(self.screen_clear_color),
1344 store: wgpu::StoreOp::Store,
1345 },
1346 })],
1347 depth_stencil_attachment: None,
1348 timestamp_writes: None,
1349 occlusion_query_set: None,
1350 });
1351
1352 self.viewport = match self.viewport_strategy {
1364 ViewportStrategy::FitIntegerScaling => Self::viewport_from_integer_scale(
1365 self.physical_surface_size,
1366 self.virtual_surface_size,
1367 ),
1368 ViewportStrategy::FitFloatScaling => Self::viewport_from_float_scale(
1369 self.physical_surface_size,
1370 self.virtual_surface_size,
1371 ),
1372 ViewportStrategy::MatchPhysicalSize => URect::new(
1373 0,
1374 0,
1375 self.physical_surface_size.x,
1376 self.physical_surface_size.y,
1377 ),
1378 };
1379
1380 render_pass.set_viewport(
1381 self.viewport.position.x as f32,
1382 self.viewport.position.y as f32,
1383 self.viewport.size.x as f32,
1384 self.viewport.size.y as f32,
1385 0.0,
1386 1.0,
1387 );
1388
1389 render_pass.set_pipeline(&self.virtual_to_screen_shader_info.pipeline);
1391 render_pass.set_bind_group(0, &self.virtual_to_surface_bind_group, &[]);
1392 render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
1393
1394 render_pass.draw(0..6, 0..1);
1395 }
1396
1397 pub fn texture_resource_from_texture(&self, texture: &wgpu::Texture, label: &str) -> Texture {
1398 trace!("load texture from memory with name: '{label}'");
1399 let size = &texture.size();
1400 let texture_and_sampler_bind_group =
1401 mireforge_wgpu_sprites::create_sprite_texture_and_sampler_bind_group(
1402 &self.device,
1403 &self.texture_sampler_bind_group_layout,
1404 &texture,
1405 &self.sampler,
1406 label,
1407 );
1408
1409 let texture_size = UVec2::new(size.width as u16, size.height as u16);
1410
1411 Texture {
1412 texture_and_sampler_bind_group,
1413 texture_size,
1414 }
1415 }
1416}
1417
1418fn create_view_projection_matrix_from_virtual(virtual_width: u16, virtual_height: u16) -> Matrix4 {
1419 OrthoInfo {
1420 left: 0.0,
1421 right: virtual_width as f32,
1422 bottom: 0.0,
1423 top: virtual_height as f32,
1424 near: 1.0, far: -1.0, }
1427 .into()
1428}
1429
1430fn create_view_uniform_view_projection_matrix(viewport_size: UVec2) -> Matrix4 {
1431 let viewport_width = viewport_size.x as f32;
1432 let viewport_height = viewport_size.y as f32;
1433
1434 let viewport_aspect_ratio = viewport_width / viewport_height;
1435
1436 let scale_x = 1.0;
1437 let scale_y = viewport_aspect_ratio; let view_projection_matrix = [
1440 [scale_x, 0.0, 0.0, 0.0],
1441 [0.0, scale_y, 0.0, 0.0],
1442 [0.0, 0.0, -1.0, 0.0],
1443 [0.0, 0.0, 0.0, 1.0],
1444 ];
1445
1446 view_projection_matrix.into()
1447}
1448
1449fn sort_render_items_by_z_and_material(items: &mut [RenderItem]) {
1450 items.sort_by_key(|item| (item.position.z, item.material_ref.clone()));
1451}
1452
1453#[derive(Debug, Clone, Copy, Default)]
1454pub enum Rotation {
1455 #[default]
1456 Degrees0,
1457 Degrees90,
1458 Degrees180,
1459 Degrees270,
1460}
1461
1462#[derive(Debug, Copy, Clone)]
1463pub struct SpriteParams {
1464 pub texture_size: UVec2,
1465 pub texture_pos: UVec2,
1466 pub scale: u8,
1467 pub rotation: Rotation,
1468 pub flip_x: bool,
1469 pub flip_y: bool,
1470 pub pivot: Vec2,
1471 pub color: Color,
1472}
1473
1474impl Default for SpriteParams {
1475 fn default() -> Self {
1476 Self {
1477 texture_size: UVec2::new(0, 0),
1478 texture_pos: UVec2::new(0, 0),
1479 pivot: Vec2::new(0, 0),
1480 flip_x: false,
1481 flip_y: false,
1482 color: Color::from_octet(255, 255, 255, 255),
1483 scale: 1,
1484 rotation: Rotation::Degrees0,
1485 }
1486 }
1487}
1488
1489pub type BindGroupRef = Arc<BindGroup>;
1490
1491#[derive(Debug, PartialEq, Eq, Asset)]
1492pub struct Texture {
1493 pub texture_and_sampler_bind_group: BindGroup,
1494 pub texture_size: UVec2,
1496}
1497
1498impl Display for Texture {
1499 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
1500 write!(f, "{:?}", self.texture_size)
1501 }
1502}
1503
1504impl PartialOrd<Self> for Texture {
1505 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1506 Some(
1507 self.texture_and_sampler_bind_group
1508 .cmp(&other.texture_and_sampler_bind_group),
1509 )
1510 }
1511}
1512
1513impl Ord for Texture {
1514 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1515 self.texture_and_sampler_bind_group
1516 .cmp(&other.texture_and_sampler_bind_group)
1517 }
1518}
1519
1520#[derive(Debug, Ord, PartialOrd, PartialEq, Eq)]
1521pub struct MaterialBase {
1522 }
1524
1525#[derive(Debug, Ord, PartialOrd, PartialEq, Eq)]
1526pub struct Material {
1527 pub base: MaterialBase,
1528 pub kind: MaterialKind,
1529}
1530
1531impl Material {
1532 #[inline]
1533 #[must_use]
1534 pub fn primary_texture(&self) -> Option<TextureRef> {
1535 self.kind.primary_texture()
1536 }
1537
1538 #[inline]
1539 #[must_use]
1540 pub fn is_complete(&self, textures: &Assets<Texture>) -> bool {
1541 self.kind.is_complete(textures)
1542 }
1543}
1544
1545impl Display for Material {
1546 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1547 write!(f, "{}", self.kind)
1548 }
1549}
1550
1551#[derive(Debug, Ord, PartialOrd, PartialEq, Eq)]
1552pub enum MaterialKind {
1553 NormalSprite {
1554 primary_texture: Id<Texture>,
1555 },
1556 AlphaMasker {
1557 primary_texture: Id<Texture>,
1558 alpha_texture: Id<Texture>,
1559 },
1560 Quad,
1561 LightAdd {
1562 primary_texture: Id<Texture>,
1563 },
1564}
1565
1566impl MaterialKind {}
1567
1568impl MaterialKind {
1569 pub fn primary_texture(&self) -> Option<Id<Texture>> {
1570 match &self {
1571 Self::NormalSprite {
1572 primary_texture, ..
1573 }
1574 | Self::LightAdd { primary_texture }
1575 | Self::AlphaMasker {
1576 primary_texture, ..
1577 } => Some(primary_texture.clone()),
1578 Self::Quad => None,
1579 }
1580 }
1581
1582 pub(crate) fn is_complete(&self, textures: &Assets<Texture>) -> bool {
1583 match &self {
1584 Self::NormalSprite { primary_texture } | Self::LightAdd { primary_texture } => {
1585 textures.contains(primary_texture)
1586 }
1587 Self::AlphaMasker {
1588 primary_texture,
1589 alpha_texture,
1590 } => textures.contains(primary_texture) && textures.contains(alpha_texture),
1591 Self::Quad => true,
1592 }
1593 }
1594}
1595
1596impl Display for MaterialKind {
1597 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1598 let texture_name = self
1599 .primary_texture()
1600 .map_or_else(String::new, |x| x.to_string());
1601
1602 let kind_name = match self {
1603 Self::NormalSprite { .. } => "NormalSprite",
1604 Self::LightAdd { .. } => "Light (Add)",
1605 Self::Quad => "Quad",
1606 Self::AlphaMasker { .. } => "AlphaMasker",
1607 };
1608
1609 write!(f, "{kind_name} texture {texture_name}")
1610 }
1611}
1612
1613#[derive(Debug)]
1614pub struct Sprite {
1615 pub params: SpriteParams,
1616}
1617
1618#[derive(Debug)]
1619pub struct QuadColor {
1620 pub size: UVec2,
1621 pub color: Color,
1622}
1623
1624#[derive(Debug, Copy, Clone)]
1625pub struct Slices {
1626 pub left: u16,
1627 pub top: u16,
1628 pub right: u16, pub bottom: u16, }
1631
1632#[derive(Debug)]
1633pub struct NineSlice {
1634 pub size: UVec2, pub slices: Slices,
1636 pub color: Color, pub origin_in_atlas: UVec2,
1638 pub size_inside_atlas: Option<UVec2>,
1639}
1640
1641#[derive(Debug)]
1642pub struct TileMap {
1643 pub tiles_data_grid_size: UVec2,
1644 pub cell_count_size: UVec2,
1645 pub one_cell_size: UVec2,
1646 pub tiles: Vec<u16>,
1647 pub scale: u8,
1648}
1649
1650#[derive(PartialEq, Debug, Eq, Ord, PartialOrd)]
1651pub struct Pipeline {
1652 name: String,
1653 render_pipeline: RenderPipeline,
1654}
1655
1656impl Display for Pipeline {
1657 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1658 write!(f, "pipeline: {}", self.name)
1659 }
1660}
1661
1662pub type PipelineRef = Arc<Pipeline>;