rogged/libs/tileset/tileset.c
NotAShelf ceb657add8
tileset: initial GPU texture atlas w/ procedural sprite painting
Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I7b6991f7342362033ad72ab4700fdb9f6a6a6964
2026-04-28 15:33:12 +03:00

118 lines
3 KiB
C

#include "tileset.h"
#include <stddef.h>
#include <stdio.h>
#include <string.h>
int tileset_init(Tileset *ts, int tile_w, int tile_h) {
if (ts == NULL)
return 0;
if (tile_w <= 0 || tile_h <= 0)
return 0;
memset(ts, 0, sizeof(Tileset));
ts->tile_w = tile_w;
ts->tile_h = tile_h;
// Compute grid dimensions to fit MAX_TILE_ID tiles
ts->atlas_cols = 4; // 4 columns
ts->atlas_rows = (MAX_TILE_ID + ts->atlas_cols - 1) / ts->atlas_cols; // round up
int atlas_w = ts->atlas_cols * tile_w;
int atlas_h = ts->atlas_rows * tile_h;
// Validate atlas dimensions are reasonable
if (atlas_w <= 0 || atlas_h <= 0 || atlas_w > 4096 || atlas_h > 4096)
return 0;
ts->render_target = LoadRenderTexture(atlas_w, atlas_h);
if (!IsRenderTextureValid(ts->render_target))
return 0;
// Clear to transparent so unpainted regions don't show artifacts
BeginTextureMode(ts->render_target);
ClearBackground(BLANK);
EndTextureMode();
ts->finalized = 0;
ts->tile_count = 0;
return 1;
}
int tileset_register(Tileset *ts, int id) {
if (ts == NULL)
return 0;
if (id < 0 || id >= MAX_TILE_ID)
return 0;
if (ts->render_target.id == 0)
return 0;
if (ts->finalized)
return 0;
if (ts->regions[id].width != 0)
return 0; // already registered
int col = id % ts->atlas_cols;
int row = id / ts->atlas_cols;
ts->regions[id] =
(Rectangle){(float)(col * ts->tile_w), (float)(row * ts->tile_h), (float)ts->tile_w, (float)ts->tile_h};
ts->tile_count++;
return 1;
}
int tileset_finalize(Tileset *ts) {
if (ts == NULL)
return 0;
if (ts->render_target.id == 0)
return 0;
if (ts->finalized)
return 1; // already finalized
// Convert RenderTexture to regular Texture2D
// RenderTexture textures are flipped vertically in raylib, so we need to handle that
Texture2D old_texture = ts->render_target.texture;
// Create a new texture from the render texture data
Image img = LoadImageFromTexture(old_texture);
if (img.data == NULL) {
return 0;
}
// Flip image vertically because RenderTexture is upside-down
ImageFlipVertical(&img);
Texture2D new_tex = LoadTextureFromImage(img);
UnloadImage(img);
if (new_tex.id == 0) {
return 0;
}
// Unload the old render texture and replace with the new regular texture
UnloadRenderTexture(ts->render_target);
ts->render_target.id = 0;
ts->atlas = new_tex;
ts->finalized = 1;
return 1;
}
Rectangle tileset_get_region(const Tileset *ts, int id) {
if (ts == NULL || id < 0 || id >= MAX_TILE_ID)
return (Rectangle){0, 0, 0, 0};
if (!ts->finalized)
return (Rectangle){0, 0, 0, 0};
return ts->regions[id];
}
void tileset_destroy(Tileset *ts) {
if (ts == NULL)
return;
if (ts->finalized) {
if (ts->atlas.id != 0)
UnloadTexture(ts->atlas);
} else {
if (ts->render_target.id != 0)
UnloadRenderTexture(ts->render_target);
}
memset(ts, 0, sizeof(Tileset));
}