yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
tilemap.cc
Go to the documentation of this file.
1#include "app/gfx/tilemap.h"
2
3#include <vector>
4
5#include "app/gfx/arena.h"
7#include "app/gfx/bitmap.h"
9#include "app/gfx/snes_tile.h"
10
11namespace yaze {
12namespace gfx {
13
14Tilemap CreateTilemap(IRenderer* renderer, std::vector<uint8_t> &data, int width, int height,
15 int tile_size, int num_tiles, SnesPalette &palette) {
16 Tilemap tilemap;
17 tilemap.tile_size.x = tile_size;
18 tilemap.tile_size.y = tile_size;
19 tilemap.map_size.x = num_tiles;
20 tilemap.map_size.y = num_tiles;
21 tilemap.atlas = Bitmap(width, height, 8, data);
22 tilemap.atlas.SetPalette(palette);
23
24 // Queue texture creation directly via Arena
25 if (tilemap.atlas.is_active() && tilemap.atlas.surface()) {
27 }
28
29 return tilemap;
30}
31
32void UpdateTilemap(IRenderer* renderer, Tilemap &tilemap, const std::vector<uint8_t> &data) {
33 tilemap.atlas.set_data(data);
34
35 // Queue texture update directly via Arena
36 if (tilemap.atlas.texture() && tilemap.atlas.is_active() && tilemap.atlas.surface()) {
38 } else if (!tilemap.atlas.texture() && tilemap.atlas.is_active() && tilemap.atlas.surface()) {
39 // Create if doesn't exist yet
41 }
42}
43
44void RenderTile(IRenderer* renderer, Tilemap &tilemap, int tile_id) {
45 // Validate tilemap state before proceeding
46 if (!tilemap.atlas.is_active() || tilemap.atlas.vector().empty()) {
47 return;
48 }
49
50 if (tile_id < 0) {
51 return;
52 }
53
54 // Get tile data without using problematic tile cache
55 auto tile_data = GetTilemapData(tilemap, tile_id);
56 if (tile_data.empty()) {
57 return;
58 }
59
60 // Note: Tile cache disabled to prevent std::move() related crashes
61}
62
63void RenderTile16(IRenderer* renderer, Tilemap &tilemap, int tile_id) {
64 // Validate tilemap state before proceeding
65 if (!tilemap.atlas.is_active() || tilemap.atlas.vector().empty()) {
66 return;
67 }
68
69 if (tile_id < 0) {
70 return;
71 }
72
73 int tiles_per_row = tilemap.atlas.width() / tilemap.tile_size.x;
74 if (tiles_per_row <= 0) {
75 return;
76 }
77
78 int tile_x = (tile_id % tiles_per_row) * tilemap.tile_size.x;
79 int tile_y = (tile_id / tiles_per_row) * tilemap.tile_size.y;
80
81 // Validate tile position
82 if (tile_x < 0 || tile_x >= tilemap.atlas.width() ||
83 tile_y < 0 || tile_y >= tilemap.atlas.height()) {
84 return;
85 }
86
87 // Note: Tile cache disabled to prevent std::move() related crashes
88}
89
90void UpdateTile16(IRenderer* renderer, Tilemap &tilemap, int tile_id) {
91 // Check if tile is cached
92 Bitmap* cached_tile = tilemap.tile_cache.GetTile(tile_id);
93 if (cached_tile) {
94 // Update cached tile data
95 int tiles_per_row = tilemap.atlas.width() / tilemap.tile_size.x;
96 int tile_x = (tile_id % tiles_per_row) * tilemap.tile_size.x;
97 int tile_y = (tile_id / tiles_per_row) * tilemap.tile_size.y;
98 std::vector<uint8_t> tile_data(tilemap.tile_size.x * tilemap.tile_size.y, 0x00);
99 int tile_data_offset = 0;
100 tilemap.atlas.Get16x16Tile(tile_x, tile_y, tile_data, tile_data_offset);
101 cached_tile->set_data(tile_data);
102
103 // Queue texture update directly via Arena
104 if (cached_tile->texture() && cached_tile->is_active()) {
106 }
107 } else {
108 // Tile not cached, render it fresh
109 RenderTile16(renderer, tilemap, tile_id);
110 }
111}
112
114 const std::vector<uint8_t> &data, int tile_id, int sheet_offset) {
115 const int tile_width = 8;
116 const int tile_height = 8;
117 const int buffer_width = 128;
118 const int sheet_height = 32;
119
120 const int tiles_per_row = buffer_width / tile_width;
121 const int rows_per_sheet = sheet_height / tile_height;
122 const int tiles_per_sheet = tiles_per_row * rows_per_sheet;
123
124 int sheet = (tile_id / tiles_per_sheet) % 4 + sheet_offset;
125 int position_in_sheet = tile_id % tiles_per_sheet;
126 int row_in_sheet = position_in_sheet / tiles_per_row;
127 int column_in_sheet = position_in_sheet % tiles_per_row;
128
129 assert(sheet >= sheet_offset && sheet <= sheet_offset + 3);
130
131 std::vector<uint8_t> tile_data(tile_width * tile_height);
132 for (int y = 0; y < tile_height; ++y) {
133 for (int x = 0; x < tile_width; ++x) {
134 int src_x = column_in_sheet * tile_width + x;
135 int src_y = (sheet * sheet_height) + (row_in_sheet * tile_height) + y;
136
137 int src_index = (src_y * buffer_width) + src_x;
138 int dest_index = y * tile_width + x;
139 tile_data[dest_index] = data[src_index];
140 }
141 }
142 return tile_data;
143}
144
145namespace {
146
147void MirrorTileDataVertically(std::vector<uint8_t> &tile_data) {
148 for (int y = 0; y < 4; ++y) {
149 for (int x = 0; x < 8; ++x) {
150 std::swap(tile_data[y * 8 + x], tile_data[(7 - y) * 8 + x]);
151 }
152 }
153}
154
155void MirrorTileDataHorizontally(std::vector<uint8_t> &tile_data) {
156 for (int y = 0; y < 8; ++y) {
157 for (int x = 0; x < 4; ++x) {
158 std::swap(tile_data[y * 8 + x], tile_data[y * 8 + (7 - x)]);
159 }
160 }
161}
162
163void ComposeAndPlaceTilePart(Tilemap &tilemap, const std::vector<uint8_t> &data,
164 const TileInfo &tile_info, int base_x, int base_y,
165 int sheet_offset) {
166 std::vector<uint8_t> tile_data =
167 FetchTileDataFromGraphicsBuffer(data, tile_info.id_, sheet_offset);
168
169 if (tile_info.vertical_mirror_) {
170 MirrorTileDataVertically(tile_data);
171 }
172 if (tile_info.horizontal_mirror_) {
174 }
175
176 for (int y = 0; y < 8; ++y) {
177 for (int x = 0; x < 8; ++x) {
178 int src_index = y * 8 + x;
179 int dest_x = base_x + x;
180 int dest_y = base_y + y;
181 int dest_index = (dest_y * tilemap.atlas.width()) + dest_x;
182 tilemap.atlas.WriteToPixel(dest_index, tile_data[src_index]);
183 }
184 };
185}
186} // namespace
187
188void ModifyTile16(Tilemap &tilemap, const std::vector<uint8_t> &data,
189 const TileInfo &top_left, const TileInfo &top_right,
190 const TileInfo &bottom_left, const TileInfo &bottom_right,
191 int sheet_offset, int tile_id) {
192 // Calculate the base position for this Tile16 in the full-size bitmap
193 int tiles_per_row = tilemap.atlas.width() / tilemap.tile_size.x;
194 int tile16_row = tile_id / tiles_per_row;
195 int tile16_column = tile_id % tiles_per_row;
196 int base_x = tile16_column * tilemap.tile_size.x;
197 int base_y = tile16_row * tilemap.tile_size.y;
198
199 // Compose and place each part of the Tile16
200 ComposeAndPlaceTilePart(tilemap, data, top_left, base_x, base_y,
201 sheet_offset);
202 ComposeAndPlaceTilePart(tilemap, data, top_right, base_x + 8, base_y,
203 sheet_offset);
204 ComposeAndPlaceTilePart(tilemap, data, bottom_left, base_x, base_y + 8,
205 sheet_offset);
206 ComposeAndPlaceTilePart(tilemap, data, bottom_right, base_x + 8, base_y + 8,
207 sheet_offset);
208
209 tilemap.tile_info[tile_id] = {top_left, top_right, bottom_left, bottom_right};
210}
211
212void ComposeTile16(Tilemap &tilemap, const std::vector<uint8_t> &data,
213 const TileInfo &top_left, const TileInfo &top_right,
214 const TileInfo &bottom_left, const TileInfo &bottom_right,
215 int sheet_offset) {
216 int num_tiles = tilemap.tile_info.size();
217 int tiles_per_row = tilemap.atlas.width() / tilemap.tile_size.x;
218 int tile16_row = num_tiles / tiles_per_row;
219 int tile16_column = num_tiles % tiles_per_row;
220 int base_x = tile16_column * tilemap.tile_size.x;
221 int base_y = tile16_row * tilemap.tile_size.y;
222
223 ComposeAndPlaceTilePart(tilemap, data, top_left, base_x, base_y,
224 sheet_offset);
225 ComposeAndPlaceTilePart(tilemap, data, top_right, base_x + 8, base_y,
226 sheet_offset);
227 ComposeAndPlaceTilePart(tilemap, data, bottom_left, base_x, base_y + 8,
228 sheet_offset);
229 ComposeAndPlaceTilePart(tilemap, data, bottom_right, base_x + 8, base_y + 8,
230 sheet_offset);
231
232 tilemap.tile_info.push_back({top_left, top_right, bottom_left, bottom_right});
233}
234
235std::vector<uint8_t> GetTilemapData(Tilemap &tilemap, int tile_id) {
236
237 // Comprehensive validation to prevent crashes
238 if (tile_id < 0) {
239 SDL_Log("GetTilemapData: Invalid tile_id %d (negative)", tile_id);
240 return std::vector<uint8_t>(256, 0); // Return empty 16x16 tile data
241 }
242
243 if (!tilemap.atlas.is_active()) {
244 SDL_Log("GetTilemapData: Atlas is not active for tile_id %d", tile_id);
245 return std::vector<uint8_t>(256, 0); // Return empty 16x16 tile data
246 }
247
248 if (tilemap.atlas.vector().empty()) {
249 SDL_Log("GetTilemapData: Atlas vector is empty for tile_id %d", tile_id);
250 return std::vector<uint8_t>(256, 0); // Return empty 16x16 tile data
251 }
252
253 if (tilemap.tile_size.x <= 0 || tilemap.tile_size.y <= 0) {
254 SDL_Log("GetTilemapData: Invalid tile size (%d, %d) for tile_id %d",
255 tilemap.tile_size.x, tilemap.tile_size.y, tile_id);
256 return std::vector<uint8_t>(256, 0); // Return empty 16x16 tile data
257 }
258
259 int tile_size = tilemap.tile_size.x;
260 int width = tilemap.atlas.width();
261 int height = tilemap.atlas.height();
262
263
264 // Validate atlas dimensions
265 if (width <= 0 || height <= 0) {
266 SDL_Log("GetTilemapData: Invalid atlas dimensions (%d, %d) for tile_id %d",
267 width, height, tile_id);
268 return std::vector<uint8_t>(tile_size * tile_size, 0);
269 }
270
271 // Calculate maximum possible tile_id based on atlas size
272 int tiles_per_row = width / tile_size;
273 int tiles_per_column = height / tile_size;
274 int max_tile_id = tiles_per_row * tiles_per_column - 1;
275
276 if (tile_id > max_tile_id) {
277 SDL_Log("GetTilemapData: tile_id %d exceeds maximum %d (atlas: %dx%d, tile_size: %d)",
278 tile_id, max_tile_id, width, height, tile_size);
279 return std::vector<uint8_t>(tile_size * tile_size, 0);
280 }
281
282 std::vector<uint8_t> data(tile_size * tile_size);
283
284
285 for (int ty = 0; ty < tile_size; ty++) {
286 for (int tx = 0; tx < tile_size; tx++) {
287 // Calculate atlas position more safely
288 int tile_row = tile_id / tiles_per_row;
289 int tile_col = tile_id % tiles_per_row;
290 int atlas_x = tile_col * tile_size + tx;
291 int atlas_y = tile_row * tile_size + ty;
292 int atlas_index = atlas_y * width + atlas_x;
293
294 // Comprehensive bounds checking
295 if (atlas_x >= 0 && atlas_x < width &&
296 atlas_y >= 0 && atlas_y < height &&
297 atlas_index >= 0 && atlas_index < static_cast<int>(tilemap.atlas.vector().size())) {
298 uint8_t value = tilemap.atlas.vector()[atlas_index];
299 data[ty * tile_size + tx] = value;
300 } else {
301 SDL_Log("GetTilemapData: Atlas position (%d, %d) or index %d out of bounds (atlas: %dx%d, size: %zu)",
302 atlas_x, atlas_y, atlas_index, width, height, tilemap.atlas.vector().size());
303 data[ty * tile_size + tx] = 0; // Default to 0 if out of bounds
304 }
305 }
306 }
307
308 return data;
309}
310
311void RenderTilesBatch(IRenderer* renderer, Tilemap& tilemap, const std::vector<int>& tile_ids,
312 const std::vector<std::pair<float, float>>& positions,
313 const std::vector<std::pair<float, float>>& scales) {
314 if (tile_ids.empty() || positions.empty() || tile_ids.size() != positions.size()) {
315 return;
316 }
317
318 ScopedTimer timer("tilemap_batch_render");
319
320 // Initialize atlas renderer if not already done
321 auto& atlas_renderer = AtlasRenderer::Get();
322 if (!renderer) {
323 // For now, we'll use the existing rendering approach
324 // In a full implementation, we'd get the renderer from the core system
325 return;
326 }
327
328 // Prepare render commands
329 std::vector<RenderCommand> render_commands;
330 render_commands.reserve(tile_ids.size());
331
332 for (size_t i = 0; i < tile_ids.size(); ++i) {
333 int tile_id = tile_ids[i];
334 float x = positions[i].first;
335 float y = positions[i].second;
336
337 // Get scale factors (default to 1.0 if not provided)
338 float scale_x = 1.0F;
339 float scale_y = 1.0F;
340 if (i < scales.size()) {
341 scale_x = scales[i].first;
342 scale_y = scales[i].second;
343 }
344
345 // Try to get tile from cache first
346 Bitmap* cached_tile = tilemap.tile_cache.GetTile(tile_id);
347 if (!cached_tile) {
348 // Create and cache the tile if not found
349 gfx::Bitmap new_tile = gfx::Bitmap(
350 tilemap.tile_size.x, tilemap.tile_size.y, 8,
351 gfx::GetTilemapData(tilemap, tile_id), tilemap.atlas.palette());
352 tilemap.tile_cache.CacheTile(tile_id, std::move(new_tile));
353 cached_tile = tilemap.tile_cache.GetTile(tile_id);
354 if (cached_tile) {
355 cached_tile->CreateTexture();
356 }
357 }
358
359 if (cached_tile && cached_tile->is_active()) {
360 // Queue texture creation if needed
361 if (!cached_tile->texture() && cached_tile->surface()) {
363 }
364
365 // Add to atlas renderer
366 int atlas_id = atlas_renderer.AddBitmap(*cached_tile);
367 if (atlas_id >= 0) {
368 render_commands.emplace_back(atlas_id, x, y, scale_x, scale_y);
369 }
370 }
371 }
372
373 // Render all commands in batch
374 if (!render_commands.empty()) {
375 atlas_renderer.RenderBatch(render_commands);
376 }
377}
378
379} // namespace gfx
380} // namespace yaze
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
Definition arena.cc:32
static Arena & Get()
Definition arena.cc:15
static AtlasRenderer & Get()
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:66
const SnesPalette & palette() const
Definition bitmap.h:251
void WriteToPixel(int position, uint8_t value)
Write a value to a pixel at the given position.
Definition bitmap.cc:377
TextureHandle texture() const
Definition bitmap.h:260
const std::vector< uint8_t > & vector() const
Definition bitmap.h:261
void CreateTexture()
Creates the underlying SDL_Texture to be displayed.
Definition bitmap.cc:242
bool is_active() const
Definition bitmap.h:264
int height() const
Definition bitmap.h:254
void set_data(const std::vector< uint8_t > &data)
Definition bitmap.cc:645
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap.
Definition bitmap.cc:292
int width() const
Definition bitmap.h:253
SDL_Surface * surface() const
Definition bitmap.h:259
void Get16x16Tile(int tile_x, int tile_y, std::vector< uint8_t > &tile_data, int &tile_data_offset)
Extract a 16x16 tile from the bitmap (SNES metatile size)
Definition bitmap.cc:484
Defines an abstract interface for all rendering operations.
Definition irenderer.h:35
RAII timer for automatic timing management.
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
SNES 16-bit tile metadata container.
Definition snes_tile.h:50
void ComposeAndPlaceTilePart(Tilemap &tilemap, const std::vector< uint8_t > &data, const TileInfo &tile_info, int base_x, int base_y, int sheet_offset)
Definition tilemap.cc:163
void MirrorTileDataVertically(std::vector< uint8_t > &tile_data)
Definition tilemap.cc:147
void MirrorTileDataHorizontally(std::vector< uint8_t > &tile_data)
Definition tilemap.cc:155
void RenderTile16(IRenderer *renderer, Tilemap &tilemap, int tile_id)
Definition tilemap.cc:63
void ModifyTile16(Tilemap &tilemap, const std::vector< uint8_t > &data, const TileInfo &top_left, const TileInfo &top_right, const TileInfo &bottom_left, const TileInfo &bottom_right, int sheet_offset, int tile_id)
Definition tilemap.cc:188
void UpdateTilemap(IRenderer *renderer, Tilemap &tilemap, const std::vector< uint8_t > &data)
Definition tilemap.cc:32
void UpdateTile16(IRenderer *renderer, Tilemap &tilemap, int tile_id)
Definition tilemap.cc:90
std::vector< uint8_t > GetTilemapData(Tilemap &tilemap, int tile_id)
Definition tilemap.cc:235
Tilemap CreateTilemap(IRenderer *renderer, std::vector< uint8_t > &data, int width, int height, int tile_size, int num_tiles, SnesPalette &palette)
Definition tilemap.cc:14
void RenderTilesBatch(IRenderer *renderer, Tilemap &tilemap, const std::vector< int > &tile_ids, const std::vector< std::pair< float, float > > &positions, const std::vector< std::pair< float, float > > &scales)
Render multiple tiles using atlas rendering for improved performance.
Definition tilemap.cc:311
std::vector< uint8_t > FetchTileDataFromGraphicsBuffer(const std::vector< uint8_t > &data, int tile_id, int sheet_offset)
Definition tilemap.cc:113
void RenderTile(IRenderer *renderer, Tilemap &tilemap, int tile_id)
Definition tilemap.cc:44
void ComposeTile16(Tilemap &tilemap, const std::vector< uint8_t > &data, const TileInfo &top_left, const TileInfo &top_right, const TileInfo &bottom_left, const TileInfo &bottom_right, int sheet_offset)
Definition tilemap.cc:212
Main namespace for the application.
int y
Y coordinate or height.
Definition tilemap.h:20
int x
X coordinate or width.
Definition tilemap.h:19
void CacheTile(int tile_id, Bitmap &&bitmap)
Cache a tile bitmap.
Definition tilemap.h:58
Bitmap * GetTile(int tile_id)
Get a cached tile by ID.
Definition tilemap.h:42
Tilemap structure for SNES tile-based graphics management.
Definition tilemap.h:109
Pair tile_size
Size of individual tiles (8x8 or 16x16)
Definition tilemap.h:113
TileCache tile_cache
Smart tile cache with LRU eviction.
Definition tilemap.h:111
Pair map_size
Size of tilemap in tiles.
Definition tilemap.h:114
Bitmap atlas
Master bitmap containing all tiles.
Definition tilemap.h:110
std::vector< std::array< gfx::TileInfo, 4 > > tile_info
Tile metadata (4 tiles per 16x16)
Definition tilemap.h:112