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 #[must_use] 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: u32::from(virtual_surface_size.x),
261 height: u32::from(virtual_surface_size.y),
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 #[must_use] 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 #[must_use] pub const fn physical_surface_size(&self) -> UVec2 {
308 self.physical_surface_size
309 }
310
311 #[must_use] 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,
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 = f32::from(physical_size.x) / f32::from(physical_size.y);
415 let virtual_aspect = f32::from(virtual_size.x) / f32::from(virtual_size.y);
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 f32::from(physical_size.y) / f32::from(virtual_size.y)
423 } else {
424 f32::from(physical_size.x) / f32::from(virtual_size.x)
425 };
426
427 if float_scale < 0.01 {
428 float_scale = 0.01;
429 }
430
431 let viewport_actual_size = UVec2::new(
432 (f32::from(virtual_size.x) * float_scale) as u16,
433 (f32::from(virtual_size.y) * 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 const 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 {
545 size,
546 color,
547 params: QuadParams::default(),
548 }),
549 });
550 }
551
552 pub fn draw_quad_ex(&mut self, position: Vec3, size: UVec2, color: Color, params: QuadParams) {
553 let material = Material {
554 base: MaterialBase {},
555 kind: MaterialKind::Quad,
556 };
557
558 self.items.push(RenderItem {
559 position,
560 material_ref: MaterialRef::from(material),
561 renderable: Renderable::QuadColor(QuadColor {
562 size,
563 color,
564 params,
565 }),
566 });
567 }
568
569 #[allow(clippy::too_many_arguments)]
570 pub fn draw_nine_slice(
571 &mut self,
572 position: Vec3,
573 size: UVec2,
574 slices: Slices,
575 material_ref: &MaterialRef,
576 color: Color,
577 ) {
578 self.items.push(RenderItem {
579 position,
580 material_ref: material_ref.clone(),
581 renderable: Renderable::NineSlice(NineSlice {
582 size,
583 slices,
584 color,
585 origin_in_atlas: UVec2::new(0, 0),
586 size_inside_atlas: None,
587 }),
588 });
589 }
590
591 #[must_use] pub const fn clear_color(&self) -> wgpu::Color {
592 self.clear_color
593 }
594
595 fn calculate_texture_coords_mul_add(atlas_rect: URect, texture_size: UVec2) -> Vec4 {
597 let x = f32::from(atlas_rect.position.x) / f32::from(texture_size.x);
598 let y = f32::from(atlas_rect.position.y) / f32::from(texture_size.y);
599 let width = f32::from(atlas_rect.size.x) / f32::from(texture_size.x);
600 let height = f32::from(atlas_rect.size.y) / f32::from(texture_size.y);
601 Vec4([width, height, x, y])
602 }
603
604 fn order_render_items_in_batches(&mut self) -> Vec<Vec<&RenderItem>> {
605 let mut material_batches: Vec<Vec<&RenderItem>> = Vec::new();
606 let mut current_batch: Vec<&RenderItem> = Vec::new();
607 let mut current_material: Option<MaterialRef> = None;
608
609 for render_item in &self.items {
610 if Some(&render_item.material_ref) != current_material.as_ref() {
611 if !current_batch.is_empty() {
612 material_batches.push(current_batch.clone());
613 current_batch.clear();
614 }
615 current_material = Some(render_item.material_ref.clone());
616 }
617 current_batch.push(render_item);
618 }
619
620 if !current_batch.is_empty() {
621 material_batches.push(current_batch);
622 }
623
624 material_batches
625 }
626
627 #[must_use]
628 pub fn quad_helper_uniform(
629 position: Vec3,
630 quad_size: UVec2,
631 render_atlas: URect,
632 color: Color,
633
634 current_texture_size: UVec2,
635 ) -> SpriteInstanceUniform {
636 let model_matrix = Matrix4::from_translation(f32::from(position.x), f32::from(position.y), 0.0)
637 * Matrix4::from_scale(f32::from(quad_size.x), f32::from(quad_size.y), 1.0);
638
639 let tex_coords_mul_add =
640 Self::calculate_texture_coords_mul_add(render_atlas, current_texture_size);
641
642 let rotation_value = 0;
643
644 SpriteInstanceUniform::new(
645 model_matrix,
646 tex_coords_mul_add,
647 rotation_value,
648 Vec4(color.to_f32_slice()),
649 )
650 }
651
652 #[allow(clippy::too_many_lines)]
655 pub fn write_vertex_indices_and_uv_to_buffer(
656 &mut self,
657 textures: &Assets<Texture>,
658 fonts: &Assets<Font>,
659 ) {
660 const FLIP_X_MASK: u32 = 0b0000_0100;
661 const FLIP_Y_MASK: u32 = 0b0000_1000;
662
663 let batches = self.sort_and_put_in_batches();
664
665 let mut quad_matrix_and_uv: Vec<SpriteInstanceUniform> = Vec::new();
666 let mut batch_vertex_ranges: Vec<(MaterialRef, u32, u32)> = Vec::new();
667
668 for render_items in batches {
669 let quad_len_before = quad_matrix_and_uv.len();
670
671 let weak_material_ref = render_items
673 .first()
674 .map(|item| {
675 let material_ref: MaterialRef = item.material_ref.clone();
677 material_ref
678 })
679 .expect("Render items batch was empty");
680
681 if !weak_material_ref.is_complete(textures) {
682 trace!(?weak_material_ref, "material is not complete yet");
684 continue;
685 }
686 let material = weak_material_ref.clone();
687
688 let maybe_texture_ref = material.primary_texture();
689 let maybe_texture = maybe_texture_ref
690 .and_then(|found_primary_texture_ref| textures.get(&found_primary_texture_ref));
691
692 for render_item in render_items {
693 let quad_len_before_inner = quad_matrix_and_uv.len();
694
695 match &render_item.renderable {
696 Renderable::Sprite(sprite) => {
697 let current_texture_size = maybe_texture.unwrap().texture_size;
698
699 let params = &sprite.params;
700 let mut size = params.texture_size;
701 if size.x == 0 && size.y == 0 {
702 size = current_texture_size;
703 }
704
705 let render_atlas = URect {
706 position: params.texture_pos,
707 size,
708 };
709
710 match params.rotation {
711 Rotation::Degrees90 | Rotation::Degrees270 => {
712 swap(&mut size.x, &mut size.y);
713 }
714 _ => {}
715 }
716
717 let y_offset = if params.anchor == Anchor::UpperLeft {
718 current_texture_size.y as i16
719 } else {
720 0
721 };
722
723 let model_matrix = Matrix4::from_translation(
724 f32::from(render_item.position.x),
725 f32::from(render_item.position.y - y_offset),
726 0.0,
727 ) * Matrix4::from_scale(
728 f32::from(size.x * u16::from(params.scale)),
729 f32::from(size.y * u16::from(params.scale)),
730 1.0,
731 );
732
733 let tex_coords_mul_add = Self::calculate_texture_coords_mul_add(
734 render_atlas,
735 current_texture_size,
736 );
737
738 let mut rotation_value = match params.rotation {
739 Rotation::Degrees0 => 0,
740 Rotation::Degrees90 => 1,
741 Rotation::Degrees180 => 2,
742 Rotation::Degrees270 => 3,
743 };
744
745 if params.flip_x {
746 rotation_value |= FLIP_X_MASK;
747 }
748 if params.flip_y {
749 rotation_value |= FLIP_Y_MASK;
750 }
751
752 let quad_instance = SpriteInstanceUniform::new(
753 model_matrix,
754 tex_coords_mul_add,
755 rotation_value,
756 Vec4(params.color.to_f32_slice()),
757 );
758 quad_matrix_and_uv.push(quad_instance);
759 }
760
761 Renderable::Mask(texture_offset, color) => {
762 let current_texture_size = maybe_texture.unwrap().texture_size;
763 let params = SpriteParams {
764 texture_size: current_texture_size,
765 texture_pos: *texture_offset,
766 scale: 1,
767 rotation: Rotation::default(),
768 flip_x: false,
769 flip_y: false,
770 pivot: Vec2 { x: 0, y: 0 },
771 color: *color,
772 anchor: Anchor::LowerLeft,
773 };
774
775 let mut size = params.texture_size;
776 if size.x == 0 && size.y == 0 {
777 size = current_texture_size;
778 }
779
780 let render_atlas = URect {
781 position: params.texture_pos,
782 size,
783 };
784
785 let model_matrix = Matrix4::from_translation(
786 f32::from(render_item.position.x),
787 f32::from(render_item.position.y),
788 0.0,
789 ) * Matrix4::from_scale(
790 f32::from(size.x * u16::from(params.scale)),
791 f32::from(size.y * u16::from(params.scale)),
792 1.0,
793 );
794
795 let tex_coords_mul_add = Self::calculate_texture_coords_mul_add(
796 render_atlas,
797 current_texture_size,
798 );
799
800 let mut rotation_value = match params.rotation {
801 Rotation::Degrees0 => 0,
802 Rotation::Degrees90 => 1,
803 Rotation::Degrees180 => 2,
804 Rotation::Degrees270 => 3,
805 };
806
807 if params.flip_x {
808 rotation_value |= FLIP_X_MASK;
809 }
810 if params.flip_y {
811 rotation_value |= FLIP_Y_MASK;
812 }
813
814 let quad_instance = SpriteInstanceUniform::new(
815 model_matrix,
816 tex_coords_mul_add,
817 rotation_value,
818 Vec4(params.color.to_f32_slice()),
819 );
820 quad_matrix_and_uv.push(quad_instance);
821 }
822
823 Renderable::NineSlice(nine_slice) => {
824 let current_texture_size = maybe_texture.unwrap().texture_size;
825 Self::prepare_nine_slice(
826 nine_slice,
827 render_item.position,
828 &mut quad_matrix_and_uv,
829 current_texture_size,
830 );
831 }
832
833 Renderable::QuadColor(quad) => {
834 let model_matrix =
835 Matrix4::from_translation(
836 f32::from(render_item.position.x) - f32::from(quad.params.pivot.x),
837 f32::from(render_item.position.y) - f32::from(quad.params.pivot.y),
838 0.0,
839 ) * Matrix4::from_scale(f32::from(quad.size.x), f32::from(quad.size.y), 1.0);
840
841 let tex_coords_mul_add = Vec4([
842 0.0, 0.0, 0.0, 0.0,
845 ]);
846 let rotation_value = 0;
847
848 let quad_instance = SpriteInstanceUniform::new(
849 model_matrix,
850 tex_coords_mul_add,
851 rotation_value,
852 Vec4(quad.color.to_f32_slice()),
853 );
854 quad_matrix_and_uv.push(quad_instance);
855 }
856
857 Renderable::Text(text) => {
858 let current_texture_size = maybe_texture.unwrap().texture_size;
859 let result = fonts.get_weak(text.font_ref);
860 if result.is_none() {
861 continue;
862 }
863 let font = result.unwrap();
864
865 let glyph_draw = font.draw(&text.text);
866 for glyph in glyph_draw.glyphs {
867 let pos = render_item.position + Vec3::from(glyph.relative_position);
868 let texture_size = glyph.texture_rectangle.size;
869 let model_matrix =
870 Matrix4::from_translation(f32::from(pos.x), f32::from(pos.y), 0.0)
871 * Matrix4::from_scale(
872 f32::from(texture_size.x),
873 f32::from(texture_size.y),
874 1.0,
875 );
876 let tex_coords_mul_add = Self::calculate_texture_coords_mul_add(
877 glyph.texture_rectangle,
878 current_texture_size,
879 );
880
881 let quad_instance = SpriteInstanceUniform::new(
882 model_matrix,
883 tex_coords_mul_add,
884 0,
885 Vec4(text.color.to_f32_slice()),
886 );
887 quad_matrix_and_uv.push(quad_instance);
888 }
889 }
890
891 Renderable::TileMap(tile_map) => {
892 for (index, tile) in tile_map.tiles.iter().enumerate() {
893 let cell_pos_x = (index as u16 % tile_map.tiles_data_grid_size.x)
894 * tile_map.one_cell_size.x
895 * u16::from(tile_map.scale);
896 let cell_pos_y = (index as u16 / tile_map.tiles_data_grid_size.x)
897 * tile_map.one_cell_size.y
898 * u16::from(tile_map.scale);
899 let cell_x = *tile % tile_map.cell_count_size.x;
900 let cell_y = *tile / tile_map.cell_count_size.x;
901
902 let tex_x = cell_x * tile_map.one_cell_size.x;
903 let tex_y = cell_y * tile_map.one_cell_size.x;
904
905 let cell_texture_area = URect::new(
906 tex_x,
907 tex_y,
908 tile_map.one_cell_size.x,
909 tile_map.one_cell_size.y,
910 );
911
912 let cell_model_matrix = Matrix4::from_translation(
913 f32::from(render_item.position.x + cell_pos_x as i16),
914 f32::from(render_item.position.y + cell_pos_y as i16),
915 0.0,
916 ) * Matrix4::from_scale(
917 f32::from(tile_map.one_cell_size.x * u16::from(tile_map.scale)),
918 f32::from(tile_map.one_cell_size.y * u16::from(tile_map.scale)),
919 1.0,
920 );
921
922 let current_texture_size = maybe_texture.unwrap().texture_size;
923 let cell_tex_coords_mul_add = Self::calculate_texture_coords_mul_add(
924 cell_texture_area,
925 current_texture_size,
926 );
927
928 let quad_instance = SpriteInstanceUniform::new(
929 cell_model_matrix,
930 cell_tex_coords_mul_add,
931 0,
932 Vec4([1.0, 1.0, 1.0, 1.0]),
933 );
934 quad_matrix_and_uv.push(quad_instance);
935 }
936 }
937 }
938
939 let quad_count_for_this_render_item =
940 quad_matrix_and_uv.len() - quad_len_before_inner;
941 assert!(
942 quad_count_for_this_render_item <= MAXIMUM_QUADS_FOR_RENDER_ITEM,
943 "too many quads {quad_count_for_this_render_item} for render item {render_item:?}"
944 );
945 }
946
947 let quad_count_for_this_batch = quad_matrix_and_uv.len() - quad_len_before;
948 assert!(
949 quad_count_for_this_batch <= MAXIMUM_QUADS_IN_A_BATCH,
950 "too many quads {quad_count_for_this_batch} total to render in this batch"
951 );
952
953 assert!(
954 quad_matrix_and_uv.len() <= MAXIMUM_QUADS_IN_ONE_RENDER,
955 "too many quads for whole render {}",
956 quad_matrix_and_uv.len()
957 );
958
959 batch_vertex_ranges.push((
960 weak_material_ref,
961 quad_len_before as u32,
962 quad_count_for_this_batch as u32,
963 ));
964 }
965
966 self.queue.write_buffer(
968 &self.quad_matrix_and_uv_instance_buffer,
969 0,
970 bytemuck::cast_slice(&quad_matrix_and_uv),
971 );
972
973 self.batch_offsets = batch_vertex_ranges;
974 }
975
976 #[allow(clippy::too_many_lines)]
977 #[inline]
978 pub fn prepare_nine_slice(
979 nine_slice: &NineSlice,
980 position_offset: Vec3,
981 quad_matrix_and_uv: &mut Vec<SpriteInstanceUniform>,
982 current_texture_size: UVec2,
983 ) {
984 let world_window_size = nine_slice.size;
985 let slices = &nine_slice.slices;
986
987 assert!(
992 world_window_size.x >= slices.left + slices.right,
993 "NineSlice.width ({}) < slices.left + slices.right ({})",
994 world_window_size.x,
995 slices.left + slices.right
996 );
997 assert!(
998 world_window_size.y >= slices.top + slices.bottom,
999 "NineSlice.height ({}) < slices.top + slices.bottom ({})",
1000 world_window_size.y,
1001 slices.top + slices.bottom
1002 );
1003
1004 let texture_window_size = nine_slice.size_inside_atlas.unwrap_or(current_texture_size);
1005
1006 assert!(
1008 texture_window_size.x >= slices.left + slices.right,
1009 "texture_window_size.width ({}) < slices.left + slices.right ({})",
1010 texture_window_size.x,
1011 slices.left + slices.right
1012 );
1013 assert!(
1014 texture_window_size.y >= slices.top + slices.bottom,
1015 "texture_window_size.height ({}) < slices.top + slices.bottom ({})",
1016 texture_window_size.y,
1017 slices.top + slices.bottom
1018 );
1019 let color = nine_slice.color;
1022
1023 let atlas_origin = nine_slice.origin_in_atlas;
1024 let texture_window_size = nine_slice.size_inside_atlas.unwrap_or(current_texture_size);
1025
1026 let world_edge_width = nine_slice.size.x - slices.left - slices.right;
1027 let world_edge_height = nine_slice.size.y - slices.top - slices.bottom;
1028 let texture_edge_width = texture_window_size.x - slices.left - slices.right;
1029 let texture_edge_height = texture_window_size.y - slices.top - slices.bottom;
1030
1031 let lower_left_pos = Vec3::new(position_offset.x, position_offset.y, 0);
1034 let corner_size = UVec2::new(slices.left, slices.bottom);
1035 let lower_left_quad_size = UVec2::new(corner_size.x, corner_size.y);
1037 let lower_left_atlas = URect::new(
1038 atlas_origin.x,
1039 atlas_origin.y + texture_window_size.y - slices.bottom, corner_size.x,
1041 corner_size.y,
1042 );
1043 let lower_left_quad = Self::quad_helper_uniform(
1044 lower_left_pos,
1045 lower_left_quad_size,
1046 lower_left_atlas,
1047 color,
1048 current_texture_size,
1049 );
1050 quad_matrix_and_uv.push(lower_left_quad);
1051
1052 let lower_side_position =
1054 Vec3::new(position_offset.x + slices.left as i16, position_offset.y, 0);
1055 let lower_side_world_quad_size = UVec2::new(world_edge_width, slices.bottom);
1058 let lower_side_texture_size = UVec2::new(texture_edge_width, slices.bottom);
1059 let lower_side_atlas = URect::new(
1061 atlas_origin.x + slices.left,
1062 atlas_origin.y + texture_window_size.y - slices.bottom, lower_side_texture_size.x,
1064 lower_side_texture_size.y,
1065 );
1066 let lower_side_quad = Self::quad_helper_uniform(
1067 lower_side_position,
1068 lower_side_world_quad_size,
1069 lower_side_atlas,
1070 color,
1071 current_texture_size,
1072 );
1073 quad_matrix_and_uv.push(lower_side_quad);
1074
1075 let lower_right_pos = Vec3::new(
1077 position_offset.x + (world_window_size.x - slices.right) as i16,
1078 position_offset.y,
1079 0,
1080 );
1081 let lower_right_corner_size = UVec2::new(slices.right, slices.bottom);
1082 let lower_right_atlas = URect::new(
1083 atlas_origin.x + texture_window_size.x - slices.right,
1084 atlas_origin.y + texture_window_size.y - slices.bottom, lower_right_corner_size.x,
1086 lower_right_corner_size.y,
1087 );
1088 let lower_right_quad = Self::quad_helper_uniform(
1089 lower_right_pos,
1090 lower_right_corner_size,
1091 lower_right_atlas,
1092 color,
1093 current_texture_size,
1094 );
1095 quad_matrix_and_uv.push(lower_right_quad);
1096
1097 let left_edge_pos = Vec3::new(
1099 position_offset.x,
1100 position_offset.y + slices.bottom as i16,
1101 0,
1102 );
1103 let left_edge_world_quad_size = UVec2::new(slices.left, world_edge_height);
1104 let left_edge_texture_size = UVec2::new(slices.left, texture_edge_height);
1105 let left_edge_atlas = URect::new(
1106 atlas_origin.x,
1107 atlas_origin.y + slices.top, left_edge_texture_size.x,
1109 left_edge_texture_size.y,
1110 );
1111 let left_edge_quad = Self::quad_helper_uniform(
1112 left_edge_pos,
1113 left_edge_world_quad_size,
1114 left_edge_atlas,
1115 color,
1116 current_texture_size,
1117 );
1118 quad_matrix_and_uv.push(left_edge_quad);
1119
1120 let base_center_x = atlas_origin.x + slices.left;
1126 let base_center_y = atlas_origin.y + slices.top;
1127
1128 let repeat_x_count = (f32::from(world_edge_width) / f32::from(texture_edge_width)).ceil() as usize;
1130 let repeat_y_count =
1131 (f32::from(world_edge_height) / f32::from(texture_edge_height)).ceil() as usize;
1132
1133 for y in 0..repeat_y_count {
1134 for x in 0..repeat_x_count {
1135 let this_quad_width =
1136 if x == repeat_x_count - 1 && world_edge_width % texture_edge_width != 0 {
1137 world_edge_width % texture_edge_width
1138 } else {
1139 texture_edge_width
1140 };
1141
1142 let this_quad_height =
1143 if y == repeat_y_count - 1 && world_edge_height % texture_edge_height != 0 {
1144 world_edge_height % texture_edge_height
1145 } else {
1146 texture_edge_height
1147 };
1148
1149 let quad_pos = Vec3::new(
1150 position_offset.x + slices.left as i16 + (x as u16 * texture_edge_width) as i16,
1151 position_offset.y
1152 + slices.bottom as i16
1153 + (y as u16 * texture_edge_height) as i16,
1154 0,
1155 );
1156
1157 let texture_x = base_center_x;
1158
1159 let texture_y = if y == repeat_y_count - 1 && this_quad_height < texture_edge_height
1160 {
1161 base_center_y + (texture_edge_height - this_quad_height)
1162 } else {
1163 base_center_y
1164 };
1165
1166 let this_texture_region =
1167 URect::new(texture_x, texture_y, this_quad_width, this_quad_height);
1168
1169 let center_quad = Self::quad_helper_uniform(
1170 quad_pos,
1171 UVec2::new(this_quad_width, this_quad_height),
1172 this_texture_region,
1173 color,
1174 current_texture_size,
1175 );
1176
1177 quad_matrix_and_uv.push(center_quad);
1178 }
1179 }
1180 let right_edge_pos = Vec3::new(
1184 position_offset.x + (world_window_size.x - slices.right) as i16,
1185 position_offset.y + slices.bottom as i16,
1186 0,
1187 );
1188 let right_edge_world_quad_size = UVec2::new(slices.right, world_edge_height);
1189 let right_edge_texture_size = UVec2::new(slices.right, texture_edge_height);
1190 let right_edge_atlas = URect::new(
1191 atlas_origin.x + texture_window_size.x - slices.right,
1192 atlas_origin.y + slices.top, right_edge_texture_size.x,
1194 right_edge_texture_size.y,
1195 );
1196
1197 let right_edge_quad = Self::quad_helper_uniform(
1198 right_edge_pos,
1199 right_edge_world_quad_size,
1200 right_edge_atlas,
1201 color,
1202 current_texture_size,
1203 );
1204 quad_matrix_and_uv.push(right_edge_quad);
1205
1206 let top_left_pos = Vec3::new(
1208 position_offset.x,
1209 position_offset.y + (world_window_size.y - slices.top) as i16,
1210 0,
1211 );
1212 let top_left_corner_size = UVec2::new(slices.left, slices.top);
1213 let top_left_atlas = URect::new(
1214 atlas_origin.x,
1215 atlas_origin.y, top_left_corner_size.x,
1217 top_left_corner_size.y,
1218 );
1219 let top_left_quad = Self::quad_helper_uniform(
1220 top_left_pos,
1221 top_left_corner_size,
1222 top_left_atlas,
1223 color,
1224 current_texture_size,
1225 );
1226 quad_matrix_and_uv.push(top_left_quad);
1227
1228 let top_edge_pos = Vec3::new(
1230 position_offset.x + slices.left as i16,
1231 position_offset.y + (world_window_size.y - slices.top) as i16,
1232 0,
1233 );
1234 let top_edge_world_quad_size = UVec2::new(world_edge_width, slices.top);
1235 let top_edge_texture_size = UVec2::new(texture_edge_width, slices.top);
1236 let top_edge_atlas = URect::new(
1237 atlas_origin.x + slices.left,
1238 atlas_origin.y, top_edge_texture_size.x,
1240 top_edge_texture_size.y,
1241 );
1242 let top_edge_quad = Self::quad_helper_uniform(
1243 top_edge_pos,
1244 top_edge_world_quad_size,
1245 top_edge_atlas,
1246 color,
1247 current_texture_size,
1248 );
1249 quad_matrix_and_uv.push(top_edge_quad);
1250
1251 let top_right_pos = Vec3::new(
1253 position_offset.x + (world_window_size.x - slices.right) as i16,
1254 position_offset.y + (world_window_size.y - slices.top) as i16,
1255 0,
1256 );
1257 let top_right_corner_size = UVec2::new(slices.right, slices.top);
1258 let top_right_atlas = URect::new(
1259 atlas_origin.x + texture_window_size.x - slices.right,
1260 atlas_origin.y, top_right_corner_size.x,
1262 top_right_corner_size.y,
1263 );
1264 let top_right_quad = Self::quad_helper_uniform(
1265 top_right_pos,
1266 top_right_corner_size,
1267 top_right_atlas,
1268 color,
1269 current_texture_size,
1270 );
1271 quad_matrix_and_uv.push(top_right_quad);
1272 }
1273
1274 fn sort_and_put_in_batches(&mut self) -> Vec<Vec<&RenderItem>> {
1275 sort_render_items_by_z_and_material(&mut self.items);
1276
1277 self.order_render_items_in_batches()
1278 }
1279
1280 #[allow(clippy::too_many_lines)]
1283 pub fn render(
1284 &mut self,
1285 command_encoder: &mut CommandEncoder,
1286 display_surface_texture_view: &TextureView,
1287 textures: &Assets<Texture>,
1289 fonts: &Assets<Font>,
1290 now: Millis,
1291 ) {
1292 self.debug_tick += 1;
1293 trace!("start render()");
1294 self.last_render_at = now;
1295
1296 self.set_viewport_and_view_projection_matrix();
1297
1298 self.write_vertex_indices_and_uv_to_buffer(textures, fonts);
1299
1300 self.render_batches_to_virtual_texture(command_encoder, textures);
1301
1302 self.render_virtual_texture_to_display(command_encoder, display_surface_texture_view);
1303 }
1304
1305 pub fn set_viewport_and_view_projection_matrix(&mut self) {
1306 let view_proj_matrix = create_view_projection_matrix_from_virtual(
1307 self.virtual_surface_size.x,
1308 self.virtual_surface_size.y,
1309 );
1310
1311 let scale_matrix = Matrix4::from_scale(self.scale, self.scale, 0.0);
1312 let origin_translation_matrix =
1313 Matrix4::from_translation(f32::from(-self.origin.x), f32::from(-self.origin.y), 0.0);
1314
1315 let total_matrix = scale_matrix * view_proj_matrix * origin_translation_matrix;
1316
1317 self.queue.write_buffer(
1319 &self.camera_buffer,
1320 0,
1321 bytemuck::cast_slice(&[total_matrix]),
1322 );
1323 }
1324
1325 pub fn render_batches_to_virtual_texture(
1326 &mut self,
1327 command_encoder: &mut CommandEncoder,
1328 textures: &Assets<Texture>,
1329 ) {
1330 let mut render_pass = command_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1331 label: Some("Game Render Pass"),
1332 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1333 view: &self.virtual_surface_texture_view,
1334 depth_slice: None,
1335 resolve_target: None,
1336 ops: wgpu::Operations {
1337 load: wgpu::LoadOp::Clear(self.clear_color),
1338 store: wgpu::StoreOp::Store,
1339 },
1340 })],
1341 depth_stencil_attachment: None,
1342 timestamp_writes: None,
1343 occlusion_query_set: None,
1344 });
1345
1346 render_pass.set_viewport(
1347 0.0,
1348 0.0,
1349 f32::from(self.virtual_surface_size.x),
1350 f32::from(self.virtual_surface_size.y),
1351 0.0,
1352 1.0,
1353 );
1354
1355 render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
1357 render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
1358
1359 render_pass.set_vertex_buffer(1, self.quad_matrix_and_uv_instance_buffer.slice(..));
1361
1362 let num_indices = mireforge_wgpu_sprites::INDICES.len() as u32;
1363
1364 let mut current_pipeline: Option<&MaterialKind> = None;
1365
1366 for &(ref weak_material_ref, start, count) in &self.batch_offsets {
1367 let wgpu_material = weak_material_ref;
1368
1369 let pipeline_kind = &wgpu_material.kind;
1370
1371 if current_pipeline != Some(pipeline_kind) {
1372 let pipeline = match pipeline_kind {
1373 MaterialKind::NormalSprite { .. } => &self.normal_sprite_pipeline.pipeline,
1374 MaterialKind::Quad => &self.quad_shader_info.pipeline,
1375 MaterialKind::AlphaMasker { .. } => &self.mask_shader_info.pipeline,
1376 MaterialKind::LightAdd { .. } => &self.light_shader_info.pipeline,
1377 };
1378 render_pass.set_pipeline(pipeline);
1380 current_pipeline = Some(pipeline_kind);
1383 render_pass.set_bind_group(0, &self.camera_bind_group, &[]);
1384 }
1385
1386 match &wgpu_material.kind {
1387 MaterialKind::NormalSprite { primary_texture }
1388 | MaterialKind::LightAdd { primary_texture } => {
1389 let texture = textures.get(primary_texture).unwrap();
1390 render_pass.set_bind_group(1, &texture.texture_and_sampler_bind_group, &[]);
1392 }
1393 MaterialKind::AlphaMasker {
1394 primary_texture,
1395 alpha_texture,
1396 } => {
1397 let real_diffuse_texture = textures.get(primary_texture).unwrap();
1398 let alpha_texture = textures.get(alpha_texture).unwrap();
1399 render_pass.set_bind_group(
1400 1,
1401 &real_diffuse_texture.texture_and_sampler_bind_group,
1402 &[],
1403 );
1404 render_pass.set_bind_group(
1405 2,
1406 &alpha_texture.texture_and_sampler_bind_group,
1407 &[],
1408 );
1409 }
1410 MaterialKind::Quad => {
1411 }
1413 }
1414 assert!(
1415 count <= MAXIMUM_QUADS_IN_A_BATCH as u32,
1416 "too many instanced draw in a batch {count}"
1417 );
1418
1419 trace!(material=%weak_material_ref, start=%start, count=%count, %num_indices, "draw instanced");
1421 render_pass.draw_indexed(0..num_indices, 0, start..(start + count));
1422 }
1423 self.items.clear();
1424 }
1425
1426 pub fn render_virtual_texture_to_display(
1427 &mut self,
1428 command_encoder: &mut CommandEncoder,
1429 display_surface_texture_view: &TextureView,
1430 ) {
1431 let mut render_pass = command_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1432 label: Some("Screen Render Pass"),
1433 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1434 view: display_surface_texture_view,
1435 depth_slice: None,
1436 resolve_target: None,
1437 ops: wgpu::Operations {
1438 load: wgpu::LoadOp::Clear(self.screen_clear_color),
1439 store: wgpu::StoreOp::Store,
1440 },
1441 })],
1442 depth_stencil_attachment: None,
1443 timestamp_writes: None,
1444 occlusion_query_set: None,
1445 });
1446
1447 self.viewport = match self.viewport_strategy {
1459 ViewportStrategy::FitIntegerScaling => Self::viewport_from_integer_scale(
1460 self.physical_surface_size,
1461 self.virtual_surface_size,
1462 ),
1463 ViewportStrategy::FitFloatScaling => Self::viewport_from_float_scale(
1464 self.physical_surface_size,
1465 self.virtual_surface_size,
1466 ),
1467 ViewportStrategy::MatchPhysicalSize => URect::new(
1468 0,
1469 0,
1470 self.physical_surface_size.x,
1471 self.physical_surface_size.y,
1472 ),
1473 };
1474
1475 render_pass.set_viewport(
1476 f32::from(self.viewport.position.x),
1477 f32::from(self.viewport.position.y),
1478 f32::from(self.viewport.size.x),
1479 f32::from(self.viewport.size.y),
1480 0.0,
1481 1.0,
1482 );
1483
1484 render_pass.set_pipeline(&self.virtual_to_screen_shader_info.pipeline);
1486 render_pass.set_bind_group(0, &self.virtual_to_surface_bind_group, &[]);
1487 render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
1488
1489 render_pass.draw(0..6, 0..1);
1490 }
1491
1492 pub fn texture_resource_from_texture(&self, texture: &wgpu::Texture, label: &str) -> Texture {
1493 trace!("load texture from memory with name: '{label}'");
1494 let size = &texture.size();
1495 let texture_and_sampler_bind_group =
1496 mireforge_wgpu_sprites::create_sprite_texture_and_sampler_bind_group(
1497 &self.device,
1498 &self.texture_sampler_bind_group_layout,
1499 texture,
1500 &self.sampler,
1501 label,
1502 );
1503
1504 let texture_size = UVec2::new(size.width as u16, size.height as u16);
1505
1506 Texture {
1507 texture_and_sampler_bind_group,
1508 texture_size,
1509 }
1510 }
1511}
1512
1513fn create_view_projection_matrix_from_virtual(virtual_width: u16, virtual_height: u16) -> Matrix4 {
1514 let (bottom, top) = (0.0, f32::from(virtual_height));
1515
1516 let (near, far) =
1519 (-1.0, 1.0)
1521 ;
1522
1523 OrthoInfo {
1524 left: 0.0,
1525 right: f32::from(virtual_width),
1526 bottom,
1527 top,
1528 near, far, }
1531 .into()
1532}
1533
1534fn create_view_uniform_view_projection_matrix(viewport_size: UVec2) -> Matrix4 {
1535 let viewport_width = f32::from(viewport_size.x);
1536 let viewport_height = f32::from(viewport_size.y);
1537
1538 let viewport_aspect_ratio = viewport_width / viewport_height;
1539
1540 let scale_x = 1.0;
1541 let scale_y = viewport_aspect_ratio;
1542
1543 let view_projection_matrix = [
1544 [scale_x, 0.0, 0.0, 0.0],
1545 [0.0, scale_y, 0.0, 0.0],
1546 [0.0, 0.0, -1.0, 0.0],
1547 [0.0, 0.0, 0.0, 1.0],
1548 ];
1549
1550 view_projection_matrix.into()
1551}
1552
1553fn sort_render_items_by_z_and_material(items: &mut [RenderItem]) {
1554 items.sort_by_key(|item| (item.position.z, item.material_ref.clone()));
1555}
1556
1557#[derive(Debug, Clone, Copy, Default)]
1558pub enum Rotation {
1559 #[default]
1560 Degrees0,
1561 Degrees90,
1562 Degrees180,
1563 Degrees270,
1564}
1565
1566#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1567pub enum Anchor {
1568 LowerLeft,
1569 UpperLeft,
1570}
1571
1572#[derive(Debug, Copy, Clone)]
1573pub struct SpriteParams {
1574 pub texture_size: UVec2,
1575 pub texture_pos: UVec2,
1576 pub scale: u8,
1577 pub rotation: Rotation,
1578 pub flip_x: bool,
1579 pub flip_y: bool,
1580 pub pivot: Vec2,
1581 pub color: Color,
1582 pub anchor: Anchor,
1583}
1584
1585impl Default for SpriteParams {
1586 fn default() -> Self {
1587 Self {
1588 texture_size: UVec2::new(0, 0),
1589 texture_pos: UVec2::new(0, 0),
1590 pivot: Vec2::new(0, 0),
1591 flip_x: false,
1592 flip_y: false,
1593 color: Color::from_octet(255, 255, 255, 255),
1594 scale: 1,
1595 rotation: Rotation::Degrees0,
1596 anchor: Anchor::LowerLeft,
1597 }
1598 }
1599}
1600
1601#[derive(Debug, Copy, Clone)]
1602pub struct QuadParams {
1603 pub scale: u8,
1604 pub pivot: Vec2,
1605}
1606
1607impl Default for QuadParams {
1608 fn default() -> Self {
1609 Self {
1610 pivot: Vec2::new(0, 0),
1611 scale: 1,
1612 }
1613 }
1614}
1615
1616pub type BindGroupRef = Arc<BindGroup>;
1617
1618#[derive(Debug, PartialEq, Eq, Asset)]
1619pub struct Texture {
1620 pub texture_and_sampler_bind_group: BindGroup,
1621 pub texture_size: UVec2,
1623}
1624
1625impl Display for Texture {
1626 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
1627 write!(f, "{:?}", self.texture_size)
1628 }
1629}
1630
1631impl PartialOrd<Self> for Texture {
1632 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1633 Some(
1634 self.texture_and_sampler_bind_group
1635 .cmp(&other.texture_and_sampler_bind_group),
1636 )
1637 }
1638}
1639
1640impl Ord for Texture {
1641 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1642 self.texture_and_sampler_bind_group
1643 .cmp(&other.texture_and_sampler_bind_group)
1644 }
1645}
1646
1647#[derive(Debug, Ord, PartialOrd, PartialEq, Eq)]
1648pub struct MaterialBase {
1649 }
1651
1652#[derive(Debug, Ord, PartialOrd, PartialEq, Eq)]
1653pub struct Material {
1654 pub base: MaterialBase,
1655 pub kind: MaterialKind,
1656}
1657
1658impl Material {
1659 #[inline]
1660 #[must_use]
1661 pub fn primary_texture(&self) -> Option<TextureRef> {
1662 self.kind.primary_texture()
1663 }
1664
1665 #[inline]
1666 #[must_use]
1667 pub fn is_complete(&self, textures: &Assets<Texture>) -> bool {
1668 self.kind.is_complete(textures)
1669 }
1670}
1671
1672impl Display for Material {
1673 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1674 write!(f, "{}", self.kind)
1675 }
1676}
1677
1678#[derive(Debug, Ord, PartialOrd, PartialEq, Eq)]
1679pub enum MaterialKind {
1680 NormalSprite {
1681 primary_texture: Id<Texture>,
1682 },
1683 AlphaMasker {
1684 primary_texture: Id<Texture>,
1685 alpha_texture: Id<Texture>,
1686 },
1687 Quad,
1688 LightAdd {
1689 primary_texture: Id<Texture>,
1690 },
1691}
1692
1693impl MaterialKind {}
1694
1695impl MaterialKind {
1696 #[must_use] pub fn primary_texture(&self) -> Option<Id<Texture>> {
1697 match &self {
1698 Self::NormalSprite {
1699 primary_texture, ..
1700 }
1701 | Self::LightAdd { primary_texture }
1702 | Self::AlphaMasker {
1703 primary_texture, ..
1704 } => Some(primary_texture.clone()),
1705 Self::Quad => None,
1706 }
1707 }
1708
1709 pub(crate) fn is_complete(&self, textures: &Assets<Texture>) -> bool {
1710 match &self {
1711 Self::NormalSprite { primary_texture } | Self::LightAdd { primary_texture } => {
1712 textures.contains(primary_texture)
1713 }
1714 Self::AlphaMasker {
1715 primary_texture,
1716 alpha_texture,
1717 } => textures.contains(primary_texture) && textures.contains(alpha_texture),
1718 Self::Quad => true,
1719 }
1720 }
1721}
1722
1723impl Display for MaterialKind {
1724 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1725 let texture_name = self
1726 .primary_texture()
1727 .map_or_else(String::new, |x| x.to_string());
1728
1729 let kind_name = match self {
1730 Self::NormalSprite { .. } => "NormalSprite",
1731 Self::LightAdd { .. } => "Light (Add)",
1732 Self::Quad => "Quad",
1733 Self::AlphaMasker { .. } => "AlphaMasker",
1734 };
1735
1736 write!(f, "{kind_name} texture {texture_name}")
1737 }
1738}
1739
1740#[derive(Debug)]
1741pub struct Sprite {
1742 pub params: SpriteParams,
1743}
1744
1745#[derive(Debug)]
1746pub struct QuadColor {
1747 pub size: UVec2,
1748 pub color: Color,
1749 pub params: QuadParams,
1750}
1751
1752#[derive(Debug, Copy, Clone)]
1753pub struct Slices {
1754 pub left: u16,
1755 pub top: u16,
1756 pub right: u16, pub bottom: u16, }
1759
1760#[derive(Debug)]
1761pub struct NineSlice {
1762 pub size: UVec2, pub slices: Slices,
1764 pub color: Color, pub origin_in_atlas: UVec2,
1766 pub size_inside_atlas: Option<UVec2>,
1767}
1768
1769#[derive(Debug)]
1770pub struct TileMap {
1771 pub tiles_data_grid_size: UVec2,
1772 pub cell_count_size: UVec2,
1773 pub one_cell_size: UVec2,
1774 pub tiles: Vec<u16>,
1775 pub scale: u8,
1776}
1777
1778#[derive(PartialEq, Debug, Eq, Ord, PartialOrd)]
1779pub struct Pipeline {
1780 name: String,
1781 render_pipeline: RenderPipeline,
1782}
1783
1784impl Display for Pipeline {
1785 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1786 write!(f, "pipeline: {}", self.name)
1787 }
1788}
1789
1790pub type PipelineRef = Arc<Pipeline>;