From 0d78ec0e53d9824a029840e74bf0721ed2187e6a Mon Sep 17 00:00:00 2001 From: davidovski Date: Sun, 23 Jul 2023 15:30:42 +0200 Subject: add chunk caching --- src/chunkedtiledmap.c | 263 ------------------------------------------------ src/chunkedtiledmap.h | 24 ----- src/editor.c | 21 ++-- src/tiled.c | 16 ++- src/tiled.h | 8 +- src/tiledmap.c | 272 +++++++++++++++++++++++++++++++++++++------------- src/tiledmap.h | 36 ++++--- 7 files changed, 255 insertions(+), 385 deletions(-) delete mode 100644 src/chunkedtiledmap.c delete mode 100644 src/chunkedtiledmap.h (limited to 'src') diff --git a/src/chunkedtiledmap.c b/src/chunkedtiledmap.c deleted file mode 100644 index a3019af..0000000 --- a/src/chunkedtiledmap.c +++ /dev/null @@ -1,263 +0,0 @@ -#include -#include -#include -#include - -#include "chunkedtiledmap.h" - -const int endian = 1; -#define is_bigendian() ( (*(char*)&endian) == 0 ) - - -void textureFromPixels(Texture2D *texOut, Color *pixels, int width, int height) { - Image checkedIm = { - .data = pixels, - .width = width, - .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 - }; - - *texOut = LoadTextureFromImage(checkedIm); -} - - -//! read a big endian bytes from file -int readb(char * out, size_t noBytes, FILE * file) { - if (!fread(out, (size_t)1, (size_t) noBytes, file)) - return 1; - - if (is_bigendian()) - return 0; - - int tmp; - // reverse byte order - for(int i = 0; i < noBytes/2; i++) { - tmp = out[i]; - out[i] = out[noBytes-i-1]; - out[noBytes-i-1] = tmp; - } - - return 0; -} - -int writeb(char * in, size_t noBytes, FILE * file) { - if (!is_bigendian()) { - int tmp; - // reverse byte order - for(int i = 0; i < noBytes/2; i++) { - tmp = in[i]; - in[i] = in[noBytes-i-1]; - in[noBytes-i-1] = tmp; - } - - } - - return fwrite(in, (size_t)1, (size_t) noBytes, file); -} - -void buildChunkTree(ChunkedTiledMap *tiledMap) { - // initialise chunk tree - tiledMap->chunkTree = NULL; - - size_t chunkSizeBytes = tiledMap->chunkWidth * tiledMap->chunkHeight; - // calculate how much space there is until the end of the file - // TODO save the number of chunks total to avoid having to jump around to find referenceso - // or just keep reading chunks until eof or other descriptor - long chunksStart = ftell(tiledMap->file); - fseek(tiledMap->file, 0, SEEK_END); - long totalChunksSize = ftell(tiledMap->file) - chunksStart; - - fseek(tiledMap->file, chunksStart, SEEK_SET); - - for (int n = 0; n < totalChunksSize; n += chunkSizeBytes + 8) { - int x, y; - readb((char *)&x, 4, tiledMap->file); - readb((char *)&y, 4, tiledMap->file); - long pointer = ftell(tiledMap->file); - - kdtree_insert(&tiledMap->chunkTree, x, y, (char *) pointer); - fseek(tiledMap->file, chunkSizeBytes, SEEK_CUR); - } -} - -ChunkedTiledMap openTiledMap(char * filename) { - ChunkedTiledMap tiledMap; - - if (!(tiledMap.file = fopen(filename, "r+b"))) { - fprintf(stderr, "Failed to load %s\n", filename); - } - - FILE * file = tiledMap.file; - - // skip header - fseek(file, 10, SEEK_CUR); - // 4 bytes for int width - readb((char *)&tiledMap.chunkWidth, 4, file); - // 4 bytes for int height - readb((char *)&tiledMap.chunkHeight, 4, file); - - // read the pixel size of each tile - readb((char *)&tiledMap.tileSize, 4, file); - - // read the atlas size - readb((char *)&tiledMap.atlasSize[0], 4, file); - readb((char *)&tiledMap.atlasSize[1], 4, file); - - // read the atlas itself - size_t atlasSizeBytes = tiledMap.atlasSize[0]*tiledMap.tileSize*tiledMap.atlasSize[1]*tiledMap.tileSize*4; - tiledMap.atlasData = malloc(atlasSizeBytes); - fread(tiledMap.atlasData, atlasSizeBytes, (size_t) 1, file); - - tiledMap.tileCount = tiledMap.atlasSize[0]*tiledMap.atlasSize[1] + 1; - - buildChunkTree(&tiledMap); - return tiledMap; -} - -//! write a chunk to the file -char* writeChunk(ChunkedTiledMap *tiledMap, int x, int y, char * chunk) { - size_t chunkSizeBytes = tiledMap->chunkWidth * tiledMap->chunkHeight; - - - long pos = (long) kdtree_search(tiledMap->chunkTree, x, y); - - fseek(tiledMap->file, pos, SEEK_SET); - fwrite(chunk, 1, chunkSizeBytes, tiledMap->file); - return chunk; -} - -char* loadChunk(ChunkedTiledMap tiledMap, int x, int y) { - // TODO add caching for this - size_t chunkSizeBytes = tiledMap.chunkWidth * tiledMap.chunkHeight; - char * chunk = malloc(chunkSizeBytes); - - long pos = (long) kdtree_search(tiledMap.chunkTree, x, y); - // if this chunk is not indexed, return NULL - if (pos == 0) - return NULL; - - fseek(tiledMap.file, pos, SEEK_SET); - fread(chunk, 1, chunkSizeBytes, tiledMap.file); - return chunk; -} - -char * appendChunk(ChunkedTiledMap *tiledMap, int x, int y, char * chunk) { - // TODO does this actually need tiledMap as a pointer - size_t chunkSizeBytes = tiledMap->chunkWidth * tiledMap->chunkHeight; - - fseek(tiledMap->file, 0, SEEK_END); - - // calculate position before writing - long pos = ftell(tiledMap->file) + 8; - kdtree_insert(&tiledMap->chunkTree, x, y, (char *)pos); - - int chunkx, chunky; - writeb((char *) &x, 4, tiledMap->file); - writeb((char *) &y, 4, tiledMap->file); - fwrite(chunk, 1, chunkSizeBytes, tiledMap->file); - - return chunk; -} - -char * appendEmptyChunk(ChunkedTiledMap * tiledMap, int x, int y) { - char * chunk = calloc(tiledMap->chunkWidth*tiledMap->chunkHeight, 1); - return appendChunk(tiledMap, x, y, chunk); -} - -char getChunkedTile(ChunkedTiledMap tiledMap, int x, int y) { - // TODO put this calculation in function - int inChunkX = x % tiledMap.chunkWidth; - int inChunkY = y % tiledMap.chunkHeight; - int chunkX = (x - inChunkX) / tiledMap.chunkWidth; - int chunkY = (y - inChunkY) / tiledMap.chunkHeight; - - char * chunk = loadChunk(tiledMap, chunkX, chunkY); - if (chunk == NULL) - return 0; - - char v = chunk[inChunkY * tiledMap.chunkWidth + inChunkX]; - return v; -} - -char setChunkedTile(ChunkedTiledMap * tiledMap, int x, int y, char value) { - int inChunkX = x % tiledMap->chunkWidth; - int inChunkY = y % tiledMap->chunkHeight; - int chunkX = (x - inChunkX) / tiledMap->chunkWidth; - int chunkY = (y - inChunkY) / tiledMap->chunkHeight; - - char * chunk = loadChunk(*tiledMap, chunkX, chunkY); - if (chunk == NULL) - chunk = appendEmptyChunk(tiledMap, chunkX, chunkY); - - chunk[inChunkY * tiledMap->chunkWidth + inChunkX] = value; - - writeChunk(tiledMap, chunkX, chunkY, chunk); - - return value; -} - - -void writeTiledMapHeader(ChunkedTiledMap tiledMap) { - FILE * file = tiledMap.file; - size_t atlasSizeBytes = tiledMap.atlasSize[0]*tiledMap.tileSize*tiledMap.atlasSize[1]*tiledMap.tileSize*4; - - // write header information from the start of the file - fseek(file, 0, SEEK_SET); - fwrite("TILEFILEv3", 10, 1, file); - - writeb((char *) &tiledMap.chunkWidth, 4, file); - writeb((char *) &tiledMap.chunkHeight, 4, file); - - writeb((char *) &tiledMap.tileSize, 4, file); - writeb((char *) &tiledMap.atlasSize[0], 4, file); - writeb((char *) &tiledMap.atlasSize[1], 4, file); - - fwrite(tiledMap.atlasData, 1, atlasSizeBytes, file); - // since chunks are already directly written here, do not write anything else - // TODO when caching, commit everything left in cache here -} - -ChunkedTiledMap openNewTiledMap(char * filename, Image atlas, int tileSize, int chunkWidth, int chunkHeight, int width, int height) { - ChunkedTiledMap tiledMap; - tiledMap.chunkWidth = chunkWidth; - tiledMap.chunkHeight = chunkHeight; - - tiledMap.tileSize = tileSize; - - tiledMap.atlasSize[0] = atlas.width / tileSize; - tiledMap.atlasSize[1] = atlas.height / tileSize; - - tiledMap.atlasData = LoadImageColors(atlas); - tiledMap.tileCount = tiledMap.atlasSize[0]*tiledMap.atlasSize[1] + 1; - - tiledMap.chunkTree = NULL; - - if (!(tiledMap.file = fopen(filename, "wb"))) { - fprintf(stderr, "Failed to load %s\n", filename); - } - - writeTiledMapHeader(tiledMap); - for (int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) { - char * chunk = calloc(chunkWidth*chunkHeight, 1); - appendChunk(&tiledMap, x, y, chunk); - } - } - - // reopen the file in read+write mode - fclose(tiledMap.file); - - if (!(tiledMap.file = fopen(filename, "r+b"))) { - fprintf(stderr, "Failed to load %s\n", filename); - } - - return tiledMap; -} - -void closeTiledMap(ChunkedTiledMap tiledMap) { - kdtree_free(&tiledMap.chunkTree); - UnloadImageColors(tiledMap.atlasData); - fclose(tiledMap.file); -} - diff --git a/src/chunkedtiledmap.h b/src/chunkedtiledmap.h deleted file mode 100644 index 8bc2aaa..0000000 --- a/src/chunkedtiledmap.h +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include "kdtree.h" - -typedef struct ChunkedTiledMap { - FILE * file; - int chunkWidth; - int chunkHeight; - int tileSize; - int atlasSize[2]; - int tileCount; - Color * atlasData; - kdtree_t * chunkTree; -} ChunkedTiledMap; - -void textureFromPixels(Texture2D *texOut, Color *pixels, int width, int height); -ChunkedTiledMap openTiledMap(char * filename); -char * loadChunk(ChunkedTiledMap tiledMap, int x, int y); -char getChunkedTile(ChunkedTiledMap tiledMap, int x, int y); -char setChunkedTile(ChunkedTiledMap * tiledMap, int x, int y, char value); -char * appendChunk(ChunkedTiledMap * tiledMap, int x, int y, char * chunk); -void writeTiledMapHeader(ChunkedTiledMap tiledMap); -ChunkedTiledMap openNewTiledMap(char * filename, Image atlas, int tileSize, int chunkWidth, int chunkHeight, int width, int height); -void closeTiledMap(ChunkedTiledMap tiledMap); - diff --git a/src/editor.c b/src/editor.c index 46afe37..2c9de3a 100644 --- a/src/editor.c +++ b/src/editor.c @@ -23,7 +23,7 @@ void drawOverlay(Tiled tiled) { void modifyTile(Tiled *tiled, int tile) { setChunkedTile(&tiled->tiledMap, selectedTile[0], selectedTile[1], tile); - redrawTiledMap(*tiled); + redrawTile(*tiled, selectedTile[0], selectedTile[1]); } void setDrawMode(Tiled *tiled, int tile) { @@ -34,10 +34,10 @@ void setDrawMode(Tiled *tiled, int tile) { void handleInputs(Tiled *tiled) { if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) - setDrawMode(tiled, getChunkedTile(tiled->tiledMap, selectedTile[0], selectedTile[1]) + 1); + setDrawMode(tiled, getChunkedTile(&tiled->tiledMap, selectedTile[0], selectedTile[1]) + 1); if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) - setDrawMode(tiled, getChunkedTile(tiled->tiledMap, selectedTile[0], selectedTile[1]) - 1); + setDrawMode(tiled, getChunkedTile(&tiled->tiledMap, selectedTile[0], selectedTile[1]) - 1); if (IsMouseButtonPressed(MOUSE_BUTTON_MIDDLE)) setDrawMode(tiled, 0); @@ -69,11 +69,11 @@ void update(Tiled *tiled) { handleInputs(tiled); } -ChunkedTiledMap launchEditor(ChunkedTiledMap tiledMap) { +void launchEditor(TiledMap * tiledMap) { SetConfigFlags(FLAG_WINDOW_RESIZABLE); InitWindow(SCREEN_W, SCREEN_H, "tiled"); - Tiled tiled = initTiled(tiledMap); + Tiled tiled = initTiled(*tiledMap); while (!WindowShouldClose()) { update(&tiled); @@ -92,7 +92,6 @@ ChunkedTiledMap launchEditor(ChunkedTiledMap tiledMap) { unloadTiled(&tiled); CloseWindow(); - return tiledMap; } void printUsage(char *progname) { @@ -104,8 +103,8 @@ int main(int argc, char *argv[]) { char * tiledFilePath; const char * atlasFilePath = NULL; int tileSize = 16; - int mapSize = 2; - int chunkSize = 2; + int mapSize = 1; + int chunkSize = 16; int flags, opt; while ((opt = getopt(argc, argv, "c:s:a:h")) != -1) { @@ -126,7 +125,7 @@ int main(int argc, char *argv[]) { tiledFilePath = argv[optind]; - ChunkedTiledMap tiledMap; + TiledMap tiledMap; if (access(tiledFilePath, F_OK)) { if (atlasFilePath == NULL) { fprintf(stderr, "Atlas file must be specified!\n"); @@ -139,6 +138,6 @@ int main(int argc, char *argv[]) { tiledMap = openTiledMap(tiledFilePath); } - launchEditor(tiledMap); - closeTiledMap(tiledMap); + launchEditor(&tiledMap); + closeTiledMap(&tiledMap); } diff --git a/src/tiled.c b/src/tiled.c index f490d6a..0628523 100644 --- a/src/tiled.c +++ b/src/tiled.c @@ -48,15 +48,25 @@ Vector2 translateTiledScreenPosition(Tiled tiled, Vector2 tiledPos) { }; } +void redrawTile(Tiled tiled, int x, int y) { + BeginTextureMode(tiled.tilemapTexture); + unsigned char v = getChunkedTile(&tiled.tiledMap, x, y); + Color c = (Color){ + v, 0, 0, 255 + }; + DrawPixel(x, tiled.mapSize[1] - y - 1, c); + EndTextureMode(); +} + void redrawTiledMap(Tiled tiled) { BeginTextureMode(tiled.tilemapTexture); for (int y = 0; y < tiled.mapSize[1]; y++) { for (int x = 0; x < tiled.mapSize[0]; x++) { - unsigned char v = getChunkedTile(tiled.tiledMap, x, tiled.mapSize[1] - y - 1); + unsigned char v = getChunkedTile(&tiled.tiledMap, x, y); Color c = (Color){ v, 0, 0, 255 }; - DrawPixel(x, y, c); + DrawPixel(x, tiled.mapSize[1] - y - 1, c); } } EndTextureMode(); @@ -64,7 +74,7 @@ void redrawTiledMap(Tiled tiled) { } -Tiled initTiled(ChunkedTiledMap tiledMap) { +Tiled initTiled(TiledMap tiledMap) { Tiled tiled; tiled.tiledMap = tiledMap; diff --git a/src/tiled.h b/src/tiled.h index 11518e7..c3d0950 100644 --- a/src/tiled.h +++ b/src/tiled.h @@ -1,12 +1,12 @@ #include -#include "chunkedtiledmap.h" +#include "tiledmap.h" #define SCREEN_W 1280 #define SCREEN_H 720 typedef struct Tiled { - ChunkedTiledMap tiledMap; + TiledMap tiledMap; float zoom; Vector2 offset; @@ -27,14 +27,14 @@ typedef struct Tiled { int atlasTextureLoc; int tilemapTextureLoc; - } Tiled; void updateTiledCamera(Tiled *tiled); Vector2 translateTiledPosition(Tiled tiled, Vector2 screenPos); Vector2 translateTiledScreenPosition(Tiled tiled, Vector2 tiledPos); -Tiled initTiled(ChunkedTiledMap tiledMap); +Tiled initTiled(TiledMap tiledMap); void drawTiled(Tiled *tiled); void unloadTiled(Tiled *tiled); void redrawTiledMap(Tiled tiled); +void redrawTile(Tiled tiled, int x, int y); diff --git a/src/tiledmap.c b/src/tiledmap.c index e274a6e..1c6964b 100644 --- a/src/tiledmap.c +++ b/src/tiledmap.c @@ -1,12 +1,12 @@ #include #include #include -#include +#include #include "tiledmap.h" -const int i = 1; -#define is_bigendian() ( (*(char*)&i) == 0 ) +const int endian = 1; +#define is_bigendian() ( (*(char*)&endian) == 0 ) void textureFromPixels(Texture2D *texOut, Color *pixels, int width, int height) { Image checkedIm = { @@ -20,16 +20,26 @@ void textureFromPixels(Texture2D *texOut, Color *pixels, int width, int height) *texOut = LoadTextureFromImage(checkedIm); } -//! read rgba image from file -void readrgba(Texture2D *loc, int width, int height, FILE *file) { - Color *pixels = malloc(width*height*4); - fread(pixels, (size_t) width*height*4, (size_t) 1, file); - textureFromPixels(loc, pixels, width, height); -} +//! read a big endian bytes from file +int readb(char * out, size_t noBytes, FILE * file) { + if (!fread(out, (size_t)1, (size_t) noBytes, file)) + return 1; + if (is_bigendian()) + return 0; + + int tmp; + // reverse byte order + for(int i = 0; i < noBytes/2; i++) { + tmp = out[i]; + out[i] = out[noBytes-i-1]; + out[noBytes-i-1] = tmp; + } + + return 0; +} -//! write a big endian bytes from file int writeb(char * in, size_t noBytes, FILE * file) { if (!is_bigendian()) { int tmp; @@ -45,53 +55,50 @@ int writeb(char * in, size_t noBytes, FILE * file) { return fwrite(in, (size_t)1, (size_t) noBytes, file); } -//! read a big endian bytes from file -int readb(char * out, size_t noBytes, FILE * file) { - if (!fread(out, (size_t)1, (size_t) noBytes, file)) - return 1; +void buildChunkTree(TiledMap *tiledMap) { + // initialise chunk tree + tiledMap->chunkTree = NULL; - if (is_bigendian()) - return 0; + size_t chunkSizeBytes = tiledMap->chunkWidth * tiledMap->chunkHeight; + // calculate how much space there is until the end of the file + // TODO save the number of chunks total to avoid having to jump around to find referenceso + // or just keep reading chunks until eof or other descriptor + long chunksStart = ftell(tiledMap->file); + fseek(tiledMap->file, 0, SEEK_END); + long totalChunksSize = ftell(tiledMap->file) - chunksStart; - int tmp; - // reverse byte order - for(int i = 0; i < noBytes/2; i++) { - tmp = out[i]; - out[i] = out[noBytes-i-1]; - out[noBytes-i-1] = tmp; - } + fseek(tiledMap->file, chunksStart, SEEK_SET); - return 0; -} + for (int n = 0; n < totalChunksSize; n += chunkSizeBytes + 8) { + int x, y; + readb((char *)&x, 4, tiledMap->file); + readb((char *)&y, 4, tiledMap->file); + long pointer = ftell(tiledMap->file); -char getTiledMapTile(TiledMap tiledMap, int pos[2]) { - return tiledMap.tilelayout[pos[1]*tiledMap.width + pos[0]]; -} + CachedChunk * cached = malloc(sizeof(CachedChunk)); + cached->filePos = pointer; + cached->chunk = NULL; -void setTiledMapTile(TiledMap tiledMap, int pos[2], char tile) { - tiledMap.tilelayout[pos[1]*tiledMap.width + pos[0]] = tile; + kdtree_insert(&tiledMap->chunkTree, x, y, (char *) cached); + fseek(tiledMap->file, chunkSizeBytes, SEEK_CUR); + } } -//! load tilemap data from file -TiledMap loadTiledMap(char * filename) { +TiledMap openTiledMap(char * filename) { TiledMap tiledMap; - FILE * file; - if (!(file = fopen(filename, "rb"))) { + if (!(tiledMap.file = fopen(filename, "r+b"))) { fprintf(stderr, "Failed to load %s\n", filename); - return tiledMap; } + FILE * file = tiledMap.file; + // skip header fseek(file, 10, SEEK_CUR); // 4 bytes for int width - readb((char *)&tiledMap.width, 4, file); + readb((char *)&tiledMap.chunkWidth, 4, file); // 4 bytes for int height - readb((char *)&tiledMap.height, 4, file); - - size_t layoutSize = tiledMap.width*tiledMap.height; - tiledMap.tilelayout = malloc(layoutSize); - fread(tiledMap.tilelayout, layoutSize, 1, file); + readb((char *)&tiledMap.chunkHeight, 4, file); // read the pixel size of each tile readb((char *)&tiledMap.tileSize, 4, file); @@ -104,52 +111,183 @@ TiledMap loadTiledMap(char * filename) { size_t atlasSizeBytes = tiledMap.atlasSize[0]*tiledMap.tileSize*tiledMap.atlasSize[1]*tiledMap.tileSize*4; tiledMap.atlasData = malloc(atlasSizeBytes); fread(tiledMap.atlasData, atlasSizeBytes, (size_t) 1, file); - + tiledMap.tileCount = tiledMap.atlasSize[0]*tiledMap.atlasSize[1] + 1; - fclose(file); + buildChunkTree(&tiledMap); return tiledMap; } -TiledMap openNewTiledMap(Image atlas, int tileSize, int width, int height) { - TiledMap tiledMap; - tiledMap.width = width; - tiledMap.height = height; - tiledMap.tilelayout = malloc(width * height); +//! save chunk to file +void commitChunk(TiledMap * tiledMap, CachedChunk * cached) { + size_t chunkSizeBytes = tiledMap->chunkWidth * tiledMap->chunkHeight; - tiledMap.tileSize = tileSize; + fseek(tiledMap->file, cached->filePos, SEEK_SET); + fwrite(cached->chunk, 1, chunkSizeBytes, tiledMap->file); +} - tiledMap.atlasSize[0] = atlas.width / tileSize; - tiledMap.atlasSize[1] = atlas.height / tileSize; +void unloadChunk(TiledMap * tiledMap, CachedChunk * cached) { + // commit a chunk before unloading + commitChunk(tiledMap, cached); - tiledMap.atlasData = LoadImageColors(atlas); - - return tiledMap; + // free memory + free(cached->chunk); + cached->chunk = NULL; } -void saveTiledMap(char * filename, TiledMap tiledMap) { - FILE * file; +//! load a chunk into the cache and return it +CachedChunk * loadChunk(TiledMap * tiledMap, int x, int y) { + // TODO add caching for this + size_t chunkSizeBytes = tiledMap->chunkWidth * tiledMap->chunkHeight; - if (!(file = fopen(filename, "wb"))) { - fprintf(stderr, "Failed to load %s\n", filename); - return; + + CachedChunk *cached = (CachedChunk * ) kdtree_search(tiledMap->chunkTree, x, y); + // if this chunk is not indexed, return NULL + if (cached == NULL) + return NULL; + + // if this chunk is not loaded into memory, load it + if (cached->chunk == NULL) { + cached->chunk = malloc(chunkSizeBytes); + fseek(tiledMap->file, cached->filePos, SEEK_SET); + fread(cached->chunk, 1, chunkSizeBytes, tiledMap->file); } - size_t layoutSize = tiledMap.width*tiledMap.height; + + return cached; +} + +CachedChunk * createChunk(TiledMap *tiledMap, int x, int y, Chunk chunk) { + size_t chunkSizeBytes = tiledMap->chunkWidth * tiledMap->chunkHeight; + + fseek(tiledMap->file, 0, SEEK_END); + + // calculate position before writing + long pos = ftell(tiledMap->file) + 8; + CachedChunk *cached = malloc(sizeof(CachedChunk)); + cached->filePos = pos; + cached->chunk = chunk; + + kdtree_insert(&tiledMap->chunkTree, x, y, (char *) cached); + + int chunkx, chunky; + writeb((char *) &x, 4, tiledMap->file); + writeb((char *) &y, 4, tiledMap->file); + fwrite(chunk, 1, chunkSizeBytes, tiledMap->file); + + return cached; +} + +CachedChunk * createEmptyChunk(TiledMap * tiledMap, int x, int y) { + Chunk chunk = calloc(tiledMap->chunkWidth*tiledMap->chunkHeight, 1); + return createChunk(tiledMap, x, y, chunk); +} + +char getChunkedTile(TiledMap *tiledMap, int x, int y) { + // TODO put this calculation in function + int inChunkX = x % tiledMap->chunkWidth; + int inChunkY = y % tiledMap->chunkHeight; + int chunkX = (x - inChunkX) / tiledMap->chunkWidth; + int chunkY = (y - inChunkY) / tiledMap->chunkHeight; + + CachedChunk * cached = loadChunk(tiledMap, chunkX, chunkY); + if (cached == NULL) + return 0; + + if (cached->chunk == NULL) + return 0; + + char v = cached->chunk[inChunkY * tiledMap->chunkWidth + inChunkX]; + return v; +} + +char setChunkedTile(TiledMap * tiledMap, int x, int y, char value) { + int inChunkX = x % tiledMap->chunkWidth; + int inChunkY = y % tiledMap->chunkHeight; + int chunkX = (x - inChunkX) / tiledMap->chunkWidth; + int chunkY = (y - inChunkY) / tiledMap->chunkHeight; + + CachedChunk * cached = loadChunk(tiledMap, chunkX, chunkY); + if (cached == NULL) + cached = createEmptyChunk(tiledMap, chunkX, chunkY); + + cached->chunk[inChunkY * tiledMap->chunkWidth + inChunkX] = value; + + // TODO do this when unloading + //commitChunk(tiledMap, cached); + return value; +} + + +void writeTiledMapHeader(TiledMap tiledMap) { + FILE * file = tiledMap.file; size_t atlasSizeBytes = tiledMap.atlasSize[0]*tiledMap.tileSize*tiledMap.atlasSize[1]*tiledMap.tileSize*4; - fwrite("TILEFILEv2", 10, 1, file); + // write header information from the start of the file + fseek(file, 0, SEEK_SET); + fwrite("TILEFILEv3", 10, 1, file); - writeb((char *) &tiledMap.width, 4, file); - writeb((char *) &tiledMap.height, 4, file); - - fwrite(tiledMap.tilelayout, 1, layoutSize, file); + writeb((char *) &tiledMap.chunkWidth, 4, file); + writeb((char *) &tiledMap.chunkHeight, 4, file); writeb((char *) &tiledMap.tileSize, 4, file); writeb((char *) &tiledMap.atlasSize[0], 4, file); writeb((char *) &tiledMap.atlasSize[1], 4, file); fwrite(tiledMap.atlasData, 1, atlasSizeBytes, file); + // since chunks are already directly written here, do not write anything else + // TODO when caching, commit everything left in cache here +} - fclose(file); - fprintf(stderr, "Written tiledfiled to %s\n", filename); +TiledMap openNewTiledMap(char * filename, Image atlas, int tileSize, int chunkWidth, int chunkHeight, int width, int height) { + TiledMap tiledMap; + tiledMap.chunkWidth = chunkWidth; + tiledMap.chunkHeight = chunkHeight; + + tiledMap.tileSize = tileSize; + + tiledMap.atlasSize[0] = atlas.width / tileSize; + tiledMap.atlasSize[1] = atlas.height / tileSize; + + tiledMap.atlasData = LoadImageColors(atlas); + tiledMap.tileCount = tiledMap.atlasSize[0]*tiledMap.atlasSize[1] + 1; + + tiledMap.chunkTree = NULL; + + if (!(tiledMap.file = fopen(filename, "wb"))) { + fprintf(stderr, "Failed to load %s\n", filename); + } + + writeTiledMapHeader(tiledMap); + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + char * chunk = calloc(chunkWidth*chunkHeight, 1); + createChunk(&tiledMap, x, y, chunk); + } + } + + // reopen the file in read+write mode + fclose(tiledMap.file); + + if (!(tiledMap.file = fopen(filename, "r+b"))) { + fprintf(stderr, "Failed to load %s\n", filename); + } + + return tiledMap; } + +void unloadChunks(TiledMap *tiledMap, kdtree_t * root) { + if (root == NULL) + return; + + unloadChunks(tiledMap, root->left); + unloadChunks(tiledMap, root->right); + unloadChunk(tiledMap, (CachedChunk *)root->value); +} + +void closeTiledMap(TiledMap *tiledMap) { + unloadChunks(tiledMap, tiledMap->chunkTree); + kdtree_free(&tiledMap->chunkTree); + UnloadImageColors(tiledMap->atlasData); + fclose(tiledMap->file); +} + diff --git a/src/tiledmap.h b/src/tiledmap.h index fb9c789..2c4cf94 100644 --- a/src/tiledmap.h +++ b/src/tiledmap.h @@ -1,23 +1,33 @@ #include +#include "kdtree.h" -typedef struct TiledMap { - int width; - int height; - char * tilelayout; +#define CHUNK_CACHE_SIZE 32 + +typedef char * Chunk; + +typedef struct CachedChunk { + long filePos; + Chunk chunk; +} CachedChunk; + +typedef struct ChunkedTiledMap { + FILE * file; + int chunkWidth; + int chunkHeight; int tileSize; int atlasSize[2]; int tileCount; Color * atlasData; + kdtree_t * chunkTree; } TiledMap; void textureFromPixels(Texture2D *texOut, Color *pixels, int width, int height); +TiledMap openTiledMap(char * filename); +CachedChunk * loadChunk(TiledMap *tiledMap, int x, int y); +char getChunkedTile(TiledMap *tiledMap, int x, int y); +char setChunkedTile(TiledMap * tiledMap, int x, int y, char value); +CachedChunk * createChunk(TiledMap * tiledMap, int x, int y, Chunk chunk); +void writeTiledMapHeader(TiledMap tiledMap); +TiledMap openNewTiledMap(char * filename, Image atlas, int tileSize, int chunkWidth, int chunkHeight, int width, int height); +void closeTiledMap(TiledMap * tiledMap); -void setTiledMapTile(TiledMap tiledMap, int pos[2], char tile); - -char getTiledMapTile(TiledMap tiledMap, int pos[2]); - -TiledMap loadTiledMap(char * filename); - -void saveTiledMap(char * filename, TiledMap tiledMap); - -TiledMap newTiledMap(Image atlas, int tileSize, int width, int height); -- cgit v1.2.1