1
- use crate :: { AlphaMode2d , Anchor , MeshMaterial2d } ;
1
+ use crate :: { AlphaMode2d , MeshMaterial2d } ;
2
2
use bevy_app:: { App , Plugin , Update } ;
3
- use bevy_asset:: { Assets , Handle , RenderAssetUsages } ;
3
+ use bevy_asset:: { Assets , Handle } ;
4
+ use bevy_color:: Color ;
4
5
use bevy_derive:: { Deref , DerefMut } ;
5
6
use bevy_ecs:: {
6
7
component:: Component ,
@@ -11,15 +12,11 @@ use bevy_ecs::{
11
12
system:: { Query , ResMut } ,
12
13
world:: DeferredWorld ,
13
14
} ;
14
- use bevy_image:: { Image , ImageSampler , ToExtents } ;
15
- use bevy_math:: { FloatOrd , UVec2 , Vec2 , Vec3 } ;
15
+ use bevy_image:: Image ;
16
+ use bevy_math:: { primitives :: Rectangle , UVec2 } ;
16
17
use bevy_platform:: collections:: HashMap ;
17
- use bevy_render:: {
18
- mesh:: { Indices , Mesh , Mesh2d , PrimitiveTopology } ,
19
- render_resource:: {
20
- TextureDataOrder , TextureDescriptor , TextureDimension , TextureFormat , TextureUsages ,
21
- } ,
22
- } ;
18
+ use bevy_render:: mesh:: { Mesh , Mesh2d } ;
19
+ use bevy_utils:: default;
23
20
use tracing:: warn;
24
21
25
22
mod tilemap_chunk_material;
@@ -37,16 +34,13 @@ impl Plugin for TilemapChunkPlugin {
37
34
}
38
35
}
39
36
40
- type TilemapChunkMeshCacheKey = ( UVec2 , FloatOrd , FloatOrd , FloatOrd , FloatOrd ) ;
41
-
42
37
/// A resource storing the meshes for each tilemap chunk size.
43
38
#[ derive( Resource , Default , Deref , DerefMut ) ]
44
- pub struct TilemapChunkMeshCache ( HashMap < TilemapChunkMeshCacheKey , Handle < Mesh > > ) ;
39
+ pub struct TilemapChunkMeshCache ( HashMap < UVec2 , Handle < Mesh > > ) ;
45
40
46
41
/// A component representing a chunk of a tilemap.
47
42
/// Each chunk is a rectangular section of tiles that is rendered as a single mesh.
48
43
#[ derive( Component , Clone , Debug , Default ) ]
49
- #[ require( Anchor ) ]
50
44
#[ component( immutable, on_insert = on_insert_tilemap_chunk) ]
51
45
pub struct TilemapChunk {
52
46
/// The size of the chunk in tiles
@@ -60,10 +54,36 @@ pub struct TilemapChunk {
60
54
pub alpha_mode : AlphaMode2d ,
61
55
}
62
56
57
+ #[ derive( Clone , Copy , Debug ) ]
58
+ pub struct TileData {
59
+ pub tileset_index : u16 ,
60
+ pub visible : bool ,
61
+ pub color : Color ,
62
+ }
63
+
64
+ impl TileData {
65
+ pub fn from_index ( index : u16 ) -> Self {
66
+ Self {
67
+ tileset_index : index,
68
+ ..default ( )
69
+ }
70
+ }
71
+ }
72
+
73
+ impl Default for TileData {
74
+ fn default ( ) -> Self {
75
+ Self {
76
+ tileset_index : 0 ,
77
+ visible : true ,
78
+ color : Color :: WHITE ,
79
+ }
80
+ }
81
+ }
82
+
63
83
/// Component storing the indices of tiles within a chunk.
64
84
/// Each index corresponds to a specific tile in the tileset.
65
85
#[ derive( Component , Clone , Debug , Deref , DerefMut ) ]
66
- pub struct TilemapChunkIndices ( pub Vec < Option < u16 > > ) ;
86
+ pub struct TilemapChunkTileData ( pub Vec < Option < TileData > > ) ;
67
87
68
88
fn on_insert_tilemap_chunk ( mut world : DeferredWorld , HookContext { entity, .. } : HookContext ) {
69
89
let Some ( tilemap_chunk) = world. get :: < TilemapChunk > ( entity) else {
@@ -75,55 +95,46 @@ fn on_insert_tilemap_chunk(mut world: DeferredWorld, HookContext { entity, .. }:
75
95
let alpha_mode = tilemap_chunk. alpha_mode ;
76
96
let tileset = tilemap_chunk. tileset . clone ( ) ;
77
97
78
- let Some ( indices ) = world. get :: < TilemapChunkIndices > ( entity) else {
98
+ let Some ( tile_data ) = world. get :: < TilemapChunkTileData > ( entity) else {
79
99
warn ! ( "TilemapChunkIndices not found for tilemap chunk {}" , entity) ;
80
100
return ;
81
101
} ;
82
102
83
- let Some ( & anchor) = world. get :: < Anchor > ( entity) else {
84
- warn ! ( "Anchor not found for tilemap chunk {}" , entity) ;
85
- return ;
86
- } ;
87
-
88
- let expected_indices_length = chunk_size. element_product ( ) as usize ;
89
- if indices. len ( ) != expected_indices_length {
103
+ let expected_tile_data_length = chunk_size. element_product ( ) as usize ;
104
+ if tile_data. len ( ) != expected_tile_data_length {
90
105
warn ! (
91
- "Invalid indices length for tilemap chunk {} of size {}. Expected {}, got {}" ,
106
+ "Invalid tile data length for tilemap chunk {} of size {}. Expected {}, got {}" ,
92
107
entity,
93
108
chunk_size,
94
- indices . len ( ) ,
95
- expected_indices_length
109
+ expected_tile_data_length ,
110
+ tile_data . len ( ) ,
96
111
) ;
97
112
return ;
98
113
}
99
114
100
- let indices_image = make_chunk_image ( & chunk_size, & indices. 0 ) ;
115
+ let packed_tile_data: Vec < PackedTileData > =
116
+ tile_data. 0 . iter ( ) . map ( |& tile| tile. into ( ) ) . collect ( ) ;
101
117
102
- let display_size = ( chunk_size * tilemap_chunk. tile_display_size ) . as_vec2 ( ) ;
103
-
104
- let mesh_key: TilemapChunkMeshCacheKey = (
105
- chunk_size,
106
- FloatOrd ( display_size. x ) ,
107
- FloatOrd ( display_size. y ) ,
108
- FloatOrd ( anchor. as_vec ( ) . x ) ,
109
- FloatOrd ( anchor. as_vec ( ) . y ) ,
110
- ) ;
118
+ let tile_data_image = make_chunk_tile_data_image ( & chunk_size, & packed_tile_data) ;
111
119
112
120
let tilemap_chunk_mesh_cache = world. resource :: < TilemapChunkMeshCache > ( ) ;
113
- let mesh = if let Some ( mesh) = tilemap_chunk_mesh_cache. get ( & mesh_key) {
121
+
122
+ let mesh_size = chunk_size * tilemap_chunk. tile_display_size ;
123
+
124
+ let mesh = if let Some ( mesh) = tilemap_chunk_mesh_cache. get ( & mesh_size) {
114
125
mesh. clone ( )
115
126
} else {
116
127
let mut meshes = world. resource_mut :: < Assets < Mesh > > ( ) ;
117
- meshes. add ( make_chunk_mesh ( & chunk_size , & display_size , & anchor ) )
128
+ meshes. add ( Rectangle :: from_size ( mesh_size . as_vec2 ( ) ) )
118
129
} ;
119
130
120
131
let mut images = world. resource_mut :: < Assets < Image > > ( ) ;
121
- let indices = images. add ( indices_image ) ;
132
+ let tile_data = images. add ( tile_data_image ) ;
122
133
123
134
let mut materials = world. resource_mut :: < Assets < TilemapChunkMaterial > > ( ) ;
124
135
let material = materials. add ( TilemapChunkMaterial {
125
136
tileset,
126
- indices ,
137
+ tile_data ,
127
138
alpha_mode,
128
139
} ) ;
129
140
@@ -138,27 +149,30 @@ fn update_tilemap_chunk_indices(
138
149
(
139
150
Entity ,
140
151
& TilemapChunk ,
141
- & TilemapChunkIndices ,
152
+ & TilemapChunkTileData ,
142
153
& MeshMaterial2d < TilemapChunkMaterial > ,
143
154
) ,
144
- Changed < TilemapChunkIndices > ,
155
+ Changed < TilemapChunkTileData > ,
145
156
> ,
146
157
mut materials : ResMut < Assets < TilemapChunkMaterial > > ,
147
158
mut images : ResMut < Assets < Image > > ,
148
159
) {
149
- for ( chunk_entity, TilemapChunk { chunk_size, .. } , indices , material) in query {
150
- let expected_indices_length = chunk_size. element_product ( ) as usize ;
151
- if indices . len ( ) != expected_indices_length {
160
+ for ( chunk_entity, TilemapChunk { chunk_size, .. } , tile_data , material) in query {
161
+ let expected_tile_data_length = chunk_size. element_product ( ) as usize ;
162
+ if tile_data . len ( ) != expected_tile_data_length {
152
163
warn ! (
153
- "Invalid TilemapChunkIndices length for tilemap chunk {} of size {}. Expected {}, got {}" ,
164
+ "Invalid TilemapChunkTileData length for tilemap chunk {} of size {}. Expected {}, got {}" ,
154
165
chunk_entity,
155
166
chunk_size,
156
- indices . len( ) ,
157
- expected_indices_length
167
+ tile_data . len( ) ,
168
+ expected_tile_data_length
158
169
) ;
159
170
continue ;
160
171
}
161
172
173
+ let packed_tile_data: Vec < PackedTileData > =
174
+ tile_data. 0 . iter ( ) . map ( |& tile| tile. into ( ) ) . collect ( ) ;
175
+
162
176
// Getting the material mutably to trigger change detection
163
177
let Some ( material) = materials. get_mut ( material. id ( ) ) else {
164
178
warn ! (
@@ -167,101 +181,21 @@ fn update_tilemap_chunk_indices(
167
181
) ;
168
182
continue ;
169
183
} ;
170
- let Some ( indices_image ) = images. get_mut ( & material. indices ) else {
184
+ let Some ( tile_data_image ) = images. get_mut ( & material. tile_data ) else {
171
185
warn ! (
172
- "TilemapChunkMaterial indices image not found for tilemap chunk {}" ,
186
+ "TilemapChunkMaterial tile data image not found for tilemap chunk {}" ,
173
187
chunk_entity
174
188
) ;
175
189
continue ;
176
190
} ;
177
- let Some ( data) = indices_image . data . as_mut ( ) else {
191
+ let Some ( data) = tile_data_image . data . as_mut ( ) else {
178
192
warn ! (
179
- "TilemapChunkMaterial indices image data not found for tilemap chunk {}" ,
193
+ "TilemapChunkMaterial tile data image data not found for tilemap chunk {}" ,
180
194
chunk_entity
181
195
) ;
182
196
continue ;
183
197
} ;
184
198
data. clear ( ) ;
185
- data. extend (
186
- indices
187
- . iter ( )
188
- . copied ( )
189
- . flat_map ( |i| u16:: to_ne_bytes ( i. unwrap_or ( u16:: MAX ) ) ) ,
190
- ) ;
191
- }
192
- }
193
-
194
- fn make_chunk_image ( size : & UVec2 , indices : & [ Option < u16 > ] ) -> Image {
195
- Image {
196
- data : Some (
197
- indices
198
- . iter ( )
199
- . copied ( )
200
- . flat_map ( |i| u16:: to_ne_bytes ( i. unwrap_or ( u16:: MAX ) ) )
201
- . collect ( ) ,
202
- ) ,
203
- data_order : TextureDataOrder :: default ( ) ,
204
- texture_descriptor : TextureDescriptor {
205
- size : size. to_extents ( ) ,
206
- dimension : TextureDimension :: D2 ,
207
- format : TextureFormat :: R16Uint ,
208
- label : None ,
209
- mip_level_count : 1 ,
210
- sample_count : 1 ,
211
- usage : TextureUsages :: TEXTURE_BINDING | TextureUsages :: COPY_DST ,
212
- view_formats : & [ ] ,
213
- } ,
214
- sampler : ImageSampler :: nearest ( ) ,
215
- texture_view_descriptor : None ,
216
- asset_usage : RenderAssetUsages :: RENDER_WORLD | RenderAssetUsages :: MAIN_WORLD ,
217
- copy_on_resize : false ,
199
+ data. extend_from_slice ( bytemuck:: cast_slice ( & packed_tile_data) ) ;
218
200
}
219
201
}
220
-
221
- fn make_chunk_mesh ( size : & UVec2 , display_size : & Vec2 , anchor : & Anchor ) -> Mesh {
222
- let mut mesh = Mesh :: new (
223
- PrimitiveTopology :: TriangleList ,
224
- RenderAssetUsages :: RENDER_WORLD | RenderAssetUsages :: MAIN_WORLD ,
225
- ) ;
226
-
227
- let offset = display_size * ( Vec2 :: splat ( -0.5 ) - anchor. as_vec ( ) ) ;
228
-
229
- let num_quads = size. element_product ( ) as usize ;
230
- let quad_size = display_size / size. as_vec2 ( ) ;
231
-
232
- let mut positions = Vec :: with_capacity ( 4 * num_quads) ;
233
- let mut uvs = Vec :: with_capacity ( 4 * num_quads) ;
234
- let mut indices = Vec :: with_capacity ( 6 * num_quads) ;
235
-
236
- for y in 0 ..size. y {
237
- for x in 0 ..size. x {
238
- let i = positions. len ( ) as u32 ;
239
-
240
- let p0 = offset + quad_size * UVec2 :: new ( x, y) . as_vec2 ( ) ;
241
- let p1 = p0 + quad_size;
242
-
243
- positions. extend ( [
244
- Vec3 :: new ( p0. x , p0. y , 0.0 ) ,
245
- Vec3 :: new ( p1. x , p0. y , 0.0 ) ,
246
- Vec3 :: new ( p0. x , p1. y , 0.0 ) ,
247
- Vec3 :: new ( p1. x , p1. y , 0.0 ) ,
248
- ] ) ;
249
-
250
- uvs. extend ( [
251
- Vec2 :: new ( 0.0 , 1.0 ) ,
252
- Vec2 :: new ( 1.0 , 1.0 ) ,
253
- Vec2 :: new ( 0.0 , 0.0 ) ,
254
- Vec2 :: new ( 1.0 , 0.0 ) ,
255
- ] ) ;
256
-
257
- indices. extend ( [ i, i + 2 , i + 1 ] ) ;
258
- indices. extend ( [ i + 3 , i + 1 , i + 2 ] ) ;
259
- }
260
- }
261
-
262
- mesh. insert_attribute ( Mesh :: ATTRIBUTE_POSITION , positions) ;
263
- mesh. insert_attribute ( Mesh :: ATTRIBUTE_UV_0 , uvs) ;
264
- mesh. insert_indices ( Indices :: U32 ( indices) ) ;
265
-
266
- mesh
267
- }
0 commit comments