summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordavidovski <david@davidovski.xyz>2023-07-23 15:30:42 +0200
committerdavidovski <david@davidovski.xyz>2023-07-23 15:30:42 +0200
commit0d78ec0e53d9824a029840e74bf0721ed2187e6a (patch)
tree74781baff855e3f3760730c9897e932994f3a8a4
parent5ec62c0a2a0357a79f92b8bb37f67272e7f910ff (diff)
add chunk caching
-rw-r--r--makefile2
-rw-r--r--src/chunkedtiledmap.c263
-rw-r--r--src/chunkedtiledmap.h24
-rw-r--r--src/editor.c21
-rw-r--r--src/tiled.c16
-rw-r--r--src/tiled.h8
-rw-r--r--src/tiledmap.c272
-rw-r--r--src/tiledmap.h36
8 files changed, 256 insertions, 386 deletions
diff --git a/makefile b/makefile
index 928632f..6edef8d 100644
--- a/makefile
+++ b/makefile
@@ -3,7 +3,7 @@ FLAGS=-lm -lraylib -ggdb
.DEFAULT_GOAL := editor
-EDITOR_SOURCE=src/chunkedtiledmap.c src/editor.c src/kdtree.c src/tiled.c
+EDITOR_SOURCE=src/tiledmap.c src/editor.c src/kdtree.c src/tiled.c
editor: ${EDITOR_SOURCE}
${CC} ${EDITOR_SOURCE} -o editor ${FLAGS}
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 <raylib.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#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 <raylib.h>
-#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 <raylib.h>
-#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 <raylib.h>
#include <stdlib.h>
#include <stdio.h>
-#include <string.h>
+#include <unistd.h>
#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 <raylib.h>
+#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);