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 NineSliceStretch(NineSlice),
135 TileMap(TileMap),
136 Text(Text),
137 Mask(UVec2, Color),
138}
139
140const MAXIMUM_QUADS_FOR_RENDER_ITEM: usize = 1024;
141const MAXIMUM_QUADS_IN_A_BATCH: usize = 4096;
142const MAXIMUM_QUADS_IN_ONE_RENDER: usize = MAXIMUM_QUADS_IN_A_BATCH * 8;
143
144#[derive(Resource)]
145pub struct Render {
146 virtual_surface_texture_view: TextureView,
147 virtual_surface_texture: wgpu::Texture,
148 virtual_to_surface_bind_group: BindGroup,
149 index_buffer: Buffer, vertex_buffer: Buffer, sampler: wgpu::Sampler,
152 virtual_to_screen_shader_info: ShaderInfo,
153 pub normal_sprite_pipeline: ShaderInfo,
154 pub quad_shader_info: ShaderInfo,
155 pub mask_shader_info: ShaderInfo,
156 pub light_shader_info: ShaderInfo,
157 physical_surface_size: UVec2,
158 viewport_strategy: ViewportStrategy,
159 virtual_surface_size: UVec2,
160 camera_bind_group: BindGroup,
162 #[allow(unused)]
163 camera_buffer: Buffer,
164
165 texture_sampler_bind_group_layout: BindGroupLayout,
167
168 quad_matrix_and_uv_instance_buffer: Buffer,
170
171 device: Arc<wgpu::Device>,
172 queue: Arc<wgpu::Queue>, items: Vec<RenderItem>,
176 origin: Vec2,
178
179 batch_offsets: Vec<(WeakMaterialRef, u32, u32)>,
181 viewport: URect,
182 clear_color: wgpu::Color,
183 screen_clear_color: wgpu::Color,
184 last_render_at: Millis,
185 scale: f32,
186 surface_texture_format: TextureFormat,
187 debug_tick: u64,
188}
189
190impl Render {}
191
192impl Debug for Render {
193 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
194 write!(f, "Render")
195 }
196}
197
198impl Render {
199 #[must_use]
200 pub fn new(
201 device: Arc<wgpu::Device>,
202 queue: Arc<wgpu::Queue>, surface_texture_format: wgpu::TextureFormat,
204 physical_size: UVec2,
205 virtual_surface_size: UVec2,
206 now: Millis,
207 ) -> Self {
208 let sprite_info = SpriteInfo::new(
209 &device,
210 surface_texture_format,
211 create_view_uniform_view_projection_matrix(physical_size),
212 );
213
214 let (virtual_surface_texture, virtual_surface_texture_view, virtual_to_surface_bind_group) =
215 Self::create_virtual_texture(&device, surface_texture_format, virtual_surface_size);
216
217 Self {
218 device,
219 queue,
220 surface_texture_format,
221 items: Vec::new(),
222 virtual_to_screen_shader_info: sprite_info.virtual_to_screen_shader_info,
224 virtual_surface_texture,
225 virtual_surface_texture_view,
226 virtual_to_surface_bind_group,
227 sampler: sprite_info.sampler,
228 normal_sprite_pipeline: sprite_info.sprite_shader_info,
229 quad_shader_info: sprite_info.quad_shader_info,
230 mask_shader_info: sprite_info.mask_shader_info,
231 light_shader_info: sprite_info.light_shader_info,
232 texture_sampler_bind_group_layout: sprite_info.sprite_texture_sampler_bind_group_layout,
233 index_buffer: sprite_info.index_buffer,
234 vertex_buffer: sprite_info.vertex_buffer,
235 quad_matrix_and_uv_instance_buffer: sprite_info.quad_matrix_and_uv_instance_buffer,
236 camera_bind_group: sprite_info.camera_bind_group,
237 batch_offsets: Vec::new(),
238 camera_buffer: sprite_info.camera_uniform_buffer,
239 viewport: Self::viewport_from_integer_scale(physical_size, virtual_surface_size),
240 clear_color: to_wgpu_color(Color::from_f32(0.008, 0.015, 0.008, 1.0)),
241 screen_clear_color: to_wgpu_color(Color::from_f32(0.018, 0.025, 0.018, 1.0)),
242 origin: Vec2::new(0, 0),
243 last_render_at: now,
244 physical_surface_size: physical_size,
245 viewport_strategy: ViewportStrategy::FitIntegerScaling,
246 virtual_surface_size,
247 scale: 1.0,
248 debug_tick: 0,
249 }
250 }
251
252 #[must_use]
253 pub fn create_virtual_texture(
254 device: &Device,
255 surface_texture_format: TextureFormat,
256 virtual_surface_size: UVec2,
257 ) -> (wgpu::Texture, TextureView, BindGroup) {
258 let virtual_surface_texture = device.create_texture(&wgpu::TextureDescriptor {
260 label: Some("Render Texture"),
261 size: wgpu::Extent3d {
262 width: u32::from(virtual_surface_size.x),
263 height: u32::from(virtual_surface_size.y),
264 depth_or_array_layers: 1,
265 },
266 mip_level_count: 1,
267 sample_count: 1,
268 dimension: wgpu::TextureDimension::D2,
269 format: surface_texture_format, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
271 view_formats: &[],
272 });
273
274 let virtual_surface_texture_view =
275 virtual_surface_texture.create_view(&wgpu::TextureViewDescriptor::default());
276
277 let virtual_to_screen_sampler =
278 create_nearest_sampler(device, "nearest sampler for virtual to screen");
279 let virtual_to_screen_layout =
280 create_texture_and_sampler_group_layout(device, "virtual to screen layout");
281 let virtual_to_surface_bind_group = create_texture_and_sampler_bind_group_ex(
282 device,
283 &virtual_to_screen_layout,
284 &virtual_surface_texture_view,
285 &virtual_to_screen_sampler,
286 "virtual to screen bind group",
287 );
288
289 (
290 virtual_surface_texture,
291 virtual_surface_texture_view,
292 virtual_to_surface_bind_group,
293 )
294 }
295
296 pub const fn set_now(&mut self, now: Millis) {
297 self.last_render_at = now;
298 }
299
300 #[must_use]
301 pub const fn virtual_surface_size_with_scaling(&self) -> UVec2 {
302 match self.viewport_strategy {
303 ViewportStrategy::FitIntegerScaling | ViewportStrategy::FitFloatScaling => {
304 self.virtual_surface_size
305 }
306 ViewportStrategy::MatchPhysicalSize => self.physical_surface_size,
307 }
308 }
309
310 #[must_use]
311 pub const fn physical_surface_size(&self) -> UVec2 {
312 self.physical_surface_size
313 }
314
315 #[must_use]
316 pub const fn viewport(&self) -> URect {
317 self.viewport
318 }
319
320 #[inline]
321 fn push_sprite(&mut self, position: Vec3, material: &MaterialRef, sprite: Sprite) {
322 self.items.push(RenderItem {
323 position,
324 material_ref: material.clone(),
325 renderable: Renderable::Sprite(sprite),
326 });
327 }
328
329 pub fn push_mask(
330 &mut self,
331 position: Vec3,
332 size: UVec2,
333 color: Color,
334 alpha_masked: &MaterialRef,
335 ) {
336 self.items.push(RenderItem {
337 position,
338 material_ref: alpha_masked.clone(),
339 renderable: Renderable::Mask(size, color),
340 });
341 }
342
343 pub fn push_mask_create_material(
344 &mut self,
345 position: Vec3,
346 primary_texture: TextureRef,
347 alpha_texture: TextureRef,
348 texture_offset: UVec2,
349 color: Color,
350 ) {
351 let masked_material = Material {
352 base: MaterialBase {},
353 kind: MaterialKind::AlphaMasker {
354 primary_texture,
355 alpha_texture,
356 },
357 };
358
359 let masked_material_ref = Arc::new(masked_material);
360
361 self.items.push(RenderItem {
362 position,
363 material_ref: masked_material_ref,
364 renderable: Renderable::Mask(texture_offset, color),
365 });
366 }
367
368 pub fn push_nine_slice(
369 &mut self,
370 position: Vec3,
371 size: UVec2,
372 color: Color,
373 nine_slice_and_material: &NineSliceAndMaterial,
374 ) {
375 let nine_slice_info = NineSlice {
376 size,
377 slices: nine_slice_and_material.slices,
378 color,
379 origin_in_atlas: UVec2::new(0, 0),
380 size_inside_atlas: None,
381 };
382
383 self.items.push(RenderItem {
384 position,
385 material_ref: nine_slice_and_material.material_ref.clone(),
386 renderable: Renderable::NineSlice(nine_slice_info),
387 });
388 }
389
390 pub fn push_nine_slice_stretch(
391 &mut self,
392 position: Vec3,
393 size: UVec2,
394 color: Color,
395 nine_slice_and_material: &NineSliceAndMaterial,
396 ) {
397 let nine_slice_info = NineSlice {
398 size,
399 slices: nine_slice_and_material.slices,
400 color,
401 origin_in_atlas: UVec2::new(0, 0),
402 size_inside_atlas: None,
403 };
404
405 self.items.push(RenderItem {
406 position,
407 material_ref: nine_slice_and_material.material_ref.clone(),
408 renderable: Renderable::NineSliceStretch(nine_slice_info),
409 });
410 }
411
412 #[must_use]
413 pub fn viewport_from_integer_scale(physical_size: UVec2, virtual_size: UVec2) -> URect {
414 let scale_factor = (physical_size.x / virtual_size.x)
415 .min(physical_size.y / virtual_size.y)
416 .max(1);
417
418 let ideal_viewport_size = virtual_size * scale_factor;
419
420 let final_viewport_size =
421 if physical_size.x < ideal_viewport_size.x || physical_size.y < ideal_viewport_size.y {
422 physical_size
423 } else {
424 ideal_viewport_size
425 };
426
427 let border_size = physical_size - final_viewport_size;
428
429 let offset = border_size / 2;
430
431 URect::new(
432 offset.x,
433 offset.y,
434 final_viewport_size.x,
435 final_viewport_size.y,
436 )
437 }
438
439 #[must_use]
440 pub fn viewport_from_float_scale(physical_size: UVec2, virtual_size: UVec2) -> URect {
441 let window_aspect = f32::from(physical_size.x) / f32::from(physical_size.y);
442 let virtual_aspect = f32::from(virtual_size.x) / f32::from(virtual_size.y);
443
444 if physical_size.x < virtual_size.x || physical_size.y < virtual_size.y {
445 return URect::new(0, 0, physical_size.x, physical_size.y);
446 }
447
448 let mut float_scale = if window_aspect > virtual_aspect {
449 f32::from(physical_size.y) / f32::from(virtual_size.y)
450 } else {
451 f32::from(physical_size.x) / f32::from(virtual_size.x)
452 };
453
454 if float_scale < 0.01 {
455 float_scale = 0.01;
456 }
457
458 let viewport_actual_size = UVec2::new(
459 (f32::from(virtual_size.x) * float_scale) as u16,
460 (f32::from(virtual_size.y) * float_scale) as u16,
461 );
462
463 let border_size = physical_size - viewport_actual_size;
464
465 let offset = border_size / 2;
466
467 URect::new(
468 offset.x,
469 offset.y,
470 viewport_actual_size.x,
471 viewport_actual_size.y,
472 )
473 }
474
475 pub const fn resize(&mut self, physical_size: UVec2) {
476 self.physical_surface_size = physical_size;
477 }
478
479 pub fn resize_virtual(&mut self, virtual_surface_size: UVec2) {
480 if virtual_surface_size == self.virtual_surface_size {
481 return;
482 }
483 self.virtual_surface_size = virtual_surface_size;
484 debug!(?virtual_surface_size, "virtual surface changed");
485 let (virtual_surface_texture, virtual_surface_texture_view, virtual_to_surface_bind_group) =
486 Self::create_virtual_texture(
487 &self.device,
488 self.surface_texture_format,
489 virtual_surface_size,
490 );
491 self.virtual_surface_texture = virtual_surface_texture;
492 self.virtual_surface_texture_view = virtual_surface_texture_view;
493 self.virtual_to_surface_bind_group = virtual_to_surface_bind_group;
494 }
495
496 pub fn sprite_atlas(&mut self, position: Vec3, atlas_rect: URect, material_ref: &MaterialRef) {
497 self.push_sprite(
498 position,
499 material_ref,
500 Sprite {
501 params: SpriteParams {
502 texture_pos: atlas_rect.position,
503 texture_size: atlas_rect.size,
504 ..Default::default()
505 },
506 },
507 );
508 }
509
510 pub fn sprite_atlas_frame(&mut self, position: Vec3, frame: u16, atlas: &impl FrameLookup) {
511 let (material_ref, atlas_rect) = atlas.lookup(frame);
512 self.push_sprite(
513 position,
514 material_ref,
515 Sprite {
516 params: SpriteParams {
517 texture_pos: atlas_rect.position,
518 texture_size: atlas_rect.size,
519 ..Default::default()
520 },
521 },
522 );
523 }
524
525 pub fn sprite_atlas_frame_ex(
526 &mut self,
527 position: Vec3,
528 frame: u16,
529 atlas: &impl FrameLookup,
530 mut params: SpriteParams,
531 ) {
532 let (material_ref, atlas_rect) = atlas.lookup(frame);
533 params.texture_pos = atlas_rect.position;
534 params.texture_size = atlas_rect.size;
535 self.push_sprite(position, material_ref, Sprite { params });
536 }
537
538 pub fn draw_sprite(&mut self, position: Vec3, material: &MaterialRef) {
539 self.push_sprite(
540 position,
541 material,
542 Sprite {
543 params: SpriteParams::default(),
544 },
545 );
546 }
547
548 pub fn draw_sprite_ex(&mut self, position: Vec3, material: &MaterialRef, params: SpriteParams) {
549 self.push_sprite(position, material, Sprite { params });
550 }
551
552 pub fn nine_slice(
553 &mut self,
554 position: Vec3,
555 size: UVec2,
556 color: Color,
557 nine_slice_and_material: &NineSliceAndMaterial,
558 ) {
559 self.push_nine_slice(position, size, color, nine_slice_and_material);
560 }
561
562 pub fn nine_slice_stretch(
563 &mut self,
564 position: Vec3,
565 size: UVec2,
566 color: Color,
567 nine_slice_and_material: &NineSliceAndMaterial,
568 ) {
569 self.push_nine_slice_stretch(position, size, color, nine_slice_and_material);
570 }
571
572 pub fn draw_quad(&mut self, position: Vec3, size: UVec2, color: Color) {
573 let material = Material {
574 base: MaterialBase {},
575 kind: MaterialKind::Quad,
576 };
577
578 self.items.push(RenderItem {
579 position,
580 material_ref: MaterialRef::from(material),
581 renderable: Renderable::QuadColor(QuadColor {
582 size,
583 color,
584 params: QuadParams::default(),
585 }),
586 });
587 }
588
589 pub fn draw_quad_ex(&mut self, position: Vec3, size: UVec2, color: Color, params: QuadParams) {
590 let material = Material {
591 base: MaterialBase {},
592 kind: MaterialKind::Quad,
593 };
594
595 self.items.push(RenderItem {
596 position,
597 material_ref: MaterialRef::from(material),
598 renderable: Renderable::QuadColor(QuadColor {
599 size,
600 color,
601 params,
602 }),
603 });
604 }
605
606 #[allow(clippy::too_many_arguments)]
607 pub fn draw_nine_slice(
608 &mut self,
609 position: Vec3,
610 size: UVec2,
611 slices: Slices,
612 material_ref: &MaterialRef,
613 color: Color,
614 ) {
615 self.items.push(RenderItem {
616 position,
617 material_ref: material_ref.clone(),
618 renderable: Renderable::NineSlice(NineSlice {
619 size,
620 slices,
621 color,
622 origin_in_atlas: UVec2::new(0, 0),
623 size_inside_atlas: None,
624 }),
625 });
626 }
627
628 #[must_use]
629 pub const fn clear_color(&self) -> wgpu::Color {
630 self.clear_color
631 }
632
633 fn calculate_texture_coords_mul_add(atlas_rect: URect, texture_size: UVec2) -> Vec4 {
635 let x = f32::from(atlas_rect.position.x) / f32::from(texture_size.x);
636 let y = f32::from(atlas_rect.position.y) / f32::from(texture_size.y);
637 let width = f32::from(atlas_rect.size.x) / f32::from(texture_size.x);
638 let height = f32::from(atlas_rect.size.y) / f32::from(texture_size.y);
639 Vec4([width, height, x, y])
640 }
641
642 fn order_render_items_in_batches(&mut self) -> Vec<Vec<&RenderItem>> {
643 let mut material_batches: Vec<Vec<&RenderItem>> = Vec::new();
644 let mut current_batch: Vec<&RenderItem> = Vec::new();
645 let mut current_material: Option<MaterialRef> = None;
646
647 for render_item in &self.items {
648 if Some(&render_item.material_ref) != current_material.as_ref() {
649 if !current_batch.is_empty() {
650 material_batches.push(current_batch.clone());
651 current_batch.clear();
652 }
653 current_material = Some(render_item.material_ref.clone());
654 }
655 current_batch.push(render_item);
656 }
657
658 if !current_batch.is_empty() {
659 material_batches.push(current_batch);
660 }
661
662 material_batches
663 }
664
665 #[must_use]
666 pub fn quad_helper_uniform(
667 position: Vec3,
668 quad_size: UVec2,
669 render_atlas: URect,
670 color: Color,
671
672 current_texture_size: UVec2,
673 ) -> SpriteInstanceUniform {
674 let model_matrix =
675 Matrix4::from_translation(f32::from(position.x), f32::from(position.y), 0.0)
676 * Matrix4::from_scale(f32::from(quad_size.x), f32::from(quad_size.y), 1.0);
677
678 let tex_coords_mul_add =
679 Self::calculate_texture_coords_mul_add(render_atlas, current_texture_size);
680
681 let rotation_value = 0;
682
683 SpriteInstanceUniform::new(
684 model_matrix,
685 tex_coords_mul_add,
686 rotation_value,
687 Vec4(color.to_f32_slice()),
688 )
689 }
690
691 #[allow(clippy::too_many_lines)]
694 pub fn write_vertex_indices_and_uv_to_buffer(
695 &mut self,
696 textures: &Assets<Texture>,
697 fonts: &Assets<Font>,
698 ) {
699 const FLIP_X_MASK: u32 = 0b0000_0100;
700 const FLIP_Y_MASK: u32 = 0b0000_1000;
701
702 let batches = self.sort_and_put_in_batches();
703
704 let mut quad_matrix_and_uv: Vec<SpriteInstanceUniform> = Vec::new();
705 let mut batch_vertex_ranges: Vec<(MaterialRef, u32, u32)> = Vec::new();
706
707 for render_items in batches {
708 let quad_len_before = quad_matrix_and_uv.len();
709
710 let weak_material_ref = render_items
712 .first()
713 .map(|item| {
714 let material_ref: MaterialRef = item.material_ref.clone();
716 material_ref
717 })
718 .expect("Render items batch was empty");
719
720 if !weak_material_ref.is_complete(textures) {
721 trace!(?weak_material_ref, "material is not complete yet");
723 continue;
724 }
725 let material = weak_material_ref.clone();
726
727 let maybe_texture_ref = material.primary_texture();
728 let maybe_texture = maybe_texture_ref
729 .and_then(|found_primary_texture_ref| textures.get(&found_primary_texture_ref));
730
731 for render_item in render_items {
732 let quad_len_before_inner = quad_matrix_and_uv.len();
733
734 match &render_item.renderable {
735 Renderable::Sprite(sprite) => {
736 let current_texture_size = maybe_texture.unwrap().texture_size;
737
738 let params = &sprite.params;
739 let mut size = params.texture_size;
740 if size.x == 0 && size.y == 0 {
741 size = current_texture_size;
742 }
743
744 let render_atlas = URect {
745 position: params.texture_pos,
746 size,
747 };
748
749 match params.rotation {
750 Rotation::Degrees90 | Rotation::Degrees270 => {
751 swap(&mut size.x, &mut size.y);
752 }
753 _ => {}
754 }
755
756 let y_offset = if params.anchor == Anchor::UpperLeft {
757 current_texture_size.y as i16
758 } else {
759 0
760 };
761
762 let model_matrix = Matrix4::from_translation(
763 f32::from(render_item.position.x),
764 f32::from(render_item.position.y - y_offset),
765 0.0,
766 ) * Matrix4::from_scale(
767 f32::from(size.x * u16::from(params.scale)),
768 f32::from(size.y * u16::from(params.scale)),
769 1.0,
770 );
771
772 let tex_coords_mul_add = Self::calculate_texture_coords_mul_add(
773 render_atlas,
774 current_texture_size,
775 );
776
777 let mut rotation_value = match params.rotation {
778 Rotation::Degrees0 => 0,
779 Rotation::Degrees90 => 1,
780 Rotation::Degrees180 => 2,
781 Rotation::Degrees270 => 3,
782 };
783
784 if params.flip_x {
785 rotation_value |= FLIP_X_MASK;
786 }
787 if params.flip_y {
788 rotation_value |= FLIP_Y_MASK;
789 }
790
791 let quad_instance = SpriteInstanceUniform::new(
792 model_matrix,
793 tex_coords_mul_add,
794 rotation_value,
795 Vec4(params.color.to_f32_slice()),
796 );
797 quad_matrix_and_uv.push(quad_instance);
798 }
799
800 Renderable::Mask(texture_offset, color) => {
801 let current_texture_size = maybe_texture.unwrap().texture_size;
802 let params = SpriteParams {
803 texture_size: current_texture_size,
804 texture_pos: *texture_offset,
805 scale: 1,
806 rotation: Rotation::default(),
807 flip_x: false,
808 flip_y: false,
809 pivot: Vec2 { x: 0, y: 0 },
810 color: *color,
811 anchor: Anchor::LowerLeft,
812 };
813
814 let mut size = params.texture_size;
815 if size.x == 0 && size.y == 0 {
816 size = current_texture_size;
817 }
818
819 let render_atlas = URect {
820 position: params.texture_pos,
821 size,
822 };
823
824 let model_matrix = Matrix4::from_translation(
825 f32::from(render_item.position.x),
826 f32::from(render_item.position.y),
827 0.0,
828 ) * Matrix4::from_scale(
829 f32::from(size.x * u16::from(params.scale)),
830 f32::from(size.y * u16::from(params.scale)),
831 1.0,
832 );
833
834 let tex_coords_mul_add = Self::calculate_texture_coords_mul_add(
835 render_atlas,
836 current_texture_size,
837 );
838
839 let mut rotation_value = match params.rotation {
840 Rotation::Degrees0 => 0,
841 Rotation::Degrees90 => 1,
842 Rotation::Degrees180 => 2,
843 Rotation::Degrees270 => 3,
844 };
845
846 if params.flip_x {
847 rotation_value |= FLIP_X_MASK;
848 }
849 if params.flip_y {
850 rotation_value |= FLIP_Y_MASK;
851 }
852
853 let quad_instance = SpriteInstanceUniform::new(
854 model_matrix,
855 tex_coords_mul_add,
856 rotation_value,
857 Vec4(params.color.to_f32_slice()),
858 );
859 quad_matrix_and_uv.push(quad_instance);
860 }
861
862 Renderable::NineSlice(nine_slice) => {
863 let current_texture_size = maybe_texture.unwrap().texture_size;
864 Self::prepare_nine_slice(
865 nine_slice,
866 render_item.position,
867 &mut quad_matrix_and_uv,
868 current_texture_size,
869 );
870 }
871
872 Renderable::NineSliceStretch(nine_slice) => {
873 let current_texture_size = maybe_texture.unwrap().texture_size;
874 Self::prepare_nine_slice_single_center_quad(
875 nine_slice,
876 render_item.position,
877 &mut quad_matrix_and_uv,
878 current_texture_size,
879 );
880 }
881
882 Renderable::QuadColor(quad) => {
883 let model_matrix = Matrix4::from_translation(
884 f32::from(render_item.position.x) - f32::from(quad.params.pivot.x),
885 f32::from(render_item.position.y) - f32::from(quad.params.pivot.y),
886 0.0,
887 ) * Matrix4::from_scale(
888 f32::from(quad.size.x),
889 f32::from(quad.size.y),
890 1.0,
891 );
892
893 let tex_coords_mul_add = Vec4([
894 0.0, 0.0, 0.0, 0.0,
897 ]);
898 let rotation_value = 0;
899
900 let quad_instance = SpriteInstanceUniform::new(
901 model_matrix,
902 tex_coords_mul_add,
903 rotation_value,
904 Vec4(quad.color.to_f32_slice()),
905 );
906 quad_matrix_and_uv.push(quad_instance);
907 }
908
909 Renderable::Text(text) => {
910 let current_texture_size = maybe_texture.unwrap().texture_size;
911 let result = fonts.get_weak(text.font_ref);
912 if result.is_none() {
913 continue;
914 }
915 let font = result.unwrap();
916
917 let glyph_draw = font.draw(&text.text);
918 for glyph in glyph_draw.glyphs {
919 let pos = render_item.position + Vec3::from(glyph.relative_position);
920 let texture_size = glyph.texture_rectangle.size;
921 let model_matrix =
922 Matrix4::from_translation(f32::from(pos.x), f32::from(pos.y), 0.0)
923 * Matrix4::from_scale(
924 f32::from(texture_size.x),
925 f32::from(texture_size.y),
926 1.0,
927 );
928 let tex_coords_mul_add = Self::calculate_texture_coords_mul_add(
929 glyph.texture_rectangle,
930 current_texture_size,
931 );
932
933 let quad_instance = SpriteInstanceUniform::new(
934 model_matrix,
935 tex_coords_mul_add,
936 0,
937 Vec4(text.color.to_f32_slice()),
938 );
939 quad_matrix_and_uv.push(quad_instance);
940 }
941 }
942
943 Renderable::TileMap(tile_map) => {
944 for (index, tile) in tile_map.tiles.iter().enumerate() {
945 let cell_pos_x = (index as u16 % tile_map.tiles_data_grid_size.x)
946 * tile_map.one_cell_size.x
947 * u16::from(tile_map.scale);
948 let cell_pos_y = (index as u16 / tile_map.tiles_data_grid_size.x)
949 * tile_map.one_cell_size.y
950 * u16::from(tile_map.scale);
951 let cell_x = *tile % tile_map.cell_count_size.x;
952 let cell_y = *tile / tile_map.cell_count_size.x;
953
954 let tex_x = cell_x * tile_map.one_cell_size.x;
955 let tex_y = cell_y * tile_map.one_cell_size.x;
956
957 let cell_texture_area = URect::new(
958 tex_x,
959 tex_y,
960 tile_map.one_cell_size.x,
961 tile_map.one_cell_size.y,
962 );
963
964 let cell_model_matrix = Matrix4::from_translation(
965 f32::from(render_item.position.x + cell_pos_x as i16),
966 f32::from(render_item.position.y + cell_pos_y as i16),
967 0.0,
968 ) * Matrix4::from_scale(
969 f32::from(tile_map.one_cell_size.x * u16::from(tile_map.scale)),
970 f32::from(tile_map.one_cell_size.y * u16::from(tile_map.scale)),
971 1.0,
972 );
973
974 let current_texture_size = maybe_texture.unwrap().texture_size;
975 let cell_tex_coords_mul_add = Self::calculate_texture_coords_mul_add(
976 cell_texture_area,
977 current_texture_size,
978 );
979
980 let quad_instance = SpriteInstanceUniform::new(
981 cell_model_matrix,
982 cell_tex_coords_mul_add,
983 0,
984 Vec4([1.0, 1.0, 1.0, 1.0]),
985 );
986 quad_matrix_and_uv.push(quad_instance);
987 }
988 }
989 }
990
991 let quad_count_for_this_render_item =
992 quad_matrix_and_uv.len() - quad_len_before_inner;
993 assert!(
994 quad_count_for_this_render_item <= MAXIMUM_QUADS_FOR_RENDER_ITEM,
995 "too many quads {quad_count_for_this_render_item} for render item {render_item:?}"
996 );
997 }
998
999 let quad_count_for_this_batch = quad_matrix_and_uv.len() - quad_len_before;
1000 assert!(
1001 quad_count_for_this_batch <= MAXIMUM_QUADS_IN_A_BATCH,
1002 "too many quads {quad_count_for_this_batch} total to render in this batch"
1003 );
1004
1005 assert!(
1006 quad_matrix_and_uv.len() <= MAXIMUM_QUADS_IN_ONE_RENDER,
1007 "too many quads for whole render {}",
1008 quad_matrix_and_uv.len()
1009 );
1010
1011 batch_vertex_ranges.push((
1012 weak_material_ref,
1013 quad_len_before as u32,
1014 quad_count_for_this_batch as u32,
1015 ));
1016 }
1017
1018 self.queue.write_buffer(
1020 &self.quad_matrix_and_uv_instance_buffer,
1021 0,
1022 bytemuck::cast_slice(&quad_matrix_and_uv),
1023 );
1024
1025 self.batch_offsets = batch_vertex_ranges;
1026 }
1027
1028 #[allow(clippy::too_many_lines)]
1029 #[inline]
1030 pub fn prepare_nine_slice(
1031 nine_slice: &NineSlice,
1032 position_offset: Vec3,
1033 quad_matrix_and_uv: &mut Vec<SpriteInstanceUniform>,
1034 current_texture_size: UVec2,
1035 ) {
1036 let world_window_size = nine_slice.size;
1037 let slices = &nine_slice.slices;
1038
1039 assert!(
1044 world_window_size.x >= slices.left + slices.right,
1045 "NineSlice.width ({}) < slices.left + slices.right ({})",
1046 world_window_size.x,
1047 slices.left + slices.right
1048 );
1049 assert!(
1050 world_window_size.y >= slices.top + slices.bottom,
1051 "NineSlice.height ({}) < slices.top + slices.bottom ({})",
1052 world_window_size.y,
1053 slices.top + slices.bottom
1054 );
1055
1056 let texture_window_size = nine_slice.size_inside_atlas.unwrap_or(current_texture_size);
1057
1058 assert!(
1060 texture_window_size.x >= slices.left + slices.right,
1061 "texture_window_size.width ({}) < slices.left + slices.right ({})",
1062 texture_window_size.x,
1063 slices.left + slices.right
1064 );
1065 assert!(
1066 texture_window_size.y >= slices.top + slices.bottom,
1067 "texture_window_size.height ({}) < slices.top + slices.bottom ({})",
1068 texture_window_size.y,
1069 slices.top + slices.bottom
1070 );
1071 let color = nine_slice.color;
1074
1075 let atlas_origin = nine_slice.origin_in_atlas;
1076 let texture_window_size = nine_slice.size_inside_atlas.unwrap_or(current_texture_size);
1077
1078 let world_edge_width = nine_slice.size.x - slices.left - slices.right;
1079 let world_edge_height = nine_slice.size.y - slices.top - slices.bottom;
1080 let texture_edge_width = texture_window_size.x - slices.left - slices.right;
1081 let texture_edge_height = texture_window_size.y - slices.top - slices.bottom;
1082
1083 let lower_left_pos = Vec3::new(position_offset.x, position_offset.y, 0);
1086 let corner_size = UVec2::new(slices.left, slices.bottom);
1087 let lower_left_quad_size = UVec2::new(corner_size.x, corner_size.y);
1089 let lower_left_atlas = URect::new(
1090 atlas_origin.x,
1091 atlas_origin.y + texture_window_size.y - slices.bottom, corner_size.x,
1093 corner_size.y,
1094 );
1095 let lower_left_quad = Self::quad_helper_uniform(
1096 lower_left_pos,
1097 lower_left_quad_size,
1098 lower_left_atlas,
1099 color,
1100 current_texture_size,
1101 );
1102 quad_matrix_and_uv.push(lower_left_quad);
1103
1104 let lower_side_position =
1106 Vec3::new(position_offset.x + slices.left as i16, position_offset.y, 0);
1107 let lower_side_world_quad_size = UVec2::new(world_edge_width, slices.bottom);
1110 let lower_side_texture_size = UVec2::new(texture_edge_width, slices.bottom);
1111 let lower_side_atlas = URect::new(
1113 atlas_origin.x + slices.left,
1114 atlas_origin.y + texture_window_size.y - slices.bottom, lower_side_texture_size.x,
1116 lower_side_texture_size.y,
1117 );
1118 let lower_side_quad = Self::quad_helper_uniform(
1119 lower_side_position,
1120 lower_side_world_quad_size,
1121 lower_side_atlas,
1122 color,
1123 current_texture_size,
1124 );
1125 quad_matrix_and_uv.push(lower_side_quad);
1126
1127 let lower_right_pos = Vec3::new(
1129 position_offset.x + (world_window_size.x - slices.right) as i16,
1130 position_offset.y,
1131 0,
1132 );
1133 let lower_right_corner_size = UVec2::new(slices.right, slices.bottom);
1134 let lower_right_atlas = URect::new(
1135 atlas_origin.x + texture_window_size.x - slices.right,
1136 atlas_origin.y + texture_window_size.y - slices.bottom, lower_right_corner_size.x,
1138 lower_right_corner_size.y,
1139 );
1140 let lower_right_quad = Self::quad_helper_uniform(
1141 lower_right_pos,
1142 lower_right_corner_size,
1143 lower_right_atlas,
1144 color,
1145 current_texture_size,
1146 );
1147 quad_matrix_and_uv.push(lower_right_quad);
1148
1149 let left_edge_pos = Vec3::new(
1151 position_offset.x,
1152 position_offset.y + slices.bottom as i16,
1153 0,
1154 );
1155 let left_edge_world_quad_size = UVec2::new(slices.left, world_edge_height);
1156 let left_edge_texture_size = UVec2::new(slices.left, texture_edge_height);
1157 let left_edge_atlas = URect::new(
1158 atlas_origin.x,
1159 atlas_origin.y + slices.top, left_edge_texture_size.x,
1161 left_edge_texture_size.y,
1162 );
1163 let left_edge_quad = Self::quad_helper_uniform(
1164 left_edge_pos,
1165 left_edge_world_quad_size,
1166 left_edge_atlas,
1167 color,
1168 current_texture_size,
1169 );
1170 quad_matrix_and_uv.push(left_edge_quad);
1171
1172 let base_center_x = atlas_origin.x + slices.left;
1178 let base_center_y = atlas_origin.y + slices.top;
1179
1180 let repeat_x_count =
1182 (f32::from(world_edge_width) / f32::from(texture_edge_width)).ceil() as usize;
1183 let repeat_y_count =
1184 (f32::from(world_edge_height) / f32::from(texture_edge_height)).ceil() as usize;
1185
1186 for y in 0..repeat_y_count {
1187 for x in 0..repeat_x_count {
1188 let this_quad_width = if x == repeat_x_count - 1
1189 && !world_edge_width.is_multiple_of(texture_edge_width)
1190 {
1191 world_edge_width % texture_edge_width
1192 } else {
1193 texture_edge_width
1194 };
1195
1196 let this_quad_height = if y == repeat_y_count - 1
1197 && !world_edge_height.is_multiple_of(texture_edge_height)
1198 {
1199 world_edge_height % texture_edge_height
1200 } else {
1201 texture_edge_height
1202 };
1203
1204 let quad_pos = Vec3::new(
1205 position_offset.x + slices.left as i16 + (x as u16 * texture_edge_width) as i16,
1206 position_offset.y
1207 + slices.bottom as i16
1208 + (y as u16 * texture_edge_height) as i16,
1209 0,
1210 );
1211
1212 let texture_x = base_center_x;
1213
1214 let texture_y = if y == repeat_y_count - 1 && this_quad_height < texture_edge_height
1215 {
1216 base_center_y + (texture_edge_height - this_quad_height)
1217 } else {
1218 base_center_y
1219 };
1220
1221 let this_texture_region =
1222 URect::new(texture_x, texture_y, this_quad_width, this_quad_height);
1223
1224 let center_quad = Self::quad_helper_uniform(
1225 quad_pos,
1226 UVec2::new(this_quad_width, this_quad_height),
1227 this_texture_region,
1228 color,
1229 current_texture_size,
1230 );
1231
1232 quad_matrix_and_uv.push(center_quad);
1233 }
1234 }
1235 let right_edge_pos = Vec3::new(
1239 position_offset.x + (world_window_size.x - slices.right) as i16,
1240 position_offset.y + slices.bottom as i16,
1241 0,
1242 );
1243 let right_edge_world_quad_size = UVec2::new(slices.right, world_edge_height);
1244 let right_edge_texture_size = UVec2::new(slices.right, texture_edge_height);
1245 let right_edge_atlas = URect::new(
1246 atlas_origin.x + texture_window_size.x - slices.right,
1247 atlas_origin.y + slices.top, right_edge_texture_size.x,
1249 right_edge_texture_size.y,
1250 );
1251
1252 let right_edge_quad = Self::quad_helper_uniform(
1253 right_edge_pos,
1254 right_edge_world_quad_size,
1255 right_edge_atlas,
1256 color,
1257 current_texture_size,
1258 );
1259 quad_matrix_and_uv.push(right_edge_quad);
1260
1261 let top_left_pos = Vec3::new(
1263 position_offset.x,
1264 position_offset.y + (world_window_size.y - slices.top) as i16,
1265 0,
1266 );
1267 let top_left_corner_size = UVec2::new(slices.left, slices.top);
1268 let top_left_atlas = URect::new(
1269 atlas_origin.x,
1270 atlas_origin.y, top_left_corner_size.x,
1272 top_left_corner_size.y,
1273 );
1274 let top_left_quad = Self::quad_helper_uniform(
1275 top_left_pos,
1276 top_left_corner_size,
1277 top_left_atlas,
1278 color,
1279 current_texture_size,
1280 );
1281 quad_matrix_and_uv.push(top_left_quad);
1282
1283 let top_edge_pos = Vec3::new(
1285 position_offset.x + slices.left as i16,
1286 position_offset.y + (world_window_size.y - slices.top) as i16,
1287 0,
1288 );
1289 let top_edge_world_quad_size = UVec2::new(world_edge_width, slices.top);
1290 let top_edge_texture_size = UVec2::new(texture_edge_width, slices.top);
1291 let top_edge_atlas = URect::new(
1292 atlas_origin.x + slices.left,
1293 atlas_origin.y, top_edge_texture_size.x,
1295 top_edge_texture_size.y,
1296 );
1297 let top_edge_quad = Self::quad_helper_uniform(
1298 top_edge_pos,
1299 top_edge_world_quad_size,
1300 top_edge_atlas,
1301 color,
1302 current_texture_size,
1303 );
1304 quad_matrix_and_uv.push(top_edge_quad);
1305
1306 let top_right_pos = Vec3::new(
1308 position_offset.x + (world_window_size.x - slices.right) as i16,
1309 position_offset.y + (world_window_size.y - slices.top) as i16,
1310 0,
1311 );
1312 let top_right_corner_size = UVec2::new(slices.right, slices.top);
1313 let top_right_atlas = URect::new(
1314 atlas_origin.x + texture_window_size.x - slices.right,
1315 atlas_origin.y, top_right_corner_size.x,
1317 top_right_corner_size.y,
1318 );
1319 let top_right_quad = Self::quad_helper_uniform(
1320 top_right_pos,
1321 top_right_corner_size,
1322 top_right_atlas,
1323 color,
1324 current_texture_size,
1325 );
1326 quad_matrix_and_uv.push(top_right_quad);
1327 }
1328
1329 #[allow(clippy::too_many_lines)]
1330 #[inline]
1331 pub fn prepare_nine_slice_single_center_quad(
1332 nine_slice: &NineSlice,
1333 position_offset: Vec3,
1334 quad_matrix_and_uv: &mut Vec<SpriteInstanceUniform>,
1335 current_texture_size: UVec2,
1336 ) {
1337 let world_window_size = nine_slice.size;
1338 let slices = &nine_slice.slices;
1339
1340 assert!(
1345 world_window_size.x >= slices.left + slices.right,
1346 "NineSlice.width ({}) < slices.left + slices.right ({})",
1347 world_window_size.x,
1348 slices.left + slices.right
1349 );
1350 assert!(
1351 world_window_size.y >= slices.top + slices.bottom,
1352 "NineSlice.height ({}) < slices.top + slices.bottom ({})",
1353 world_window_size.y,
1354 slices.top + slices.bottom
1355 );
1356
1357 let texture_window_size = nine_slice.size_inside_atlas.unwrap_or(current_texture_size);
1358
1359 assert!(
1361 texture_window_size.x >= slices.left + slices.right,
1362 "texture_window_size.width ({}) < slices.left + slices.right ({})",
1363 texture_window_size.x,
1364 slices.left + slices.right
1365 );
1366 assert!(
1367 texture_window_size.y >= slices.top + slices.bottom,
1368 "texture_window_size.height ({}) < slices.top + slices.bottom ({})",
1369 texture_window_size.y,
1370 slices.top + slices.bottom
1371 );
1372 let color = nine_slice.color;
1375
1376 let atlas_origin = nine_slice.origin_in_atlas;
1377 let texture_window_size = nine_slice.size_inside_atlas.unwrap_or(current_texture_size);
1378
1379 let world_edge_width = nine_slice.size.x - slices.left - slices.right;
1380 let world_edge_height = nine_slice.size.y - slices.top - slices.bottom;
1381 let texture_edge_width = texture_window_size.x - slices.left - slices.right;
1382 let texture_edge_height = texture_window_size.y - slices.top - slices.bottom;
1383
1384 let lower_left_pos = Vec3::new(position_offset.x, position_offset.y, 0);
1387 let corner_size = UVec2::new(slices.left, slices.bottom);
1388 let lower_left_quad_size = UVec2::new(corner_size.x, corner_size.y);
1390 let lower_left_atlas = URect::new(
1391 atlas_origin.x,
1392 atlas_origin.y + texture_window_size.y - slices.bottom, corner_size.x,
1394 corner_size.y,
1395 );
1396 let lower_left_quad = Self::quad_helper_uniform(
1397 lower_left_pos,
1398 lower_left_quad_size,
1399 lower_left_atlas,
1400 color,
1401 current_texture_size,
1402 );
1403 quad_matrix_and_uv.push(lower_left_quad);
1404
1405 let lower_side_position =
1407 Vec3::new(position_offset.x + slices.left as i16, position_offset.y, 0);
1408 let lower_side_world_quad_size = UVec2::new(world_edge_width, slices.bottom);
1411 let lower_side_texture_size = UVec2::new(texture_edge_width, slices.bottom);
1412 let lower_side_atlas = URect::new(
1414 atlas_origin.x + slices.left,
1415 atlas_origin.y + texture_window_size.y - slices.bottom, lower_side_texture_size.x,
1417 lower_side_texture_size.y,
1418 );
1419 let lower_side_quad = Self::quad_helper_uniform(
1420 lower_side_position,
1421 lower_side_world_quad_size,
1422 lower_side_atlas,
1423 color,
1424 current_texture_size,
1425 );
1426 quad_matrix_and_uv.push(lower_side_quad);
1427
1428 let lower_right_pos = Vec3::new(
1430 position_offset.x + (world_window_size.x - slices.right) as i16,
1431 position_offset.y,
1432 0,
1433 );
1434 let lower_right_corner_size = UVec2::new(slices.right, slices.bottom);
1435 let lower_right_atlas = URect::new(
1436 atlas_origin.x + texture_window_size.x - slices.right,
1437 atlas_origin.y + texture_window_size.y - slices.bottom, lower_right_corner_size.x,
1439 lower_right_corner_size.y,
1440 );
1441 let lower_right_quad = Self::quad_helper_uniform(
1442 lower_right_pos,
1443 lower_right_corner_size,
1444 lower_right_atlas,
1445 color,
1446 current_texture_size,
1447 );
1448 quad_matrix_and_uv.push(lower_right_quad);
1449
1450 let left_edge_pos = Vec3::new(
1452 position_offset.x,
1453 position_offset.y + slices.bottom as i16,
1454 0,
1455 );
1456 let left_edge_world_quad_size = UVec2::new(slices.left, world_edge_height);
1457 let left_edge_texture_size = UVec2::new(slices.left, texture_edge_height);
1458 let left_edge_atlas = URect::new(
1459 atlas_origin.x,
1460 atlas_origin.y + slices.top, left_edge_texture_size.x,
1462 left_edge_texture_size.y,
1463 );
1464 let left_edge_quad = Self::quad_helper_uniform(
1465 left_edge_pos,
1466 left_edge_world_quad_size,
1467 left_edge_atlas,
1468 color,
1469 current_texture_size,
1470 );
1471 quad_matrix_and_uv.push(left_edge_quad);
1472
1473 let center_pos = Vec3::new(
1475 position_offset.x + slices.left as i16,
1476 position_offset.y + slices.bottom as i16,
1477 0,
1478 );
1479 let center_world_size = UVec2::new(world_edge_width, world_edge_height);
1480 let center_atlas = URect::new(
1483 atlas_origin.x + slices.left,
1484 atlas_origin.y + slices.top,
1485 texture_edge_width,
1486 texture_edge_height,
1487 );
1488 let center_quad = Self::quad_helper_uniform(
1489 center_pos,
1490 center_world_size,
1491 center_atlas,
1492 color,
1493 current_texture_size,
1494 );
1495 quad_matrix_and_uv.push(center_quad);
1496
1497 let right_edge_pos = Vec3::new(
1499 position_offset.x + (world_window_size.x - slices.right) as i16,
1500 position_offset.y + slices.bottom as i16,
1501 0,
1502 );
1503 let right_edge_world_quad_size = UVec2::new(slices.right, world_edge_height);
1504 let right_edge_texture_size = UVec2::new(slices.right, texture_edge_height);
1505 let right_edge_atlas = URect::new(
1506 atlas_origin.x + texture_window_size.x - slices.right,
1507 atlas_origin.y + slices.top, right_edge_texture_size.x,
1509 right_edge_texture_size.y,
1510 );
1511
1512 let right_edge_quad = Self::quad_helper_uniform(
1513 right_edge_pos,
1514 right_edge_world_quad_size,
1515 right_edge_atlas,
1516 color,
1517 current_texture_size,
1518 );
1519 quad_matrix_and_uv.push(right_edge_quad);
1520
1521 let top_left_pos = Vec3::new(
1523 position_offset.x,
1524 position_offset.y + (world_window_size.y - slices.top) as i16,
1525 0,
1526 );
1527 let top_left_corner_size = UVec2::new(slices.left, slices.top);
1528 let top_left_atlas = URect::new(
1529 atlas_origin.x,
1530 atlas_origin.y, top_left_corner_size.x,
1532 top_left_corner_size.y,
1533 );
1534 let top_left_quad = Self::quad_helper_uniform(
1535 top_left_pos,
1536 top_left_corner_size,
1537 top_left_atlas,
1538 color,
1539 current_texture_size,
1540 );
1541 quad_matrix_and_uv.push(top_left_quad);
1542
1543 let top_edge_pos = Vec3::new(
1545 position_offset.x + slices.left as i16,
1546 position_offset.y + (world_window_size.y - slices.top) as i16,
1547 0,
1548 );
1549 let top_edge_world_quad_size = UVec2::new(world_edge_width, slices.top);
1550 let top_edge_texture_size = UVec2::new(texture_edge_width, slices.top);
1551 let top_edge_atlas = URect::new(
1552 atlas_origin.x + slices.left,
1553 atlas_origin.y, top_edge_texture_size.x,
1555 top_edge_texture_size.y,
1556 );
1557 let top_edge_quad = Self::quad_helper_uniform(
1558 top_edge_pos,
1559 top_edge_world_quad_size,
1560 top_edge_atlas,
1561 color,
1562 current_texture_size,
1563 );
1564 quad_matrix_and_uv.push(top_edge_quad);
1565
1566 let top_right_pos = Vec3::new(
1568 position_offset.x + (world_window_size.x - slices.right) as i16,
1569 position_offset.y + (world_window_size.y - slices.top) as i16,
1570 0,
1571 );
1572 let top_right_corner_size = UVec2::new(slices.right, slices.top);
1573 let top_right_atlas = URect::new(
1574 atlas_origin.x + texture_window_size.x - slices.right,
1575 atlas_origin.y, top_right_corner_size.x,
1577 top_right_corner_size.y,
1578 );
1579 let top_right_quad = Self::quad_helper_uniform(
1580 top_right_pos,
1581 top_right_corner_size,
1582 top_right_atlas,
1583 color,
1584 current_texture_size,
1585 );
1586 quad_matrix_and_uv.push(top_right_quad);
1587 }
1588
1589 fn sort_and_put_in_batches(&mut self) -> Vec<Vec<&RenderItem>> {
1590 sort_render_items_by_z_and_material(&mut self.items);
1591
1592 self.order_render_items_in_batches()
1593 }
1594
1595 #[allow(clippy::too_many_lines)]
1598 pub fn render(
1599 &mut self,
1600 command_encoder: &mut CommandEncoder,
1601 display_surface_texture_view: &TextureView,
1602 textures: &Assets<Texture>,
1604 fonts: &Assets<Font>,
1605 now: Millis,
1606 ) {
1607 self.debug_tick += 1;
1608 trace!("start render()");
1609 self.last_render_at = now;
1610
1611 self.set_viewport_and_view_projection_matrix();
1612
1613 self.write_vertex_indices_and_uv_to_buffer(textures, fonts);
1614
1615 self.render_batches_to_virtual_texture(command_encoder, textures);
1616
1617 self.render_virtual_texture_to_display(command_encoder, display_surface_texture_view);
1618 }
1619
1620 pub fn set_viewport_and_view_projection_matrix(&mut self) {
1621 let view_proj_matrix = create_view_projection_matrix_from_virtual(
1622 self.virtual_surface_size.x,
1623 self.virtual_surface_size.y,
1624 );
1625
1626 let scale_matrix = Matrix4::from_scale(self.scale, self.scale, 0.0);
1627 let origin_translation_matrix =
1628 Matrix4::from_translation(f32::from(-self.origin.x), f32::from(-self.origin.y), 0.0);
1629
1630 let total_matrix = scale_matrix * view_proj_matrix * origin_translation_matrix;
1631
1632 self.queue.write_buffer(
1634 &self.camera_buffer,
1635 0,
1636 bytemuck::cast_slice(&[total_matrix]),
1637 );
1638 }
1639
1640 pub fn render_batches_to_virtual_texture(
1641 &mut self,
1642 command_encoder: &mut CommandEncoder,
1643 textures: &Assets<Texture>,
1644 ) {
1645 let mut render_pass = command_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1646 label: Some("Game Render Pass"),
1647 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1648 view: &self.virtual_surface_texture_view,
1649 depth_slice: None,
1650 resolve_target: None,
1651 ops: wgpu::Operations {
1652 load: wgpu::LoadOp::Clear(self.clear_color),
1653 store: wgpu::StoreOp::Store,
1654 },
1655 })],
1656 depth_stencil_attachment: None,
1657 timestamp_writes: None,
1658 occlusion_query_set: None,
1659 multiview_mask: None,
1660 });
1661
1662 render_pass.set_viewport(
1663 0.0,
1664 0.0,
1665 f32::from(self.virtual_surface_size.x),
1666 f32::from(self.virtual_surface_size.y),
1667 0.0,
1668 1.0,
1669 );
1670
1671 render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
1673 render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
1674
1675 render_pass.set_vertex_buffer(1, self.quad_matrix_and_uv_instance_buffer.slice(..));
1677
1678 let num_indices = mireforge_wgpu_sprites::INDICES.len() as u32;
1679
1680 let mut current_pipeline: Option<&MaterialKind> = None;
1681
1682 for &(ref weak_material_ref, start, count) in &self.batch_offsets {
1683 let wgpu_material = weak_material_ref;
1684
1685 let pipeline_kind = &wgpu_material.kind;
1686
1687 if current_pipeline != Some(pipeline_kind) {
1688 let pipeline = match pipeline_kind {
1689 MaterialKind::NormalSprite { .. } => &self.normal_sprite_pipeline.pipeline,
1690 MaterialKind::Quad => &self.quad_shader_info.pipeline,
1691 MaterialKind::AlphaMasker { .. } => &self.mask_shader_info.pipeline,
1692 MaterialKind::LightAdd { .. } => &self.light_shader_info.pipeline,
1693 };
1694 render_pass.set_pipeline(pipeline);
1696 current_pipeline = Some(pipeline_kind);
1699 render_pass.set_bind_group(0, &self.camera_bind_group, &[]);
1700 }
1701
1702 match &wgpu_material.kind {
1703 MaterialKind::NormalSprite { primary_texture }
1704 | MaterialKind::LightAdd { primary_texture } => {
1705 let texture = textures.get(primary_texture).unwrap();
1706 render_pass.set_bind_group(1, &texture.texture_and_sampler_bind_group, &[]);
1708 }
1709 MaterialKind::AlphaMasker {
1710 primary_texture,
1711 alpha_texture,
1712 } => {
1713 let real_diffuse_texture = textures.get(primary_texture).unwrap();
1714 let alpha_texture = textures.get(alpha_texture).unwrap();
1715 render_pass.set_bind_group(
1716 1,
1717 &real_diffuse_texture.texture_and_sampler_bind_group,
1718 &[],
1719 );
1720 render_pass.set_bind_group(
1721 2,
1722 &alpha_texture.texture_and_sampler_bind_group,
1723 &[],
1724 );
1725 }
1726 MaterialKind::Quad => {
1727 }
1729 }
1730 assert!(
1731 count <= MAXIMUM_QUADS_IN_A_BATCH as u32,
1732 "too many instanced draw in a batch {count}"
1733 );
1734
1735 trace!(material=%weak_material_ref, start=%start, count=%count, %num_indices, "draw instanced");
1737 render_pass.draw_indexed(0..num_indices, 0, start..(start + count));
1738 }
1739 self.items.clear();
1740 }
1741
1742 pub fn render_virtual_texture_to_display(
1743 &mut self,
1744 command_encoder: &mut CommandEncoder,
1745 display_surface_texture_view: &TextureView,
1746 ) {
1747 let mut render_pass = command_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1748 label: Some("Screen Render Pass"),
1749 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1750 view: display_surface_texture_view,
1751 depth_slice: None,
1752 resolve_target: None,
1753 ops: wgpu::Operations {
1754 load: wgpu::LoadOp::Clear(self.screen_clear_color),
1755 store: wgpu::StoreOp::Store,
1756 },
1757 })],
1758 depth_stencil_attachment: None,
1759 timestamp_writes: None,
1760 occlusion_query_set: None,
1761 multiview_mask: None,
1762 });
1763
1764 self.viewport = match self.viewport_strategy {
1776 ViewportStrategy::FitIntegerScaling => Self::viewport_from_integer_scale(
1777 self.physical_surface_size,
1778 self.virtual_surface_size,
1779 ),
1780 ViewportStrategy::FitFloatScaling => Self::viewport_from_float_scale(
1781 self.physical_surface_size,
1782 self.virtual_surface_size,
1783 ),
1784 ViewportStrategy::MatchPhysicalSize => URect::new(
1785 0,
1786 0,
1787 self.physical_surface_size.x,
1788 self.physical_surface_size.y,
1789 ),
1790 };
1791
1792 render_pass.set_viewport(
1793 f32::from(self.viewport.position.x),
1794 f32::from(self.viewport.position.y),
1795 f32::from(self.viewport.size.x),
1796 f32::from(self.viewport.size.y),
1797 0.0,
1798 1.0,
1799 );
1800
1801 render_pass.set_pipeline(&self.virtual_to_screen_shader_info.pipeline);
1803 render_pass.set_bind_group(0, &self.virtual_to_surface_bind_group, &[]);
1804 render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
1805
1806 render_pass.draw(0..6, 0..1);
1807 }
1808
1809 pub fn texture_resource_from_texture(&self, texture: &wgpu::Texture, label: &str) -> Texture {
1810 trace!("load texture from memory with name: '{label}'");
1811 let size = &texture.size();
1812 let texture_and_sampler_bind_group =
1813 mireforge_wgpu_sprites::create_sprite_texture_and_sampler_bind_group(
1814 &self.device,
1815 &self.texture_sampler_bind_group_layout,
1816 texture,
1817 &self.sampler,
1818 label,
1819 );
1820
1821 let texture_size = UVec2::new(size.width as u16, size.height as u16);
1822
1823 Texture {
1824 texture_and_sampler_bind_group,
1825 texture_size,
1826 }
1827 }
1828}
1829
1830fn create_view_projection_matrix_from_virtual(virtual_width: u16, virtual_height: u16) -> Matrix4 {
1831 let (bottom, top) = (0.0, f32::from(virtual_height));
1832
1833 let (near, far) =
1836 (-1.0, 1.0)
1838 ;
1839
1840 OrthoInfo {
1841 left: 0.0,
1842 right: f32::from(virtual_width),
1843 bottom,
1844 top,
1845 near, far, }
1848 .into()
1849}
1850
1851fn create_view_uniform_view_projection_matrix(viewport_size: UVec2) -> Matrix4 {
1852 let viewport_width = f32::from(viewport_size.x);
1853 let viewport_height = f32::from(viewport_size.y);
1854
1855 let viewport_aspect_ratio = viewport_width / viewport_height;
1856
1857 let scale_x = 1.0;
1858 let scale_y = viewport_aspect_ratio;
1859
1860 let view_projection_matrix = [
1861 [scale_x, 0.0, 0.0, 0.0],
1862 [0.0, scale_y, 0.0, 0.0],
1863 [0.0, 0.0, -1.0, 0.0],
1864 [0.0, 0.0, 0.0, 1.0],
1865 ];
1866
1867 view_projection_matrix.into()
1868}
1869
1870fn sort_render_items_by_z_and_material(items: &mut [RenderItem]) {
1871 items.sort_by_key(|item| (item.position.z, item.material_ref.clone()));
1872}
1873
1874#[derive(Debug, Clone, Copy, Default)]
1875pub enum Rotation {
1876 #[default]
1877 Degrees0,
1878 Degrees90,
1879 Degrees180,
1880 Degrees270,
1881}
1882
1883#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1884pub enum Anchor {
1885 LowerLeft,
1886 UpperLeft,
1887}
1888
1889#[derive(Debug, Copy, Clone)]
1890pub struct SpriteParams {
1891 pub texture_size: UVec2,
1892 pub texture_pos: UVec2,
1893 pub scale: u8,
1894 pub rotation: Rotation,
1895 pub flip_x: bool,
1896 pub flip_y: bool,
1897 pub pivot: Vec2,
1898 pub color: Color,
1899 pub anchor: Anchor,
1900}
1901
1902impl Default for SpriteParams {
1903 fn default() -> Self {
1904 Self {
1905 texture_size: UVec2::new(0, 0),
1906 texture_pos: UVec2::new(0, 0),
1907 pivot: Vec2::new(0, 0),
1908 flip_x: false,
1909 flip_y: false,
1910 color: Color::from_octet(255, 255, 255, 255),
1911 scale: 1,
1912 rotation: Rotation::Degrees0,
1913 anchor: Anchor::LowerLeft,
1914 }
1915 }
1916}
1917
1918#[derive(Debug, Copy, Clone)]
1919pub struct QuadParams {
1920 pub scale: u8,
1921 pub pivot: Vec2,
1922}
1923
1924impl Default for QuadParams {
1925 fn default() -> Self {
1926 Self {
1927 pivot: Vec2::new(0, 0),
1928 scale: 1,
1929 }
1930 }
1931}
1932
1933pub type BindGroupRef = Arc<BindGroup>;
1934
1935#[derive(Debug, PartialEq, Eq, Asset)]
1936pub struct Texture {
1937 pub texture_and_sampler_bind_group: BindGroup,
1938 pub texture_size: UVec2,
1940}
1941
1942impl Display for Texture {
1943 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
1944 write!(f, "{:?}", self.texture_size)
1945 }
1946}
1947
1948impl PartialOrd<Self> for Texture {
1949 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1950 Some(
1951 self.texture_and_sampler_bind_group
1952 .cmp(&other.texture_and_sampler_bind_group),
1953 )
1954 }
1955}
1956
1957impl Ord for Texture {
1958 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1959 self.texture_and_sampler_bind_group
1960 .cmp(&other.texture_and_sampler_bind_group)
1961 }
1962}
1963
1964#[derive(Debug, Ord, PartialOrd, PartialEq, Eq)]
1965pub struct MaterialBase {
1966 }
1968
1969#[derive(Debug, Ord, PartialOrd, PartialEq, Eq)]
1970pub struct Material {
1971 pub base: MaterialBase,
1972 pub kind: MaterialKind,
1973}
1974
1975impl Material {
1976 #[inline]
1977 #[must_use]
1978 pub fn primary_texture(&self) -> Option<TextureRef> {
1979 self.kind.primary_texture()
1980 }
1981
1982 #[inline]
1983 #[must_use]
1984 pub fn is_complete(&self, textures: &Assets<Texture>) -> bool {
1985 self.kind.is_complete(textures)
1986 }
1987}
1988
1989impl Display for Material {
1990 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1991 write!(f, "{}", self.kind)
1992 }
1993}
1994
1995#[derive(Debug, Ord, PartialOrd, PartialEq, Eq)]
1996pub enum MaterialKind {
1997 NormalSprite {
1998 primary_texture: Id<Texture>,
1999 },
2000 AlphaMasker {
2001 primary_texture: Id<Texture>,
2002 alpha_texture: Id<Texture>,
2003 },
2004 Quad,
2005 LightAdd {
2006 primary_texture: Id<Texture>,
2007 },
2008}
2009
2010impl MaterialKind {}
2011
2012impl MaterialKind {
2013 #[must_use]
2014 pub fn primary_texture(&self) -> Option<Id<Texture>> {
2015 match &self {
2016 Self::NormalSprite {
2017 primary_texture, ..
2018 }
2019 | Self::LightAdd { primary_texture }
2020 | Self::AlphaMasker {
2021 primary_texture, ..
2022 } => Some(primary_texture.clone()),
2023 Self::Quad => None,
2024 }
2025 }
2026
2027 pub(crate) fn is_complete(&self, textures: &Assets<Texture>) -> bool {
2028 match &self {
2029 Self::NormalSprite { primary_texture } | Self::LightAdd { primary_texture } => {
2030 textures.contains(primary_texture)
2031 }
2032 Self::AlphaMasker {
2033 primary_texture,
2034 alpha_texture,
2035 } => textures.contains(primary_texture) && textures.contains(alpha_texture),
2036 Self::Quad => true,
2037 }
2038 }
2039}
2040
2041impl Display for MaterialKind {
2042 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2043 let texture_name = self
2044 .primary_texture()
2045 .map_or_else(String::new, |x| x.to_string());
2046
2047 let kind_name = match self {
2048 Self::NormalSprite { .. } => "NormalSprite",
2049 Self::LightAdd { .. } => "Light (Add)",
2050 Self::Quad => "Quad",
2051 Self::AlphaMasker { .. } => "AlphaMasker",
2052 };
2053
2054 write!(f, "{kind_name} texture {texture_name}")
2055 }
2056}
2057
2058#[derive(Debug)]
2059pub struct Sprite {
2060 pub params: SpriteParams,
2061}
2062
2063#[derive(Debug)]
2064pub struct QuadColor {
2065 pub size: UVec2,
2066 pub color: Color,
2067 pub params: QuadParams,
2068}
2069
2070#[derive(Debug, Copy, Clone)]
2071pub struct Slices {
2072 pub left: u16,
2073 pub top: u16,
2074 pub right: u16, pub bottom: u16, }
2077
2078#[derive(Debug)]
2079pub struct NineSlice {
2080 pub size: UVec2, pub slices: Slices,
2082 pub color: Color, pub origin_in_atlas: UVec2,
2084 pub size_inside_atlas: Option<UVec2>,
2085}
2086
2087#[derive(Debug)]
2088pub struct TileMap {
2089 pub tiles_data_grid_size: UVec2,
2090 pub cell_count_size: UVec2,
2091 pub one_cell_size: UVec2,
2092 pub tiles: Vec<u16>,
2093 pub scale: u8,
2094}
2095
2096#[derive(PartialEq, Debug, Eq, Ord, PartialOrd)]
2097pub struct Pipeline {
2098 name: String,
2099 render_pipeline: RenderPipeline,
2100}
2101
2102impl Display for Pipeline {
2103 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2104 write!(f, "pipeline: {}", self.name)
2105 }
2106}
2107
2108pub type PipelineRef = Arc<Pipeline>;