1mod autotile;
25pub mod brush;
26mod data;
27mod effect;
28mod property;
29mod tile_collider;
30mod tile_rect;
31mod tile_source;
32pub mod tileset;
33mod transform;
34mod update;
35
36pub use autotile::*;
37use brush::*;
38pub use data::*;
39pub use effect::*;
40use fxhash::FxHashSet;
41use fyrox_core::{
42 math::{frustum::Frustum, plane::Plane, ray::Ray},
43 parking_lot::Mutex,
44};
45use fyrox_resource::Resource;
46pub use tile_collider::*;
47pub use tile_rect::*;
48pub use tile_source::*;
49use tileset::*;
50pub use transform::*;
51pub use update::*;
52
53use super::{dim2::rectangle::RectangleVertex, node::constructor::NodeConstructor};
54use crate::lazy_static::*;
55use crate::{
56 asset::{untyped::ResourceKind, ResourceDataRef},
57 core::{
58 algebra::{Matrix4, Vector2, Vector3},
59 color::Color,
60 math::{aabb::AxisAlignedBoundingBox, Matrix4Ext, TriangleDefinition},
61 pool::Handle,
62 reflect::prelude::*,
63 type_traits::prelude::*,
64 variable::InheritableVariable,
65 visitor::prelude::*,
66 ImmutableString, SafeLock,
67 },
68 graph::{constructor::ConstructorProvider, BaseSceneGraph},
69 material::{Material, MaterialResource, STANDARD_2D},
70 renderer::{self, bundle::RenderContext},
71 scene::{
72 base::{Base, BaseBuilder},
73 graph::Graph,
74 mesh::{
75 buffer::{
76 VertexAttributeDataType, VertexAttributeDescriptor, VertexAttributeUsage,
77 VertexTrait,
78 },
79 RenderPath,
80 },
81 node::{Node, NodeTrait, RdcControlFlow},
82 Scene,
83 },
84};
85use bytemuck::{Pod, Zeroable};
86use fyrox_resource::manager::ResourceManager;
87use std::{
88 error::Error,
89 fmt::Display,
90 ops::{Deref, DerefMut},
91 path::PathBuf,
92};
93
94pub const VERSION: u8 = 1;
96
97lazy_static! {
98 pub static ref DEFAULT_TILE_MATERIAL: MaterialResource = MaterialResource::new_ok(
100 uuid!("36bf5b66-b4fa-4bca-80eb-33a271d8f825"),
101 ResourceKind::External,
102 Material::standard_tile()
103 );
104}
105
106pub struct TileMapRenderContext<'a, 'b> {
109 pub context: &'a mut RenderContext<'b>,
111 tile_map_handle: Handle<Node>,
113 transform: Matrix4<f32>,
115 bounds: OptionTileRect,
117 hidden_tiles: &'a mut FxHashSet<Vector2<i32>>,
118 tile_set: OptionTileSet<'a>,
119}
120
121impl TileMapRenderContext<'_, '_> {
122 pub fn transform(&self) -> &Matrix4<f32> {
124 &self.transform
125 }
126 pub fn tile_map_handle(&self) -> Handle<Node> {
128 self.tile_map_handle
129 }
130 pub fn position(&self) -> Vector3<f32> {
132 self.transform.position()
133 }
134 pub fn visible_bounds(&self) -> OptionTileRect {
136 self.bounds
137 }
138 pub fn set_tile_visible(&mut self, position: Vector2<i32>, is_visible: bool) {
144 if is_visible {
145 let _ = self.hidden_tiles.remove(&position);
146 } else {
147 let _ = self.hidden_tiles.insert(position);
148 }
149 }
150 pub fn is_tile_visible(&self, position: Vector2<i32>) -> bool {
155 !self.hidden_tiles.contains(&position)
156 }
157 pub fn get_animated_version(&self, handle: TileDefinitionHandle) -> TileDefinitionHandle {
160 self.tile_set
161 .get_animated_version(self.context.elapsed_time, handle)
162 .unwrap_or(handle)
163 }
164 pub fn draw_tile(&mut self, position: Vector2<i32>, handle: TileDefinitionHandle) {
170 if handle.is_empty() {
171 return;
172 }
173 let Some(data) = self.tile_set.get_tile_render_data(handle.into()) else {
174 return;
175 };
176 self.push_tile(position, &data);
177 }
178
179 pub fn push_tile(&mut self, position: Vector2<i32>, data: &TileRenderData) {
182 if data.is_empty() {
183 return;
184 }
185 let color = data.color;
186 if let Some(tile_bounds) = data.material_bounds.as_ref() {
187 let material = &tile_bounds.material;
188 let bounds = &tile_bounds.bounds;
189 self.push_material_tile(position, material, bounds, color);
190 } else {
191 self.push_color_tile(position, color);
192 }
193 }
194
195 fn push_color_tile(&mut self, position: Vector2<i32>, color: Color) {
196 let position = position.cast::<f32>();
197 let vertices = [(0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)]
198 .map(|(x, y)| Vector2::new(x, y))
199 .map(|p| make_rect_vertex(&self.transform, position + p, color));
200
201 let triangles = [[0, 1, 2], [2, 3, 0]].map(TriangleDefinition);
202
203 let sort_index = self.context.calculate_sorting_index(self.position());
204
205 self.context.storage.push_triangles(
206 self.context.dynamic_surface_cache,
207 RectangleVertex::layout(),
208 &STANDARD_2D.resource,
209 RenderPath::Forward,
210 sort_index,
211 self.tile_map_handle,
212 &mut move |mut vertex_buffer, mut triangle_buffer| {
213 let start_vertex_index = vertex_buffer.vertex_count();
214
215 vertex_buffer.push_vertices(&vertices).unwrap();
216
217 triangle_buffer
218 .push_triangles_iter_with_offset(start_vertex_index, triangles.into_iter());
219 },
220 );
221 }
222
223 fn push_material_tile(
224 &mut self,
225 position: Vector2<i32>,
226 material: &MaterialResource,
227 bounds: &TileBounds,
228 color: Color,
229 ) {
230 let position = position.cast::<f32>();
231 let uvs = [
232 bounds.right_top_corner,
233 bounds.left_top_corner,
234 bounds.left_bottom_corner,
235 bounds.right_bottom_corner,
236 ];
237 let vertices = [
238 (1.0, 1.0, uvs[0]),
239 (0.0, 1.0, uvs[1]),
240 (0.0, 0.0, uvs[2]),
241 (1.0, 0.0, uvs[3]),
242 ]
243 .map(|(x, y, uv)| (Vector2::new(x, y), uv))
244 .map(|(p, uv)| make_tile_vertex(&self.transform, position + p, uv, color));
245
246 let triangles = [[0, 1, 2], [2, 3, 0]].map(TriangleDefinition);
247
248 let sort_index = self.context.calculate_sorting_index(self.position());
249
250 self.context.storage.push_triangles(
251 self.context.dynamic_surface_cache,
252 TileVertex::layout(),
253 material,
254 RenderPath::Forward,
255 sort_index,
256 self.tile_map_handle,
257 &mut move |mut vertex_buffer, mut triangle_buffer| {
258 let start_vertex_index = vertex_buffer.vertex_count();
259
260 vertex_buffer.push_vertices(&vertices).unwrap();
261
262 triangle_buffer
263 .push_triangles_iter_with_offset(start_vertex_index, triangles.into_iter());
264 },
265 );
266 }
267}
268
269fn make_rect_vertex(
270 transform: &Matrix4<f32>,
271 position: Vector2<f32>,
272 color: Color,
273) -> RectangleVertex {
274 RectangleVertex {
275 position: transform
276 .transform_point(&position.to_homogeneous().into())
277 .coords,
278 tex_coord: Vector2::default(),
279 color,
280 }
281}
282
283fn make_tile_vertex(
284 transform: &Matrix4<f32>,
285 position: Vector2<f32>,
286 tex_coord: Vector2<u32>,
287 color: Color,
288) -> TileVertex {
289 TileVertex {
290 position: transform
291 .transform_point(&position.to_homogeneous().into())
292 .coords,
293 tex_coord: tex_coord.cast::<f32>(),
294 color,
295 }
296}
297
298#[derive(Default, Debug, Copy, Clone)]
300pub struct ChangeFlag(bool);
301
302impl ChangeFlag {
303 #[inline]
305 pub fn needs_save(&self) -> bool {
306 self.0
307 }
308 #[inline]
310 pub fn reset(&mut self) {
311 self.0 = false;
312 }
313 #[inline]
315 pub fn set(&mut self) {
316 self.0 = true;
317 }
318}
319
320#[derive(Copy, Clone, Debug, Default, Pod, Zeroable)]
322#[repr(C)] pub struct TileVertex {
324 pub position: Vector3<f32>,
326 pub tex_coord: Vector2<f32>,
328 pub color: Color,
330}
331
332impl VertexTrait for TileVertex {
333 fn layout() -> &'static [VertexAttributeDescriptor] {
334 &[
335 VertexAttributeDescriptor {
336 usage: VertexAttributeUsage::Position,
337 data_type: VertexAttributeDataType::F32,
338 size: 3,
339 divisor: 0,
340 shader_location: 0,
341 normalized: false,
342 },
343 VertexAttributeDescriptor {
344 usage: VertexAttributeUsage::TexCoord0,
345 data_type: VertexAttributeDataType::F32,
346 size: 2,
347 divisor: 0,
348 shader_location: 1,
349 normalized: false,
350 },
351 VertexAttributeDescriptor {
352 usage: VertexAttributeUsage::Color,
353 data_type: VertexAttributeDataType::U8,
354 size: 4,
355 divisor: 0,
356 shader_location: 2,
357 normalized: true,
358 },
359 ]
360 }
361}
362
363#[derive(Clone, Copy, Default, Debug, Visit, Reflect, PartialEq)]
368pub enum TilePaletteStage {
369 #[default]
371 Pages,
372 Tiles,
374}
375
376#[derive(Copy, Clone, Debug, Eq, PartialEq)]
378pub enum PageType {
379 Atlas,
382 Freeform,
384 Transform,
387 Animation,
393 Brush,
396}
397
398#[derive(Clone, Copy, Debug, PartialEq, Reflect)]
406pub enum ResourceTilePosition {
407 Page(Vector2<i32>),
409 Tile(Vector2<i32>, Vector2<i32>),
412}
413
414impl Display for ResourceTilePosition {
415 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
416 match self {
417 ResourceTilePosition::Page(p) => write!(f, "Page({},{})", p.x, p.y),
418 ResourceTilePosition::Tile(page, pos) => {
419 write!(f, "({},{}):({},{})", page.x, page.y, pos.x, pos.y)
420 }
421 }
422 }
423}
424
425impl From<TileDefinitionHandle> for ResourceTilePosition {
426 fn from(value: TileDefinitionHandle) -> Self {
427 Self::Tile(value.page(), value.tile())
428 }
429}
430
431impl ResourceTilePosition {
432 pub fn new(stage: TilePaletteStage, page: Vector2<i32>, tile: Vector2<i32>) -> Self {
437 match stage {
438 TilePaletteStage::Pages => Self::Page(tile),
439 TilePaletteStage::Tiles => Self::Tile(page, tile),
440 }
441 }
442 pub fn is_page(&self) -> bool {
444 matches!(self, Self::Page(_))
445 }
446 pub fn is_tile(&self) -> bool {
448 matches!(self, Self::Tile(_, _))
449 }
450 pub fn stage(&self) -> TilePaletteStage {
452 match self {
453 Self::Page(_) => TilePaletteStage::Pages,
454 Self::Tile(_, _) => TilePaletteStage::Tiles,
455 }
456 }
457 pub fn stage_position(&self) -> Vector2<i32> {
460 match self {
461 Self::Page(p) => *p,
462 Self::Tile(_, p) => *p,
463 }
464 }
465 pub fn page(&self) -> Vector2<i32> {
467 match self {
468 Self::Page(p) => *p,
469 Self::Tile(p, _) => *p,
470 }
471 }
472 pub fn handle(&self) -> Option<TileDefinitionHandle> {
474 if let Self::Tile(p, t) = self {
475 TileDefinitionHandle::try_new(*p, *t)
476 } else {
477 None
478 }
479 }
480}
481
482#[derive(Clone, Reflect, Default, Debug, PartialEq, Visit, ComponentProvider, TypeUuidProvider)]
485#[type_uuid(id = "e429ca1b-a311-46c3-b580-d5a2f49db7e2")]
486pub struct Tile {
487 pub position: Vector2<i32>,
489 pub definition_handle: TileDefinitionHandle,
491}
492
493#[derive(Debug, Clone)]
495pub struct TileIter<I> {
496 source: TileBook,
497 stage: TilePaletteStage,
498 page: Vector2<i32>,
499 positions: I,
500}
501
502impl<I: Iterator<Item = Vector2<i32>>> Iterator for TileIter<I> {
503 type Item = (Vector2<i32>, TileDefinitionHandle);
504
505 fn next(&mut self) -> Option<Self::Item> {
506 self.positions.find_map(|p| {
507 let h = self
508 .source
509 .get_tile_handle(ResourceTilePosition::new(self.stage, self.page, p))?;
510 Some((p, h))
511 })
512 }
513}
514
515#[derive(Debug, Default, Clone, PartialEq, Visit, Reflect)]
516pub enum TileBook {
520 #[default]
522 Empty,
523 TileSet(TileSetResource),
525 Brush(TileMapBrushResource),
527}
528
529impl TileBook {
530 #[inline]
532 pub fn page_icon(&self, position: Vector2<i32>) -> Option<TileDefinitionHandle> {
533 match self {
534 TileBook::Empty => None,
535 TileBook::TileSet(r) => r.state().data()?.page_icon(position),
536 TileBook::Brush(r) => r.state().data()?.page_icon(position),
537 }
538 }
539 #[inline]
541 pub fn is_tile_set(&self) -> bool {
542 matches!(self, TileBook::TileSet(_))
543 }
544 #[inline]
546 pub fn is_brush(&self) -> bool {
547 matches!(self, TileBook::Brush(_))
548 }
549 #[inline]
551 pub fn is_empty(&self) -> bool {
552 matches!(self, TileBook::Empty)
553 }
554 pub fn name(&self, resource_manager: &ResourceManager) -> String {
556 self.path(resource_manager)
557 .map(|x| x.to_string_lossy().into_owned())
558 .unwrap_or_else(|| "Error".into())
559 }
560 pub fn path(&self, resource_manager: &ResourceManager) -> Option<PathBuf> {
562 match self {
563 TileBook::Empty => None,
564 TileBook::TileSet(r) => resource_manager.resource_path(r.as_ref()),
565 TileBook::Brush(r) => resource_manager.resource_path(r.as_ref()),
566 }
567 }
568 pub fn needs_save(&self) -> bool {
570 match self {
571 TileBook::Empty => false,
572 TileBook::TileSet(r) => {
573 r.header().kind.is_external() && r.data_ref().change_flag.needs_save()
574 }
575 TileBook::Brush(r) => {
576 r.header().kind.is_external() && r.data_ref().change_flag.needs_save()
577 }
578 }
579 }
580 pub fn save(&self, resource_manager: &ResourceManager) -> Result<(), Box<dyn Error>> {
583 match self {
584 TileBook::Empty => Ok(()),
585 TileBook::TileSet(r) => {
586 if r.header().kind.is_external() && r.data_ref().change_flag.needs_save() {
587 let result = r.save(&resource_manager.resource_path(r.as_ref()).unwrap());
588 if result.is_ok() {
589 r.data_ref().change_flag.reset();
590 }
591 result
592 } else {
593 Ok(())
594 }
595 }
596 TileBook::Brush(r) => {
597 if r.header().kind.is_external() && r.data_ref().change_flag.needs_save() {
598 let result = r.save(&resource_manager.resource_path(r.as_ref()).unwrap());
599 if result.is_ok() {
600 r.data_ref().change_flag.reset();
601 }
602 result
603 } else {
604 Ok(())
605 }
606 }
607 }
608 }
609 pub fn tile_set_ref(&self) -> Option<&TileSetResource> {
611 match self {
612 TileBook::TileSet(r) => Some(r),
613 _ => None,
614 }
615 }
616 pub fn brush_ref(&self) -> Option<&TileMapBrushResource> {
618 match self {
619 TileBook::Brush(r) => Some(r),
620 _ => None,
621 }
622 }
623 pub fn get_tile_set(&self) -> Option<TileSetResource> {
627 match self {
628 TileBook::Empty => None,
629 TileBook::TileSet(r) => Some(r.clone()),
630 TileBook::Brush(r) => r.state().data()?.tile_set(),
631 }
632 }
633 pub fn get_all_tile_positions(&self, page: Vector2<i32>) -> Vec<Vector2<i32>> {
635 match self {
636 TileBook::Empty => Vec::new(),
637 TileBook::TileSet(r) => r
638 .state()
639 .data()
640 .map(|r| r.keys_on_page(page))
641 .unwrap_or_default(),
642 TileBook::Brush(r) => r
643 .state()
644 .data()
645 .and_then(|r| {
646 r.pages
647 .get(&page)
648 .map(|p| p.tiles.keys().copied().collect())
649 })
650 .unwrap_or_default(),
651 }
652 }
653 pub fn get_all_page_positions(&self) -> Vec<Vector2<i32>> {
655 match self {
656 TileBook::Empty => Vec::new(),
657 TileBook::TileSet(r) => r.state().data().map(|r| r.page_keys()).unwrap_or_default(),
658 TileBook::Brush(r) => r
659 .state()
660 .data()
661 .map(|r| r.pages.keys().copied().collect())
662 .unwrap_or_default(),
663 }
664 }
665 pub fn has_page_at(&self, position: Vector2<i32>) -> bool {
667 match self {
668 TileBook::Empty => false,
669 TileBook::TileSet(r) => r
670 .state()
671 .data()
672 .map(|r| r.pages.contains_key(&position))
673 .unwrap_or(false),
674 TileBook::Brush(r) => r
675 .state()
676 .data()
677 .map(|r| r.pages.contains_key(&position))
678 .unwrap_or(false),
679 }
680 }
681 pub fn page_type(&self, position: Vector2<i32>) -> Option<PageType> {
683 match self {
684 TileBook::Empty => None,
685 TileBook::TileSet(r) => r.state().data()?.get_page(position).map(|p| p.page_type()),
686 TileBook::Brush(r) => {
687 if r.state().data()?.has_page_at(position) {
688 Some(PageType::Brush)
689 } else {
690 None
691 }
692 }
693 }
694 }
695 pub fn is_atlas_page(&self, position: Vector2<i32>) -> bool {
697 self.page_type(position) == Some(PageType::Atlas)
698 }
699 pub fn is_free_page(&self, position: Vector2<i32>) -> bool {
701 self.page_type(position) == Some(PageType::Freeform)
702 }
703 pub fn is_transform_page(&self, position: Vector2<i32>) -> bool {
705 self.page_type(position) == Some(PageType::Transform)
706 }
707 pub fn is_animation_page(&self, position: Vector2<i32>) -> bool {
709 self.page_type(position) == Some(PageType::Animation)
710 }
711 pub fn is_brush_page(&self, position: Vector2<i32>) -> bool {
713 self.page_type(position) == Some(PageType::Brush)
714 }
715 pub fn has_tile_at(&self, page: Vector2<i32>, tile: Vector2<i32>) -> bool {
717 match self {
718 TileBook::Empty => false,
719 TileBook::TileSet(r) => r
720 .state()
721 .data()
722 .map(|r| r.has_tile_at(page, tile))
723 .unwrap_or(false),
724 TileBook::Brush(r) => r
725 .state()
726 .data()
727 .map(|r| r.has_tile_at(page, tile))
728 .unwrap_or(false),
729 }
730 }
731 pub fn get_tile_handle(&self, position: ResourceTilePosition) -> Option<TileDefinitionHandle> {
735 match self {
736 TileBook::Empty => None,
737 TileBook::TileSet(r) => r.state().data()?.redirect_handle(position),
738 TileBook::Brush(r) => r.state().data()?.redirect_handle(position),
739 }
740 }
741 pub fn get_stamp_element(&self, position: ResourceTilePosition) -> Option<StampElement> {
746 match self {
747 TileBook::Empty => None,
748 TileBook::TileSet(r) => r.state().data()?.stamp_element(position),
749 TileBook::Brush(r) => r.state().data()?.stamp_element(position),
750 }
751 }
752 pub fn get_tile_iter<I: Iterator<Item = Vector2<i32>>>(
756 &self,
757 stage: TilePaletteStage,
758 page: Vector2<i32>,
759 positions: I,
760 ) -> TileIter<I> {
761 TileIter {
762 source: self.clone(),
763 stage,
764 page,
765 positions,
766 }
767 }
768 pub fn get_tiles<I: Iterator<Item = Vector2<i32>>>(
771 &self,
772 stage: TilePaletteStage,
773 page: Vector2<i32>,
774 iter: I,
775 tiles: &mut Tiles,
776 ) {
777 match self {
778 TileBook::Empty => (),
779 TileBook::TileSet(res) => {
780 if let Some(tile_set) = res.state().data() {
781 tile_set.get_tiles(stage, page, iter, tiles);
782 }
783 }
784 TileBook::Brush(res) => {
785 if let Some(brush) = res.state().data() {
786 brush.get_tiles(stage, page, iter, tiles);
787 }
788 }
789 }
790 }
791
792 pub fn is_missing_tile_set(&self) -> bool {
794 match self {
795 TileBook::Empty => false,
796 TileBook::TileSet(_) => false,
797 TileBook::Brush(resource) => resource
798 .state()
799 .data()
800 .map(|b| b.is_missing_tile_set())
801 .unwrap_or(false),
802 }
803 }
804
805 pub fn get_tile_render_data(&self, position: ResourceTilePosition) -> Option<TileRenderData> {
815 match self {
816 TileBook::Empty => None,
817 TileBook::TileSet(resource) => resource.state().data()?.get_tile_render_data(position),
818 TileBook::Brush(resource) => resource.state().data()?.get_tile_render_data(position),
819 }
820 }
821
822 pub fn tile_render_loop<F>(&self, stage: TilePaletteStage, page: Vector2<i32>, func: F)
826 where
827 F: FnMut(Vector2<i32>, TileRenderData),
828 {
829 match self {
830 TileBook::Empty => (),
831 TileBook::TileSet(res) => {
832 if let Some(data) = res.state().data() {
833 data.palette_render_loop(stage, page, func)
834 }
835 }
836 TileBook::Brush(res) => {
837 if let Some(data) = res.state().data() {
838 data.palette_render_loop(stage, page, func)
839 }
840 }
841 };
842 }
843 pub fn tile_collider_loop<F>(&self, page: Vector2<i32>, func: F)
846 where
847 F: FnMut(Vector2<i32>, Uuid, Color, &TileCollider),
848 {
849 match self {
850 TileBook::Empty => (),
851 TileBook::TileSet(res) => {
852 if let Some(data) = res.state().data() {
853 data.tile_collider_loop(page, func)
854 }
855 }
856 TileBook::Brush(_) => (),
857 };
858 }
859 pub fn get_tile_bounds(&self, position: ResourceTilePosition) -> Option<TileMaterialBounds> {
862 match self {
863 TileBook::Empty => None,
864 TileBook::TileSet(res) => res
865 .state()
866 .data()
867 .map(|d| d.get_tile_bounds(position))
868 .unwrap_or_default(),
869 TileBook::Brush(res) => res
870 .state()
871 .data()
872 .map(|d| d.get_tile_bounds(position))
873 .unwrap_or_default(),
874 }
875 }
876 pub fn tiles_bounds(&self, stage: TilePaletteStage, page: Vector2<i32>) -> OptionTileRect {
878 match self {
879 TileBook::Empty => OptionTileRect::default(),
880 TileBook::TileSet(res) => res.data_ref().tiles_bounds(stage, page),
881 TileBook::Brush(res) => res.data_ref().tiles_bounds(stage, page),
882 }
883 }
884}
885
886#[derive(Clone, Default, Debug)]
888pub struct TileRenderData {
889 pub material_bounds: Option<TileMaterialBounds>,
891 pub color: Color,
893 empty: bool,
895}
896
897impl TileRenderData {
898 pub fn new(material_bounds: Option<TileMaterialBounds>, color: Color) -> Self {
900 Self {
901 material_bounds,
902 color,
903 empty: false,
904 }
905 }
906 pub fn empty() -> Self {
908 Self {
909 material_bounds: None,
910 color: Color::WHITE,
911 empty: true,
912 }
913 }
914 pub fn is_empty(&self) -> bool {
916 self.empty
917 }
918 pub fn missing_data() -> Self {
920 Self {
921 material_bounds: None,
922 color: Color::HOT_PINK,
923 empty: false,
924 }
925 }
926}
927
928impl OrthoTransform for TileRenderData {
929 fn x_flipped(mut self) -> Self {
930 self.material_bounds = self.material_bounds.map(|b| b.x_flipped());
931 self
932 }
933
934 fn rotated(mut self, amount: i8) -> Self {
935 self.material_bounds = self.material_bounds.map(|b| b.rotated(amount));
936 self
937 }
938}
939
940#[derive(Reflect, Debug, ComponentProvider, TypeUuidProvider)]
949#[reflect(derived_type = "Node")]
950#[type_uuid(id = "aa9a3385-a4af-4faf-a69a-8d3af1a3aa67")]
951pub struct TileMap {
952 base: Base,
953 tile_set: InheritableVariable<Option<TileSetResource>>,
955 #[reflect(hidden)]
957 pub tiles: InheritableVariable<Option<TileMapDataResource>>,
958 tile_scale: InheritableVariable<Vector2<f32>>,
959 active_brush: InheritableVariable<Option<TileMapBrushResource>>,
960 #[reflect(hidden)]
963 hidden_tiles: Mutex<FxHashSet<Vector2<i32>>>,
964 #[reflect(hidden)]
970 pub before_effects: Vec<TileMapEffectRef>,
971 #[reflect(hidden)]
975 pub after_effects: Vec<TileMapEffectRef>,
976}
977
978impl Visit for TileMap {
979 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
980 let mut region = visitor.enter_region(name)?;
981 let mut version = if region.is_reading() { 0 } else { VERSION };
982 let _ = version.visit("Version", &mut region);
983 self.base.visit("Base", &mut region)?;
984 self.tile_set.visit("TileSet", &mut region)?;
985 self.tile_scale.visit("TileScale", &mut region)?;
986 self.active_brush.visit("ActiveBrush", &mut region)?;
987 match version {
988 0 => {
989 let mut tiles = InheritableVariable::new_non_modified(Tiles::default());
990 let result = tiles.visit("Tiles", &mut region);
991 result?;
992 let mut data = TileMapData::default();
993 for (p, h) in tiles.iter() {
994 data.set(*p, *h);
995 }
996 self.tiles = Some(Resource::new_ok(
997 Uuid::new_v4(),
998 ResourceKind::Embedded,
999 data,
1000 ))
1001 .into();
1002 }
1003 VERSION => {
1004 self.tiles.visit("Tiles", &mut region)?;
1005 }
1006 _ => return Err(VisitError::User("Unknown version".into())),
1007 }
1008 Ok(())
1009 }
1010}
1011
1012pub struct TileMapDataRef<'a> {
1014 tile_set: ResourceDataRef<'a, TileSet>,
1015 handle: TileDefinitionHandle,
1016}
1017
1018impl Deref for TileMapDataRef<'_> {
1019 type Target = TileData;
1020
1021 fn deref(&self) -> &Self::Target {
1022 self.tile_set.tile_data(self.handle).unwrap()
1023 }
1024}
1025
1026#[derive(Debug)]
1028pub enum TilePropertyError {
1029 MissingTileSet,
1031 TileSetNotLoaded,
1033 UnrecognizedName(ImmutableString),
1035 UnrecognizedUuid(Uuid),
1037 WrongType(&'static str),
1039}
1040
1041impl Display for TilePropertyError {
1042 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1043 match self {
1044 TilePropertyError::MissingTileSet => write!(f, "The tile map has no tile set."),
1045 TilePropertyError::TileSetNotLoaded => {
1046 write!(f, "The tile map's tile set is not loaded.")
1047 }
1048 TilePropertyError::UnrecognizedName(name) => {
1049 write!(f, "There is no property with this name: {name}")
1050 }
1051 TilePropertyError::UnrecognizedUuid(uuid) => {
1052 write!(f, "There is no property with this UUID: {uuid}")
1053 }
1054 TilePropertyError::WrongType(message) => write!(f, "Property type error: {message}"),
1055 }
1056 }
1057}
1058
1059impl Error for TilePropertyError {}
1060
1061impl TileMap {
1062 pub fn tile_handle(&self, position: Vector2<i32>) -> Option<TileDefinitionHandle> {
1064 let tiles = self.tiles.as_ref()?.data_ref();
1065 tiles.as_loaded_ref()?.get(position)
1066 }
1067 pub fn tile_data(&self, position: Vector2<i32>) -> Option<TileMapDataRef> {
1070 let handle = self.tile_handle(position)?;
1071 let tile_set = self.tile_set.as_ref()?.data_ref();
1072 if tile_set.as_loaded_ref()?.tile_data(handle).is_some() {
1073 Some(TileMapDataRef { tile_set, handle })
1074 } else {
1075 None
1076 }
1077 }
1078 pub fn tile_property_value<T>(
1084 &self,
1085 position: Vector2<i32>,
1086 property_id: Uuid,
1087 ) -> Result<T, TilePropertyError>
1088 where
1089 T: TryFrom<TileSetPropertyValue, Error = TilePropertyError> + Default,
1090 {
1091 let Some(handle) = self.tile_handle(position) else {
1092 return Ok(T::default());
1093 };
1094 let tile_set = self
1095 .tile_set
1096 .as_ref()
1097 .ok_or(TilePropertyError::MissingTileSet)?
1098 .data_ref();
1099 let tile_set = tile_set
1100 .as_loaded_ref()
1101 .ok_or(TilePropertyError::TileSetNotLoaded)?;
1102 tile_set.tile_property_value(handle, property_id)
1103 }
1104 pub fn tile_property_value_by_name(
1110 &self,
1111 position: Vector2<i32>,
1112 property_name: &ImmutableString,
1113 ) -> Result<TileSetPropertyValue, TilePropertyError> {
1114 let tile_set = self
1115 .tile_set
1116 .as_ref()
1117 .ok_or(TilePropertyError::MissingTileSet)?
1118 .data_ref();
1119 let tile_set = tile_set
1120 .as_loaded_ref()
1121 .ok_or(TilePropertyError::TileSetNotLoaded)?;
1122 let property = tile_set
1123 .find_property_by_name(property_name)
1124 .ok_or_else(|| TilePropertyError::UnrecognizedName(property_name.clone()))?;
1125 let Some(handle) = self.tile_handle(position) else {
1126 return Ok(property.prop_type.default_value());
1127 };
1128 Ok(tile_set
1129 .property_value(handle, property.uuid)
1130 .unwrap_or_else(|| property.prop_type.default_value()))
1131 }
1132 pub fn tile_property_value_by_uuid_untyped(
1138 &self,
1139 position: Vector2<i32>,
1140 property_id: Uuid,
1141 ) -> Result<TileSetPropertyValue, TilePropertyError> {
1142 let tile_set = self
1143 .tile_set
1144 .as_ref()
1145 .ok_or(TilePropertyError::MissingTileSet)?
1146 .data_ref();
1147 let tile_set = tile_set
1148 .as_loaded_ref()
1149 .ok_or(TilePropertyError::TileSetNotLoaded)?;
1150 let value = if let Some(handle) = self.tile_handle(position) {
1151 tile_set
1152 .tile_data(handle)
1153 .and_then(|d| d.properties.get(&property_id))
1154 .cloned()
1155 } else {
1156 None
1157 };
1158 if let Some(value) = value {
1159 Ok(value)
1160 } else {
1161 let property = tile_set
1162 .find_property(property_id)
1163 .ok_or(TilePropertyError::UnrecognizedUuid(property_id))?;
1164 Ok(property.prop_type.default_value())
1165 }
1166 }
1167 pub fn tile_map_transform(&self) -> Matrix4<f32> {
1169 self.global_transform()
1170 .prepend_nonuniform_scaling(&Vector3::new(-1.0, 1.0, 1.0))
1171 }
1172 #[inline]
1174 pub fn tile_set(&self) -> Option<&TileSetResource> {
1175 self.tile_set.as_ref()
1176 }
1177
1178 #[inline]
1180 pub fn set_tile_set(&mut self, tile_set: Option<TileSetResource>) {
1181 self.tile_set.set_value_and_mark_modified(tile_set);
1182 }
1183
1184 #[inline]
1186 pub fn tiles(&self) -> Option<&TileMapDataResource> {
1187 self.tiles.as_ref()
1188 }
1189
1190 #[inline]
1192 pub fn set_tiles(&mut self, tiles: TileMapDataResource) {
1193 self.tiles.set_value_and_mark_modified(Some(tiles));
1194 }
1195
1196 #[inline]
1198 pub fn tile_scale(&self) -> Vector2<f32> {
1199 *self.tile_scale
1200 }
1201
1202 #[inline]
1204 pub fn set_tile_scale(&mut self, tile_scale: Vector2<f32>) {
1205 self.tile_scale.set_value_and_mark_modified(tile_scale);
1206 }
1207
1208 #[inline]
1211 pub fn insert_tile(
1212 &mut self,
1213 position: Vector2<i32>,
1214 tile: TileDefinitionHandle,
1215 ) -> Option<TileDefinitionHandle> {
1216 self.tiles
1217 .as_ref()?
1218 .data_ref()
1219 .as_loaded_mut()?
1220 .replace(position, Some(tile))
1221 }
1222
1223 #[inline]
1225 pub fn remove_tile(&mut self, position: Vector2<i32>) -> Option<TileDefinitionHandle> {
1226 self.tiles
1227 .as_ref()?
1228 .data_ref()
1229 .as_loaded_mut()?
1230 .replace(position, None)
1231 }
1232
1233 #[inline]
1235 pub fn active_brush(&self) -> Option<&TileMapBrushResource> {
1236 self.active_brush.as_ref()
1237 }
1238
1239 #[inline]
1241 pub fn set_active_brush(&mut self, brush: Option<TileMapBrushResource>) {
1242 self.active_brush.set_value_and_mark_modified(brush);
1243 }
1244
1245 #[inline]
1247 pub fn bounding_rect(&self) -> OptionTileRect {
1248 let Some(tiles) = self.tiles.as_ref().map(|r| r.data_ref()) else {
1249 return OptionTileRect::default();
1250 };
1251 let Some(tiles) = tiles.as_loaded_ref() else {
1252 return OptionTileRect::default();
1253 };
1254 tiles.bounding_rect()
1255 }
1256
1257 #[inline]
1261 pub fn world_to_grid(&self, world_position: Vector3<f32>) -> Vector2<i32> {
1262 let inv_global_transform = self.tile_map_transform().try_inverse().unwrap_or_default();
1263 let local_space_position = inv_global_transform.transform_point(&world_position.into());
1264 Vector2::new(
1265 local_space_position.x.floor() as i32,
1266 local_space_position.y.floor() as i32,
1267 )
1268 }
1269
1270 #[inline]
1272 pub fn grid_to_world(&self, grid_position: Vector2<i32>) -> Vector3<f32> {
1273 let v3 = grid_position.cast::<f32>().to_homogeneous();
1274 self.tile_map_transform().transform_point(&v3.into()).coords
1275 }
1276
1277 fn cells_touching_frustum(&self, frustum: &Frustum) -> OptionTileRect {
1278 let global_transform = self.global_transform();
1279
1280 fn make_ray(a: Vector3<f32>, b: Vector3<f32>) -> Ray {
1281 Ray {
1282 origin: a,
1283 dir: b - a,
1284 }
1285 }
1286
1287 let left_top_ray = make_ray(
1288 frustum.left_top_front_corner(),
1289 frustum.left_top_back_corner(),
1290 );
1291 let right_top_ray = make_ray(
1292 frustum.right_top_front_corner(),
1293 frustum.right_top_back_corner(),
1294 );
1295 let left_bottom_ray = make_ray(
1296 frustum.left_bottom_front_corner(),
1297 frustum.left_bottom_back_corner(),
1298 );
1299 let right_bottom_ray = make_ray(
1300 frustum.right_bottom_front_corner(),
1301 frustum.right_bottom_back_corner(),
1302 );
1303
1304 let plane =
1305 Plane::from_normal_and_point(&global_transform.look(), &global_transform.position())
1306 .unwrap_or_default();
1307
1308 let Some(left_top) = left_top_ray.plane_intersection_point(&plane) else {
1309 return None.into();
1310 };
1311 let Some(right_top) = right_top_ray.plane_intersection_point(&plane) else {
1312 return None.into();
1313 };
1314 let Some(left_bottom) = left_bottom_ray.plane_intersection_point(&plane) else {
1315 return None.into();
1316 };
1317 let Some(right_bottom) = right_bottom_ray.plane_intersection_point(&plane) else {
1318 return None.into();
1319 };
1320 let mut bounds = OptionTileRect::default();
1321 for corner in [left_top, right_top, left_bottom, right_bottom] {
1322 bounds.push(self.world_to_grid(corner))
1323 }
1324 bounds
1325 }
1326}
1327
1328impl Default for TileMap {
1329 fn default() -> Self {
1330 Self {
1331 base: Default::default(),
1332 tile_set: Default::default(),
1333 tiles: Default::default(),
1334 tile_scale: Vector2::repeat(1.0).into(),
1335 active_brush: Default::default(),
1336 hidden_tiles: Mutex::default(),
1337 before_effects: Vec::default(),
1338 after_effects: Vec::default(),
1339 }
1340 }
1341}
1342
1343impl Clone for TileMap {
1344 fn clone(&self) -> Self {
1345 Self {
1346 base: self.base.clone(),
1347 tile_set: self.tile_set.clone(),
1348 tiles: self.tiles.clone(),
1349 tile_scale: self.tile_scale.clone(),
1350 active_brush: self.active_brush.clone(),
1351 hidden_tiles: Mutex::default(),
1352 before_effects: self.before_effects.clone(),
1353 after_effects: self.after_effects.clone(),
1354 }
1355 }
1356}
1357
1358impl Deref for TileMap {
1359 type Target = Base;
1360
1361 fn deref(&self) -> &Self::Target {
1362 &self.base
1363 }
1364}
1365
1366impl DerefMut for TileMap {
1367 fn deref_mut(&mut self) -> &mut Self::Target {
1368 &mut self.base
1369 }
1370}
1371
1372impl ConstructorProvider<Node, Graph> for TileMap {
1373 fn constructor() -> NodeConstructor {
1374 NodeConstructor::new::<Self>()
1375 .with_variant("Tile Map", |_| {
1376 TileMapBuilder::new(BaseBuilder::new().with_name("Tile Map"))
1377 .build_node()
1378 .into()
1379 })
1380 .with_group("2D")
1381 }
1382}
1383
1384impl NodeTrait for TileMap {
1385 fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
1386 let Some(rect) = *self.bounding_rect() else {
1387 return AxisAlignedBoundingBox::default();
1388 };
1389
1390 let mut min_pos = rect.position.cast::<f32>().to_homogeneous();
1391 let mut max_pos = (rect.position + rect.size).cast::<f32>().to_homogeneous();
1392 min_pos.x *= -1.0;
1393 max_pos.x *= -1.0;
1394 let (min, max) = min_pos.inf_sup(&max_pos);
1395
1396 AxisAlignedBoundingBox::from_min_max(min, max)
1397 }
1398
1399 fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
1400 self.local_bounding_box()
1401 .transform(&self.global_transform())
1402 }
1403
1404 fn id(&self) -> Uuid {
1405 Self::type_uuid()
1406 }
1407
1408 fn collect_render_data(&self, ctx: &mut RenderContext) -> RdcControlFlow {
1409 if !self.should_be_rendered(ctx.frustum, ctx.render_mask) {
1410 return RdcControlFlow::Continue;
1411 }
1412
1413 if renderer::is_shadow_pass(ctx.render_pass_name) {
1414 return RdcControlFlow::Continue;
1415 }
1416
1417 let Some(ref tile_set_resource) = *self.tile_set else {
1418 return RdcControlFlow::Continue;
1419 };
1420
1421 let mut tile_set_lock = TileSetRef::new(tile_set_resource);
1422 let tile_set = tile_set_lock.as_loaded();
1423
1424 let mut hidden_tiles = self.hidden_tiles.safe_lock();
1425 hidden_tiles.clear();
1426
1427 let bounds = ctx
1428 .frustum
1429 .as_ref()
1430 .map(|f| self.cells_touching_frustum(f))
1431 .unwrap_or_default();
1432
1433 let mut tile_render_context = TileMapRenderContext {
1434 tile_map_handle: self.handle(),
1435 transform: self.tile_map_transform(),
1436 hidden_tiles: &mut hidden_tiles,
1437 context: ctx,
1438 bounds,
1439 tile_set,
1440 };
1441
1442 for effect in self.before_effects.iter() {
1443 effect
1444 .safe_lock()
1445 .render_special_tiles(&mut tile_render_context);
1446 }
1447 let bounds = tile_render_context.visible_bounds();
1448 let Some(tiles) = self.tiles.as_ref().map(|r| r.data_ref()) else {
1449 return RdcControlFlow::Continue;
1450 };
1451 let Some(tiles) = tiles.as_loaded_ref() else {
1452 return RdcControlFlow::Continue;
1453 };
1454 if bounds.is_some() {
1455 for (position, handle) in tiles.bounded_iter(bounds) {
1456 if bounds.contains(position) && tile_render_context.is_tile_visible(position) {
1457 let handle = tile_render_context.get_animated_version(handle);
1458 tile_render_context.draw_tile(position, handle);
1459 }
1460 }
1461 } else {
1462 for (position, handle) in tiles.iter() {
1463 if tile_render_context.is_tile_visible(position) {
1464 let handle = tile_render_context.get_animated_version(handle);
1465 tile_render_context.draw_tile(position, handle);
1466 }
1467 }
1468 }
1469 for effect in self.after_effects.iter() {
1470 effect
1471 .safe_lock()
1472 .render_special_tiles(&mut tile_render_context);
1473 }
1474 RdcControlFlow::Continue
1475 }
1476
1477 fn validate(&self, _scene: &Scene) -> Result<(), String> {
1478 if self.tile_set.is_none() {
1479 Err(
1480 "Tile set resource is not set. Tile map will not be rendered correctly!"
1481 .to_string(),
1482 )
1483 } else {
1484 Ok(())
1485 }
1486 }
1487}
1488
1489pub struct TileMapBuilder {
1491 base_builder: BaseBuilder,
1492 tile_set: Option<TileSetResource>,
1493 tiles: TileMapData,
1494 tile_scale: Vector2<f32>,
1495 before_effects: Vec<TileMapEffectRef>,
1496 after_effects: Vec<TileMapEffectRef>,
1497}
1498
1499impl TileMapBuilder {
1500 pub fn new(base_builder: BaseBuilder) -> Self {
1502 Self {
1503 base_builder,
1504 tile_set: None,
1505 tiles: TileMapData::default(),
1506 tile_scale: Vector2::repeat(1.0),
1507 before_effects: Default::default(),
1508 after_effects: Default::default(),
1509 }
1510 }
1511
1512 pub fn with_tile_set(mut self, tile_set: TileSetResource) -> Self {
1514 self.tile_set = Some(tile_set);
1515 self
1516 }
1517
1518 pub fn with_tiles(mut self, tiles: &Tiles) -> Self {
1520 for (pos, handle) in tiles.iter() {
1521 self.tiles.set(*pos, *handle);
1522 }
1523 self
1524 }
1525
1526 pub fn with_tile_scale(mut self, tile_scale: Vector2<f32>) -> Self {
1528 self.tile_scale = tile_scale;
1529 self
1530 }
1531
1532 pub fn with_before_effect(mut self, effect: TileMapEffectRef) -> Self {
1534 self.before_effects.push(effect);
1535 self
1536 }
1537
1538 pub fn with_after_effect(mut self, effect: TileMapEffectRef) -> Self {
1540 self.after_effects.push(effect);
1541 self
1542 }
1543
1544 pub fn build_node(self) -> Node {
1546 Node::new(TileMap {
1547 base: self.base_builder.build_base(),
1548 tile_set: self.tile_set.into(),
1549 tiles: Some(Resource::new_ok(
1550 Uuid::new_v4(),
1551 ResourceKind::Embedded,
1552 self.tiles,
1553 ))
1554 .into(),
1555 tile_scale: self.tile_scale.into(),
1556 active_brush: Default::default(),
1557 hidden_tiles: Mutex::default(),
1558 before_effects: self.before_effects,
1559 after_effects: self.after_effects,
1560 })
1561 }
1562
1563 pub fn build(self, graph: &mut Graph) -> Handle<Node> {
1565 graph.add_node(self.build_node())
1566 }
1567}