1mod gfx;
6mod gfx_impl;
7pub mod plugin;
8pub mod prelude;
9
10use int_math::{URect, UVec2, Vec2, Vec3};
11use limnus_assets::prelude::{Asset, Id, WeakId};
12use limnus_assets::Assets;
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 create_texture_and_sampler_bind_group_ex, create_texture_and_sampler_group_layout, ShaderInfo, SpriteInfo,
22 SpriteInstanceUniform,
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 =
1189 if x == repeat_x_count - 1 && world_edge_width % texture_edge_width != 0 {
1190 world_edge_width % texture_edge_width
1191 } else {
1192 texture_edge_width
1193 };
1194
1195 let this_quad_height =
1196 if y == repeat_y_count - 1 && world_edge_height % texture_edge_height != 0 {
1197 world_edge_height % texture_edge_height
1198 } else {
1199 texture_edge_height
1200 };
1201
1202 let quad_pos = Vec3::new(
1203 position_offset.x + slices.left as i16 + (x as u16 * texture_edge_width) as i16,
1204 position_offset.y
1205 + slices.bottom as i16
1206 + (y as u16 * texture_edge_height) as i16,
1207 0,
1208 );
1209
1210 let texture_x = base_center_x;
1211
1212 let texture_y = if y == repeat_y_count - 1 && this_quad_height < texture_edge_height
1213 {
1214 base_center_y + (texture_edge_height - this_quad_height)
1215 } else {
1216 base_center_y
1217 };
1218
1219 let this_texture_region =
1220 URect::new(texture_x, texture_y, this_quad_width, this_quad_height);
1221
1222 let center_quad = Self::quad_helper_uniform(
1223 quad_pos,
1224 UVec2::new(this_quad_width, this_quad_height),
1225 this_texture_region,
1226 color,
1227 current_texture_size,
1228 );
1229
1230 quad_matrix_and_uv.push(center_quad);
1231 }
1232 }
1233 let right_edge_pos = Vec3::new(
1237 position_offset.x + (world_window_size.x - slices.right) as i16,
1238 position_offset.y + slices.bottom as i16,
1239 0,
1240 );
1241 let right_edge_world_quad_size = UVec2::new(slices.right, world_edge_height);
1242 let right_edge_texture_size = UVec2::new(slices.right, texture_edge_height);
1243 let right_edge_atlas = URect::new(
1244 atlas_origin.x + texture_window_size.x - slices.right,
1245 atlas_origin.y + slices.top, right_edge_texture_size.x,
1247 right_edge_texture_size.y,
1248 );
1249
1250 let right_edge_quad = Self::quad_helper_uniform(
1251 right_edge_pos,
1252 right_edge_world_quad_size,
1253 right_edge_atlas,
1254 color,
1255 current_texture_size,
1256 );
1257 quad_matrix_and_uv.push(right_edge_quad);
1258
1259 let top_left_pos = Vec3::new(
1261 position_offset.x,
1262 position_offset.y + (world_window_size.y - slices.top) as i16,
1263 0,
1264 );
1265 let top_left_corner_size = UVec2::new(slices.left, slices.top);
1266 let top_left_atlas = URect::new(
1267 atlas_origin.x,
1268 atlas_origin.y, top_left_corner_size.x,
1270 top_left_corner_size.y,
1271 );
1272 let top_left_quad = Self::quad_helper_uniform(
1273 top_left_pos,
1274 top_left_corner_size,
1275 top_left_atlas,
1276 color,
1277 current_texture_size,
1278 );
1279 quad_matrix_and_uv.push(top_left_quad);
1280
1281 let top_edge_pos = Vec3::new(
1283 position_offset.x + slices.left as i16,
1284 position_offset.y + (world_window_size.y - slices.top) as i16,
1285 0,
1286 );
1287 let top_edge_world_quad_size = UVec2::new(world_edge_width, slices.top);
1288 let top_edge_texture_size = UVec2::new(texture_edge_width, slices.top);
1289 let top_edge_atlas = URect::new(
1290 atlas_origin.x + slices.left,
1291 atlas_origin.y, top_edge_texture_size.x,
1293 top_edge_texture_size.y,
1294 );
1295 let top_edge_quad = Self::quad_helper_uniform(
1296 top_edge_pos,
1297 top_edge_world_quad_size,
1298 top_edge_atlas,
1299 color,
1300 current_texture_size,
1301 );
1302 quad_matrix_and_uv.push(top_edge_quad);
1303
1304 let top_right_pos = Vec3::new(
1306 position_offset.x + (world_window_size.x - slices.right) as i16,
1307 position_offset.y + (world_window_size.y - slices.top) as i16,
1308 0,
1309 );
1310 let top_right_corner_size = UVec2::new(slices.right, slices.top);
1311 let top_right_atlas = URect::new(
1312 atlas_origin.x + texture_window_size.x - slices.right,
1313 atlas_origin.y, top_right_corner_size.x,
1315 top_right_corner_size.y,
1316 );
1317 let top_right_quad = Self::quad_helper_uniform(
1318 top_right_pos,
1319 top_right_corner_size,
1320 top_right_atlas,
1321 color,
1322 current_texture_size,
1323 );
1324 quad_matrix_and_uv.push(top_right_quad);
1325 }
1326
1327 #[allow(clippy::too_many_lines)]
1328 #[inline]
1329 pub fn prepare_nine_slice_single_center_quad(
1330 nine_slice: &NineSlice,
1331 position_offset: Vec3,
1332 quad_matrix_and_uv: &mut Vec<SpriteInstanceUniform>,
1333 current_texture_size: UVec2,
1334 ) {
1335 let world_window_size = nine_slice.size;
1336 let slices = &nine_slice.slices;
1337
1338 assert!(
1343 world_window_size.x >= slices.left + slices.right,
1344 "NineSlice.width ({}) < slices.left + slices.right ({})",
1345 world_window_size.x,
1346 slices.left + slices.right
1347 );
1348 assert!(
1349 world_window_size.y >= slices.top + slices.bottom,
1350 "NineSlice.height ({}) < slices.top + slices.bottom ({})",
1351 world_window_size.y,
1352 slices.top + slices.bottom
1353 );
1354
1355 let texture_window_size = nine_slice.size_inside_atlas.unwrap_or(current_texture_size);
1356
1357 assert!(
1359 texture_window_size.x >= slices.left + slices.right,
1360 "texture_window_size.width ({}) < slices.left + slices.right ({})",
1361 texture_window_size.x,
1362 slices.left + slices.right
1363 );
1364 assert!(
1365 texture_window_size.y >= slices.top + slices.bottom,
1366 "texture_window_size.height ({}) < slices.top + slices.bottom ({})",
1367 texture_window_size.y,
1368 slices.top + slices.bottom
1369 );
1370 let color = nine_slice.color;
1373
1374 let atlas_origin = nine_slice.origin_in_atlas;
1375 let texture_window_size = nine_slice.size_inside_atlas.unwrap_or(current_texture_size);
1376
1377 let world_edge_width = nine_slice.size.x - slices.left - slices.right;
1378 let world_edge_height = nine_slice.size.y - slices.top - slices.bottom;
1379 let texture_edge_width = texture_window_size.x - slices.left - slices.right;
1380 let texture_edge_height = texture_window_size.y - slices.top - slices.bottom;
1381
1382 let lower_left_pos = Vec3::new(position_offset.x, position_offset.y, 0);
1385 let corner_size = UVec2::new(slices.left, slices.bottom);
1386 let lower_left_quad_size = UVec2::new(corner_size.x, corner_size.y);
1388 let lower_left_atlas = URect::new(
1389 atlas_origin.x,
1390 atlas_origin.y + texture_window_size.y - slices.bottom, corner_size.x,
1392 corner_size.y,
1393 );
1394 let lower_left_quad = Self::quad_helper_uniform(
1395 lower_left_pos,
1396 lower_left_quad_size,
1397 lower_left_atlas,
1398 color,
1399 current_texture_size,
1400 );
1401 quad_matrix_and_uv.push(lower_left_quad);
1402
1403 let lower_side_position =
1405 Vec3::new(position_offset.x + slices.left as i16, position_offset.y, 0);
1406 let lower_side_world_quad_size = UVec2::new(world_edge_width, slices.bottom);
1409 let lower_side_texture_size = UVec2::new(texture_edge_width, slices.bottom);
1410 let lower_side_atlas = URect::new(
1412 atlas_origin.x + slices.left,
1413 atlas_origin.y + texture_window_size.y - slices.bottom, lower_side_texture_size.x,
1415 lower_side_texture_size.y,
1416 );
1417 let lower_side_quad = Self::quad_helper_uniform(
1418 lower_side_position,
1419 lower_side_world_quad_size,
1420 lower_side_atlas,
1421 color,
1422 current_texture_size,
1423 );
1424 quad_matrix_and_uv.push(lower_side_quad);
1425
1426 let lower_right_pos = Vec3::new(
1428 position_offset.x + (world_window_size.x - slices.right) as i16,
1429 position_offset.y,
1430 0,
1431 );
1432 let lower_right_corner_size = UVec2::new(slices.right, slices.bottom);
1433 let lower_right_atlas = URect::new(
1434 atlas_origin.x + texture_window_size.x - slices.right,
1435 atlas_origin.y + texture_window_size.y - slices.bottom, lower_right_corner_size.x,
1437 lower_right_corner_size.y,
1438 );
1439 let lower_right_quad = Self::quad_helper_uniform(
1440 lower_right_pos,
1441 lower_right_corner_size,
1442 lower_right_atlas,
1443 color,
1444 current_texture_size,
1445 );
1446 quad_matrix_and_uv.push(lower_right_quad);
1447
1448 let left_edge_pos = Vec3::new(
1450 position_offset.x,
1451 position_offset.y + slices.bottom as i16,
1452 0,
1453 );
1454 let left_edge_world_quad_size = UVec2::new(slices.left, world_edge_height);
1455 let left_edge_texture_size = UVec2::new(slices.left, texture_edge_height);
1456 let left_edge_atlas = URect::new(
1457 atlas_origin.x,
1458 atlas_origin.y + slices.top, left_edge_texture_size.x,
1460 left_edge_texture_size.y,
1461 );
1462 let left_edge_quad = Self::quad_helper_uniform(
1463 left_edge_pos,
1464 left_edge_world_quad_size,
1465 left_edge_atlas,
1466 color,
1467 current_texture_size,
1468 );
1469 quad_matrix_and_uv.push(left_edge_quad);
1470
1471 let center_pos = Vec3::new(
1473 position_offset.x + slices.left as i16,
1474 position_offset.y + slices.bottom as i16,
1475 0,
1476 );
1477 let center_world_size = UVec2::new(world_edge_width, world_edge_height);
1478 let center_atlas = URect::new(
1481 atlas_origin.x + slices.left,
1482 atlas_origin.y + slices.top,
1483 texture_edge_width,
1484 texture_edge_height,
1485 );
1486 let center_quad = Self::quad_helper_uniform(
1487 center_pos,
1488 center_world_size,
1489 center_atlas,
1490 color,
1491 current_texture_size,
1492 );
1493 quad_matrix_and_uv.push(center_quad);
1494
1495 let right_edge_pos = Vec3::new(
1497 position_offset.x + (world_window_size.x - slices.right) as i16,
1498 position_offset.y + slices.bottom as i16,
1499 0,
1500 );
1501 let right_edge_world_quad_size = UVec2::new(slices.right, world_edge_height);
1502 let right_edge_texture_size = UVec2::new(slices.right, texture_edge_height);
1503 let right_edge_atlas = URect::new(
1504 atlas_origin.x + texture_window_size.x - slices.right,
1505 atlas_origin.y + slices.top, right_edge_texture_size.x,
1507 right_edge_texture_size.y,
1508 );
1509
1510 let right_edge_quad = Self::quad_helper_uniform(
1511 right_edge_pos,
1512 right_edge_world_quad_size,
1513 right_edge_atlas,
1514 color,
1515 current_texture_size,
1516 );
1517 quad_matrix_and_uv.push(right_edge_quad);
1518
1519 let top_left_pos = Vec3::new(
1521 position_offset.x,
1522 position_offset.y + (world_window_size.y - slices.top) as i16,
1523 0,
1524 );
1525 let top_left_corner_size = UVec2::new(slices.left, slices.top);
1526 let top_left_atlas = URect::new(
1527 atlas_origin.x,
1528 atlas_origin.y, top_left_corner_size.x,
1530 top_left_corner_size.y,
1531 );
1532 let top_left_quad = Self::quad_helper_uniform(
1533 top_left_pos,
1534 top_left_corner_size,
1535 top_left_atlas,
1536 color,
1537 current_texture_size,
1538 );
1539 quad_matrix_and_uv.push(top_left_quad);
1540
1541 let top_edge_pos = Vec3::new(
1543 position_offset.x + slices.left as i16,
1544 position_offset.y + (world_window_size.y - slices.top) as i16,
1545 0,
1546 );
1547 let top_edge_world_quad_size = UVec2::new(world_edge_width, slices.top);
1548 let top_edge_texture_size = UVec2::new(texture_edge_width, slices.top);
1549 let top_edge_atlas = URect::new(
1550 atlas_origin.x + slices.left,
1551 atlas_origin.y, top_edge_texture_size.x,
1553 top_edge_texture_size.y,
1554 );
1555 let top_edge_quad = Self::quad_helper_uniform(
1556 top_edge_pos,
1557 top_edge_world_quad_size,
1558 top_edge_atlas,
1559 color,
1560 current_texture_size,
1561 );
1562 quad_matrix_and_uv.push(top_edge_quad);
1563
1564 let top_right_pos = Vec3::new(
1566 position_offset.x + (world_window_size.x - slices.right) as i16,
1567 position_offset.y + (world_window_size.y - slices.top) as i16,
1568 0,
1569 );
1570 let top_right_corner_size = UVec2::new(slices.right, slices.top);
1571 let top_right_atlas = URect::new(
1572 atlas_origin.x + texture_window_size.x - slices.right,
1573 atlas_origin.y, top_right_corner_size.x,
1575 top_right_corner_size.y,
1576 );
1577 let top_right_quad = Self::quad_helper_uniform(
1578 top_right_pos,
1579 top_right_corner_size,
1580 top_right_atlas,
1581 color,
1582 current_texture_size,
1583 );
1584 quad_matrix_and_uv.push(top_right_quad);
1585 }
1586
1587 fn sort_and_put_in_batches(&mut self) -> Vec<Vec<&RenderItem>> {
1588 sort_render_items_by_z_and_material(&mut self.items);
1589
1590 self.order_render_items_in_batches()
1591 }
1592
1593 #[allow(clippy::too_many_lines)]
1596 pub fn render(
1597 &mut self,
1598 command_encoder: &mut CommandEncoder,
1599 display_surface_texture_view: &TextureView,
1600 textures: &Assets<Texture>,
1602 fonts: &Assets<Font>,
1603 now: Millis,
1604 ) {
1605 self.debug_tick += 1;
1606 trace!("start render()");
1607 self.last_render_at = now;
1608
1609 self.set_viewport_and_view_projection_matrix();
1610
1611 self.write_vertex_indices_and_uv_to_buffer(textures, fonts);
1612
1613 self.render_batches_to_virtual_texture(command_encoder, textures);
1614
1615 self.render_virtual_texture_to_display(command_encoder, display_surface_texture_view);
1616 }
1617
1618 pub fn set_viewport_and_view_projection_matrix(&mut self) {
1619 let view_proj_matrix = create_view_projection_matrix_from_virtual(
1620 self.virtual_surface_size.x,
1621 self.virtual_surface_size.y,
1622 );
1623
1624 let scale_matrix = Matrix4::from_scale(self.scale, self.scale, 0.0);
1625 let origin_translation_matrix =
1626 Matrix4::from_translation(f32::from(-self.origin.x), f32::from(-self.origin.y), 0.0);
1627
1628 let total_matrix = scale_matrix * view_proj_matrix * origin_translation_matrix;
1629
1630 self.queue.write_buffer(
1632 &self.camera_buffer,
1633 0,
1634 bytemuck::cast_slice(&[total_matrix]),
1635 );
1636 }
1637
1638 pub fn render_batches_to_virtual_texture(
1639 &mut self,
1640 command_encoder: &mut CommandEncoder,
1641 textures: &Assets<Texture>,
1642 ) {
1643 let mut render_pass = command_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1644 label: Some("Game Render Pass"),
1645 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1646 view: &self.virtual_surface_texture_view,
1647 depth_slice: None,
1648 resolve_target: None,
1649 ops: wgpu::Operations {
1650 load: wgpu::LoadOp::Clear(self.clear_color),
1651 store: wgpu::StoreOp::Store,
1652 },
1653 })],
1654 depth_stencil_attachment: None,
1655 timestamp_writes: None,
1656 occlusion_query_set: None,
1657 });
1658
1659 render_pass.set_viewport(
1660 0.0,
1661 0.0,
1662 f32::from(self.virtual_surface_size.x),
1663 f32::from(self.virtual_surface_size.y),
1664 0.0,
1665 1.0,
1666 );
1667
1668 render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
1670 render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
1671
1672 render_pass.set_vertex_buffer(1, self.quad_matrix_and_uv_instance_buffer.slice(..));
1674
1675 let num_indices = mireforge_wgpu_sprites::INDICES.len() as u32;
1676
1677 let mut current_pipeline: Option<&MaterialKind> = None;
1678
1679 for &(ref weak_material_ref, start, count) in &self.batch_offsets {
1680 let wgpu_material = weak_material_ref;
1681
1682 let pipeline_kind = &wgpu_material.kind;
1683
1684 if current_pipeline != Some(pipeline_kind) {
1685 let pipeline = match pipeline_kind {
1686 MaterialKind::NormalSprite { .. } => &self.normal_sprite_pipeline.pipeline,
1687 MaterialKind::Quad => &self.quad_shader_info.pipeline,
1688 MaterialKind::AlphaMasker { .. } => &self.mask_shader_info.pipeline,
1689 MaterialKind::LightAdd { .. } => &self.light_shader_info.pipeline,
1690 };
1691 render_pass.set_pipeline(pipeline);
1693 current_pipeline = Some(pipeline_kind);
1696 render_pass.set_bind_group(0, &self.camera_bind_group, &[]);
1697 }
1698
1699 match &wgpu_material.kind {
1700 MaterialKind::NormalSprite { primary_texture }
1701 | MaterialKind::LightAdd { primary_texture } => {
1702 let texture = textures.get(primary_texture).unwrap();
1703 render_pass.set_bind_group(1, &texture.texture_and_sampler_bind_group, &[]);
1705 }
1706 MaterialKind::AlphaMasker {
1707 primary_texture,
1708 alpha_texture,
1709 } => {
1710 let real_diffuse_texture = textures.get(primary_texture).unwrap();
1711 let alpha_texture = textures.get(alpha_texture).unwrap();
1712 render_pass.set_bind_group(
1713 1,
1714 &real_diffuse_texture.texture_and_sampler_bind_group,
1715 &[],
1716 );
1717 render_pass.set_bind_group(
1718 2,
1719 &alpha_texture.texture_and_sampler_bind_group,
1720 &[],
1721 );
1722 }
1723 MaterialKind::Quad => {
1724 }
1726 }
1727 assert!(
1728 count <= MAXIMUM_QUADS_IN_A_BATCH as u32,
1729 "too many instanced draw in a batch {count}"
1730 );
1731
1732 trace!(material=%weak_material_ref, start=%start, count=%count, %num_indices, "draw instanced");
1734 render_pass.draw_indexed(0..num_indices, 0, start..(start + count));
1735 }
1736 self.items.clear();
1737 }
1738
1739 pub fn render_virtual_texture_to_display(
1740 &mut self,
1741 command_encoder: &mut CommandEncoder,
1742 display_surface_texture_view: &TextureView,
1743 ) {
1744 let mut render_pass = command_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1745 label: Some("Screen Render Pass"),
1746 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1747 view: display_surface_texture_view,
1748 depth_slice: None,
1749 resolve_target: None,
1750 ops: wgpu::Operations {
1751 load: wgpu::LoadOp::Clear(self.screen_clear_color),
1752 store: wgpu::StoreOp::Store,
1753 },
1754 })],
1755 depth_stencil_attachment: None,
1756 timestamp_writes: None,
1757 occlusion_query_set: None,
1758 });
1759
1760 self.viewport = match self.viewport_strategy {
1772 ViewportStrategy::FitIntegerScaling => Self::viewport_from_integer_scale(
1773 self.physical_surface_size,
1774 self.virtual_surface_size,
1775 ),
1776 ViewportStrategy::FitFloatScaling => Self::viewport_from_float_scale(
1777 self.physical_surface_size,
1778 self.virtual_surface_size,
1779 ),
1780 ViewportStrategy::MatchPhysicalSize => URect::new(
1781 0,
1782 0,
1783 self.physical_surface_size.x,
1784 self.physical_surface_size.y,
1785 ),
1786 };
1787
1788 render_pass.set_viewport(
1789 f32::from(self.viewport.position.x),
1790 f32::from(self.viewport.position.y),
1791 f32::from(self.viewport.size.x),
1792 f32::from(self.viewport.size.y),
1793 0.0,
1794 1.0,
1795 );
1796
1797 render_pass.set_pipeline(&self.virtual_to_screen_shader_info.pipeline);
1799 render_pass.set_bind_group(0, &self.virtual_to_surface_bind_group, &[]);
1800 render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
1801
1802 render_pass.draw(0..6, 0..1);
1803 }
1804
1805 pub fn texture_resource_from_texture(&self, texture: &wgpu::Texture, label: &str) -> Texture {
1806 trace!("load texture from memory with name: '{label}'");
1807 let size = &texture.size();
1808 let texture_and_sampler_bind_group =
1809 mireforge_wgpu_sprites::create_sprite_texture_and_sampler_bind_group(
1810 &self.device,
1811 &self.texture_sampler_bind_group_layout,
1812 texture,
1813 &self.sampler,
1814 label,
1815 );
1816
1817 let texture_size = UVec2::new(size.width as u16, size.height as u16);
1818
1819 Texture {
1820 texture_and_sampler_bind_group,
1821 texture_size,
1822 }
1823 }
1824}
1825
1826fn create_view_projection_matrix_from_virtual(virtual_width: u16, virtual_height: u16) -> Matrix4 {
1827 let (bottom, top) = (0.0, f32::from(virtual_height));
1828
1829 let (near, far) =
1832 (-1.0, 1.0)
1834 ;
1835
1836 OrthoInfo {
1837 left: 0.0,
1838 right: f32::from(virtual_width),
1839 bottom,
1840 top,
1841 near, far, }
1844 .into()
1845}
1846
1847fn create_view_uniform_view_projection_matrix(viewport_size: UVec2) -> Matrix4 {
1848 let viewport_width = f32::from(viewport_size.x);
1849 let viewport_height = f32::from(viewport_size.y);
1850
1851 let viewport_aspect_ratio = viewport_width / viewport_height;
1852
1853 let scale_x = 1.0;
1854 let scale_y = viewport_aspect_ratio;
1855
1856 let view_projection_matrix = [
1857 [scale_x, 0.0, 0.0, 0.0],
1858 [0.0, scale_y, 0.0, 0.0],
1859 [0.0, 0.0, -1.0, 0.0],
1860 [0.0, 0.0, 0.0, 1.0],
1861 ];
1862
1863 view_projection_matrix.into()
1864}
1865
1866fn sort_render_items_by_z_and_material(items: &mut [RenderItem]) {
1867 items.sort_by_key(|item| (item.position.z, item.material_ref.clone()));
1868}
1869
1870#[derive(Debug, Clone, Copy, Default)]
1871pub enum Rotation {
1872 #[default]
1873 Degrees0,
1874 Degrees90,
1875 Degrees180,
1876 Degrees270,
1877}
1878
1879#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1880pub enum Anchor {
1881 LowerLeft,
1882 UpperLeft,
1883}
1884
1885#[derive(Debug, Copy, Clone)]
1886pub struct SpriteParams {
1887 pub texture_size: UVec2,
1888 pub texture_pos: UVec2,
1889 pub scale: u8,
1890 pub rotation: Rotation,
1891 pub flip_x: bool,
1892 pub flip_y: bool,
1893 pub pivot: Vec2,
1894 pub color: Color,
1895 pub anchor: Anchor,
1896}
1897
1898impl Default for SpriteParams {
1899 fn default() -> Self {
1900 Self {
1901 texture_size: UVec2::new(0, 0),
1902 texture_pos: UVec2::new(0, 0),
1903 pivot: Vec2::new(0, 0),
1904 flip_x: false,
1905 flip_y: false,
1906 color: Color::from_octet(255, 255, 255, 255),
1907 scale: 1,
1908 rotation: Rotation::Degrees0,
1909 anchor: Anchor::LowerLeft,
1910 }
1911 }
1912}
1913
1914#[derive(Debug, Copy, Clone)]
1915pub struct QuadParams {
1916 pub scale: u8,
1917 pub pivot: Vec2,
1918}
1919
1920impl Default for QuadParams {
1921 fn default() -> Self {
1922 Self {
1923 pivot: Vec2::new(0, 0),
1924 scale: 1,
1925 }
1926 }
1927}
1928
1929pub type BindGroupRef = Arc<BindGroup>;
1930
1931#[derive(Debug, PartialEq, Eq, Asset)]
1932pub struct Texture {
1933 pub texture_and_sampler_bind_group: BindGroup,
1934 pub texture_size: UVec2,
1936}
1937
1938impl Display for Texture {
1939 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
1940 write!(f, "{:?}", self.texture_size)
1941 }
1942}
1943
1944impl PartialOrd<Self> for Texture {
1945 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1946 Some(
1947 self.texture_and_sampler_bind_group
1948 .cmp(&other.texture_and_sampler_bind_group),
1949 )
1950 }
1951}
1952
1953impl Ord for Texture {
1954 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1955 self.texture_and_sampler_bind_group
1956 .cmp(&other.texture_and_sampler_bind_group)
1957 }
1958}
1959
1960#[derive(Debug, Ord, PartialOrd, PartialEq, Eq)]
1961pub struct MaterialBase {
1962 }
1964
1965#[derive(Debug, Ord, PartialOrd, PartialEq, Eq)]
1966pub struct Material {
1967 pub base: MaterialBase,
1968 pub kind: MaterialKind,
1969}
1970
1971impl Material {
1972 #[inline]
1973 #[must_use]
1974 pub fn primary_texture(&self) -> Option<TextureRef> {
1975 self.kind.primary_texture()
1976 }
1977
1978 #[inline]
1979 #[must_use]
1980 pub fn is_complete(&self, textures: &Assets<Texture>) -> bool {
1981 self.kind.is_complete(textures)
1982 }
1983}
1984
1985impl Display for Material {
1986 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1987 write!(f, "{}", self.kind)
1988 }
1989}
1990
1991#[derive(Debug, Ord, PartialOrd, PartialEq, Eq)]
1992pub enum MaterialKind {
1993 NormalSprite {
1994 primary_texture: Id<Texture>,
1995 },
1996 AlphaMasker {
1997 primary_texture: Id<Texture>,
1998 alpha_texture: Id<Texture>,
1999 },
2000 Quad,
2001 LightAdd {
2002 primary_texture: Id<Texture>,
2003 },
2004}
2005
2006impl MaterialKind {}
2007
2008impl MaterialKind {
2009 #[must_use]
2010 pub fn primary_texture(&self) -> Option<Id<Texture>> {
2011 match &self {
2012 Self::NormalSprite {
2013 primary_texture, ..
2014 }
2015 | Self::LightAdd { primary_texture }
2016 | Self::AlphaMasker {
2017 primary_texture, ..
2018 } => Some(primary_texture.clone()),
2019 Self::Quad => None,
2020 }
2021 }
2022
2023 pub(crate) fn is_complete(&self, textures: &Assets<Texture>) -> bool {
2024 match &self {
2025 Self::NormalSprite { primary_texture } | Self::LightAdd { primary_texture } => {
2026 textures.contains(primary_texture)
2027 }
2028 Self::AlphaMasker {
2029 primary_texture,
2030 alpha_texture,
2031 } => textures.contains(primary_texture) && textures.contains(alpha_texture),
2032 Self::Quad => true,
2033 }
2034 }
2035}
2036
2037impl Display for MaterialKind {
2038 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2039 let texture_name = self
2040 .primary_texture()
2041 .map_or_else(String::new, |x| x.to_string());
2042
2043 let kind_name = match self {
2044 Self::NormalSprite { .. } => "NormalSprite",
2045 Self::LightAdd { .. } => "Light (Add)",
2046 Self::Quad => "Quad",
2047 Self::AlphaMasker { .. } => "AlphaMasker",
2048 };
2049
2050 write!(f, "{kind_name} texture {texture_name}")
2051 }
2052}
2053
2054#[derive(Debug)]
2055pub struct Sprite {
2056 pub params: SpriteParams,
2057}
2058
2059#[derive(Debug)]
2060pub struct QuadColor {
2061 pub size: UVec2,
2062 pub color: Color,
2063 pub params: QuadParams,
2064}
2065
2066#[derive(Debug, Copy, Clone)]
2067pub struct Slices {
2068 pub left: u16,
2069 pub top: u16,
2070 pub right: u16, pub bottom: u16, }
2073
2074#[derive(Debug)]
2075pub struct NineSlice {
2076 pub size: UVec2, pub slices: Slices,
2078 pub color: Color, pub origin_in_atlas: UVec2,
2080 pub size_inside_atlas: Option<UVec2>,
2081}
2082
2083#[derive(Debug)]
2084pub struct TileMap {
2085 pub tiles_data_grid_size: UVec2,
2086 pub cell_count_size: UVec2,
2087 pub one_cell_size: UVec2,
2088 pub tiles: Vec<u16>,
2089 pub scale: u8,
2090}
2091
2092#[derive(PartialEq, Debug, Eq, Ord, PartialOrd)]
2093pub struct Pipeline {
2094 name: String,
2095 render_pipeline: RenderPipeline,
2096}
2097
2098impl Display for Pipeline {
2099 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2100 write!(f, "pipeline: {}", self.name)
2101 }
2102}
2103
2104pub type PipelineRef = Arc<Pipeline>;