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