Skip to content

Commit 5e026ef

Browse files
committed
TilemapChunk single quad; TileData (color/visibility)
1 parent 1a410ef commit 5e026ef

File tree

4 files changed

+193
-209
lines changed

4 files changed

+193
-209
lines changed
Lines changed: 67 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
use crate::{AlphaMode2d, Anchor, MeshMaterial2d};
1+
use crate::{AlphaMode2d, MeshMaterial2d};
22
use bevy_app::{App, Plugin, Update};
3-
use bevy_asset::{Assets, Handle, RenderAssetUsages};
3+
use bevy_asset::{Assets, Handle};
4+
use bevy_color::Color;
45
use bevy_derive::{Deref, DerefMut};
56
use bevy_ecs::{
67
component::Component,
@@ -11,15 +12,11 @@ use bevy_ecs::{
1112
system::{Query, ResMut},
1213
world::DeferredWorld,
1314
};
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};
1617
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;
2320
use tracing::warn;
2421

2522
mod tilemap_chunk_material;
@@ -37,16 +34,13 @@ impl Plugin for TilemapChunkPlugin {
3734
}
3835
}
3936

40-
type TilemapChunkMeshCacheKey = (UVec2, FloatOrd, FloatOrd, FloatOrd, FloatOrd);
41-
4237
/// A resource storing the meshes for each tilemap chunk size.
4338
#[derive(Resource, Default, Deref, DerefMut)]
44-
pub struct TilemapChunkMeshCache(HashMap<TilemapChunkMeshCacheKey, Handle<Mesh>>);
39+
pub struct TilemapChunkMeshCache(HashMap<UVec2, Handle<Mesh>>);
4540

4641
/// A component representing a chunk of a tilemap.
4742
/// Each chunk is a rectangular section of tiles that is rendered as a single mesh.
4843
#[derive(Component, Clone, Debug, Default)]
49-
#[require(Anchor)]
5044
#[component(immutable, on_insert = on_insert_tilemap_chunk)]
5145
pub struct TilemapChunk {
5246
/// The size of the chunk in tiles
@@ -60,10 +54,36 @@ pub struct TilemapChunk {
6054
pub alpha_mode: AlphaMode2d,
6155
}
6256

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+
6383
/// Component storing the indices of tiles within a chunk.
6484
/// Each index corresponds to a specific tile in the tileset.
6585
#[derive(Component, Clone, Debug, Deref, DerefMut)]
66-
pub struct TilemapChunkIndices(pub Vec<Option<u16>>);
86+
pub struct TilemapChunkTileData(pub Vec<Option<TileData>>);
6787

6888
fn on_insert_tilemap_chunk(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
6989
let Some(tilemap_chunk) = world.get::<TilemapChunk>(entity) else {
@@ -75,55 +95,46 @@ fn on_insert_tilemap_chunk(mut world: DeferredWorld, HookContext { entity, .. }:
7595
let alpha_mode = tilemap_chunk.alpha_mode;
7696
let tileset = tilemap_chunk.tileset.clone();
7797

78-
let Some(indices) = world.get::<TilemapChunkIndices>(entity) else {
98+
let Some(tile_data) = world.get::<TilemapChunkTileData>(entity) else {
7999
warn!("TilemapChunkIndices not found for tilemap chunk {}", entity);
80100
return;
81101
};
82102

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 {
90105
warn!(
91-
"Invalid indices length for tilemap chunk {} of size {}. Expected {}, got {}",
106+
"Invalid tile data length for tilemap chunk {} of size {}. Expected {}, got {}",
92107
entity,
93108
chunk_size,
94-
indices.len(),
95-
expected_indices_length
109+
expected_tile_data_length,
110+
tile_data.len(),
96111
);
97112
return;
98113
}
99114

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();
101117

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);
111119

112120
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) {
114125
mesh.clone()
115126
} else {
116127
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()))
118129
};
119130

120131
let mut images = world.resource_mut::<Assets<Image>>();
121-
let indices = images.add(indices_image);
132+
let tile_data = images.add(tile_data_image);
122133

123134
let mut materials = world.resource_mut::<Assets<TilemapChunkMaterial>>();
124135
let material = materials.add(TilemapChunkMaterial {
125136
tileset,
126-
indices,
137+
tile_data,
127138
alpha_mode,
128139
});
129140

@@ -138,27 +149,30 @@ fn update_tilemap_chunk_indices(
138149
(
139150
Entity,
140151
&TilemapChunk,
141-
&TilemapChunkIndices,
152+
&TilemapChunkTileData,
142153
&MeshMaterial2d<TilemapChunkMaterial>,
143154
),
144-
Changed<TilemapChunkIndices>,
155+
Changed<TilemapChunkTileData>,
145156
>,
146157
mut materials: ResMut<Assets<TilemapChunkMaterial>>,
147158
mut images: ResMut<Assets<Image>>,
148159
) {
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 {
152163
warn!(
153-
"Invalid TilemapChunkIndices length for tilemap chunk {} of size {}. Expected {}, got {}",
164+
"Invalid TilemapChunkTileData length for tilemap chunk {} of size {}. Expected {}, got {}",
154165
chunk_entity,
155166
chunk_size,
156-
indices.len(),
157-
expected_indices_length
167+
tile_data.len(),
168+
expected_tile_data_length
158169
);
159170
continue;
160171
}
161172

173+
let packed_tile_data: Vec<PackedTileData> =
174+
tile_data.0.iter().map(|&tile| tile.into()).collect();
175+
162176
// Getting the material mutably to trigger change detection
163177
let Some(material) = materials.get_mut(material.id()) else {
164178
warn!(
@@ -167,101 +181,21 @@ fn update_tilemap_chunk_indices(
167181
);
168182
continue;
169183
};
170-
let Some(indices_image) = images.get_mut(&material.indices) else {
184+
let Some(tile_data_image) = images.get_mut(&material.tile_data) else {
171185
warn!(
172-
"TilemapChunkMaterial indices image not found for tilemap chunk {}",
186+
"TilemapChunkMaterial tile data image not found for tilemap chunk {}",
173187
chunk_entity
174188
);
175189
continue;
176190
};
177-
let Some(data) = indices_image.data.as_mut() else {
191+
let Some(data) = tile_data_image.data.as_mut() else {
178192
warn!(
179-
"TilemapChunkMaterial indices image data not found for tilemap chunk {}",
193+
"TilemapChunkMaterial tile data image data not found for tilemap chunk {}",
180194
chunk_entity
181195
);
182196
continue;
183197
};
184198
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));
218200
}
219201
}
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

Comments
 (0)