CHUNK STUFF
This commit is contained in:
@@ -15,4 +15,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
add_subdirectory(display)
|
||||
add_subdirectory(locale)
|
||||
add_subdirectory(json)
|
||||
add_subdirectory(chunk)
|
||||
add_subdirectory(chunk)
|
||||
add_subdirectory(dmf)
|
||||
@@ -45,4 +45,10 @@ assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT] = {
|
||||
.loadAsync = assetChunkLoaderAsync,
|
||||
.dispose = assetChunkDispose
|
||||
},
|
||||
|
||||
[ASSET_LOADER_TYPE_DMF] = {
|
||||
.loadSync = assetDmfLoaderSync,
|
||||
.loadAsync = assetDmfLoaderAsync,
|
||||
.dispose = assetDmfDispose
|
||||
},
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "asset/loader/locale/assetlocaleloader.h"
|
||||
#include "asset/loader/json/assetjsonloader.h"
|
||||
#include "asset/loader/chunk/assetchunkloader.h"
|
||||
#include "asset/loader/dmf/assetdmfloader.h"
|
||||
|
||||
typedef enum {
|
||||
ASSET_LOADER_TYPE_NULL,
|
||||
@@ -22,6 +23,7 @@ typedef enum {
|
||||
ASSET_LOADER_TYPE_LOCALE,
|
||||
ASSET_LOADER_TYPE_JSON,
|
||||
ASSET_LOADER_TYPE_CHUNK,
|
||||
ASSET_LOADER_TYPE_DMF,
|
||||
|
||||
ASSET_LOADER_TYPE_COUNT
|
||||
} assetloadertype_t;
|
||||
@@ -33,6 +35,7 @@ typedef union {
|
||||
assetlocaleloaderinput_t locale;
|
||||
assetjsonloaderinput_t json;
|
||||
assetchunkloaderinput_t chunk;
|
||||
assetdmfloaderinput_t dmf;
|
||||
} assetloaderinput_t;
|
||||
|
||||
typedef union {
|
||||
@@ -42,6 +45,7 @@ typedef union {
|
||||
assetlocaleloaderloading_t locale;
|
||||
assetjsonloaderloading_t json;
|
||||
assetchunkloaderloading_t chunk;
|
||||
assetdmfloaderloading_t dmf;
|
||||
} assetloaderloading_t;
|
||||
|
||||
typedef union {
|
||||
@@ -51,6 +55,7 @@ typedef union {
|
||||
assetlocaleoutput_t locale;
|
||||
assetjsonoutput_t json;
|
||||
assetchunkoutput_t chunk;
|
||||
assetdmfoutput_t dmf;
|
||||
} assetloaderoutput_t;
|
||||
|
||||
typedef struct assetloading_s assetloading_t;
|
||||
|
||||
@@ -92,22 +92,22 @@ errorret_t assetChunkLoaderSync(assetloading_t *loading) {
|
||||
"Chunk mesh count exceeds maximum."
|
||||
);
|
||||
|
||||
uint32_t poolOffset = 0;
|
||||
for(uint8_t m = 0; m < out->meshCount; m++) {
|
||||
uint32_t vertCount = endianLittleToHost32(*(uint32_t *)(data + offset));
|
||||
offset += sizeof(uint32_t);
|
||||
assertTrue(
|
||||
poolOffset + vertCount <= CHUNK_VERTEX_COUNT,
|
||||
"Chunk vertex data exceeds pool."
|
||||
);
|
||||
out->meshVertCounts[m] = vertCount;
|
||||
uint8_t nameLen = 0;
|
||||
while(
|
||||
data[offset + nameLen] != '\0' &&
|
||||
nameLen < CHUNK_MESH_NAME_MAX - 1
|
||||
) {
|
||||
nameLen++;
|
||||
}
|
||||
memoryCopy(out->meshNames[m], data + offset, nameLen);
|
||||
out->meshNames[m][nameLen] = '\0';
|
||||
offset += nameLen + 1;
|
||||
|
||||
memoryCopy(
|
||||
&out->vertices[poolOffset],
|
||||
data + offset,
|
||||
vertCount * sizeof(meshvertex_t)
|
||||
out->meshOffsets[m], data + offset, sizeof(vec3)
|
||||
);
|
||||
offset += vertCount * sizeof(meshvertex_t);
|
||||
poolOffset += vertCount;
|
||||
offset += sizeof(vec3);
|
||||
}
|
||||
|
||||
memoryFree(data);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "asset/assetfile.h"
|
||||
#include "rpg/overworld/chunk.h"
|
||||
|
||||
#define ASSET_CHUNK_FILE_VERSION 2
|
||||
#define ASSET_CHUNK_FILE_VERSION 3
|
||||
|
||||
typedef struct assetloading_s assetloading_t;
|
||||
typedef struct assetentry_s assetentry_t;
|
||||
@@ -34,8 +34,8 @@ typedef struct {
|
||||
typedef struct {
|
||||
tile_t tiles[CHUNK_TILE_COUNT];
|
||||
uint8_t meshCount;
|
||||
uint32_t meshVertCounts[CHUNK_MESH_COUNT_MAX];
|
||||
meshvertex_t vertices[CHUNK_VERTEX_COUNT];
|
||||
char_t meshNames[CHUNK_MESH_COUNT_MAX][CHUNK_MESH_NAME_MAX];
|
||||
vec3 meshOffsets[CHUNK_MESH_COUNT_MAX];
|
||||
} assetchunkoutput_t;
|
||||
|
||||
/**
|
||||
@@ -50,7 +50,8 @@ errorret_t assetChunkLoaderAsync(assetloading_t *loading);
|
||||
|
||||
/**
|
||||
* Synchronous loader for chunk assets. Validates the DCF binary previously
|
||||
* read by the async phase and populates the output assetchunkoutput_t.
|
||||
* read by the async phase and populates the output assetchunkoutput_t with
|
||||
* tile data and DMF mesh names.
|
||||
*
|
||||
* @param loading Loading information for the asset being loaded.
|
||||
* @return Error code indicating success or failure of the load operation.
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
assetdmfloader.c
|
||||
)
|
||||
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "assetdmfloader.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/endian.h"
|
||||
#include "asset/loader/assetloading.h"
|
||||
#include "asset/loader/assetentry.h"
|
||||
|
||||
errorret_t assetDmfLoaderAsync(assetloading_t *loading) {
|
||||
assertNotNull(loading, "Loading cannot be NULL");
|
||||
assertNotMainThread("Should be called from an async thread.");
|
||||
|
||||
if(loading->loading.dmf.state != ASSET_DMF_LOADING_STATE_READ_FILE) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
assertNull(loading->loading.dmf.data, "Data already defined?");
|
||||
|
||||
assetfile_t *file = &loading->loading.dmf.file;
|
||||
assetLoaderErrorChain(loading,
|
||||
assetFileInit(file, loading->entry->name, NULL, NULL)
|
||||
);
|
||||
|
||||
uint8_t *data = memoryAllocate(file->size);
|
||||
assetLoaderErrorChain(loading, assetFileOpen(file));
|
||||
assetLoaderErrorChain(loading, assetFileRead(file, data, file->size));
|
||||
assetLoaderErrorChain(loading, assetFileClose(file));
|
||||
assetLoaderErrorChain(loading, assetFileDispose(file));
|
||||
assertTrue(
|
||||
file->lastRead == file->size,
|
||||
"Failed to read entire DMF file."
|
||||
);
|
||||
|
||||
loading->loading.dmf.data = data;
|
||||
loading->loading.dmf.state = ASSET_DMF_LOADING_STATE_CREATE_MESH;
|
||||
loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetDmfLoaderSync(assetloading_t *loading) {
|
||||
assertNotNull(loading, "Loading cannot be NULL");
|
||||
assertTrue(loading->type == ASSET_LOADER_TYPE_DMF, "Invalid type.");
|
||||
assertIsMainThread("Must be called from the main thread.");
|
||||
|
||||
switch(loading->loading.dmf.state) {
|
||||
case ASSET_DMF_LOADING_STATE_INITIAL:
|
||||
loading->loading.dmf.state = ASSET_DMF_LOADING_STATE_READ_FILE;
|
||||
loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC;
|
||||
errorOk();
|
||||
break;
|
||||
|
||||
case ASSET_DMF_LOADING_STATE_CREATE_MESH:
|
||||
break;
|
||||
|
||||
default:
|
||||
errorOk();
|
||||
}
|
||||
|
||||
uint8_t *data = loading->loading.dmf.data;
|
||||
assertNotNull(data, "DMF data should have been loaded by now.");
|
||||
|
||||
if(data[0] != 'D' || data[1] != 'M' || data[2] != 'F') {
|
||||
memoryFree(data);
|
||||
assetLoaderErrorThrow(loading, "Invalid DMF file header");
|
||||
}
|
||||
|
||||
uint32_t version = endianLittleToHost32(*(uint32_t *)(data + 4));
|
||||
if(version != ASSET_DMF_FILE_VERSION) {
|
||||
memoryFree(data);
|
||||
assetLoaderErrorThrow(
|
||||
loading, "Unsupported DMF version %u", version
|
||||
);
|
||||
}
|
||||
|
||||
uint32_t vertCount = endianLittleToHost32(*(uint32_t *)(data + 8));
|
||||
assetdmfoutput_t *out = &loading->entry->data.dmf;
|
||||
|
||||
if(vertCount == 0) {
|
||||
memoryFree(data);
|
||||
loading->loading.dmf.data = NULL;
|
||||
loading->entry->state = ASSET_ENTRY_STATE_LOADED;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
out->vertices = memoryAllocate(vertCount * sizeof(meshvertex_t));
|
||||
memoryCopy(
|
||||
out->vertices, data + 12, vertCount * sizeof(meshvertex_t)
|
||||
);
|
||||
memoryFree(data);
|
||||
loading->loading.dmf.data = NULL;
|
||||
|
||||
errorret_t ret = meshInit(
|
||||
&out->mesh,
|
||||
MESH_PRIMITIVE_TYPE_TRIANGLES,
|
||||
(int32_t)vertCount,
|
||||
out->vertices
|
||||
);
|
||||
if(errorIsNotOk(ret)) {
|
||||
loading->entry->state = ASSET_ENTRY_STATE_ERROR;
|
||||
memoryFree(out->vertices);
|
||||
out->vertices = NULL;
|
||||
errorChain(ret);
|
||||
}
|
||||
|
||||
ret = meshFlush(&out->mesh, 0, (int32_t)vertCount);
|
||||
if(errorIsNotOk(ret)) {
|
||||
loading->entry->state = ASSET_ENTRY_STATE_ERROR;
|
||||
meshDispose(&out->mesh);
|
||||
memoryFree(out->vertices);
|
||||
out->vertices = NULL;
|
||||
errorChain(ret);
|
||||
}
|
||||
|
||||
loading->entry->state = ASSET_ENTRY_STATE_LOADED;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetDmfDispose(assetentry_t *entry) {
|
||||
assertNotNull(entry, "Entry cannot be NULL");
|
||||
assertTrue(entry->type == ASSET_LOADER_TYPE_DMF, "Invalid type.");
|
||||
assertIsMainThread("Must be called from the main thread.");
|
||||
|
||||
assetdmfoutput_t *out = &entry->data.dmf;
|
||||
if(out->vertices != NULL) {
|
||||
errorChain(meshDispose(&out->mesh));
|
||||
memoryFree(out->vertices);
|
||||
out->vertices = NULL;
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "asset/assetfile.h"
|
||||
#include "display/mesh/mesh.h"
|
||||
|
||||
#define ASSET_DMF_FILE_VERSION 1
|
||||
|
||||
typedef struct assetloading_s assetloading_t;
|
||||
typedef struct assetentry_s assetentry_t;
|
||||
|
||||
typedef struct {
|
||||
void *nothing;
|
||||
} assetdmfloaderinput_t;
|
||||
|
||||
typedef enum {
|
||||
ASSET_DMF_LOADING_STATE_INITIAL,
|
||||
ASSET_DMF_LOADING_STATE_READ_FILE,
|
||||
ASSET_DMF_LOADING_STATE_CREATE_MESH,
|
||||
ASSET_DMF_LOADING_STATE_DONE
|
||||
} assetdmfloadingstate_t;
|
||||
|
||||
typedef struct {
|
||||
assetfile_t file;
|
||||
assetdmfloadingstate_t state;
|
||||
uint8_t *data;
|
||||
} assetdmfloaderloading_t;
|
||||
|
||||
typedef struct {
|
||||
mesh_t mesh;
|
||||
meshvertex_t *vertices;
|
||||
} assetdmfoutput_t;
|
||||
|
||||
/**
|
||||
* Asynchronous loader for DMF mesh assets. Reads the raw DMF file bytes
|
||||
* into the loading buffer so the sync phase can parse without blocking
|
||||
* the main thread on I/O.
|
||||
*
|
||||
* @param loading Loading information for the asset being loaded.
|
||||
* @return Error code indicating success or failure.
|
||||
*/
|
||||
errorret_t assetDmfLoaderAsync(assetloading_t *loading);
|
||||
|
||||
/**
|
||||
* Synchronous loader for DMF mesh assets. Parses the DMF binary read by
|
||||
* the async phase, then initializes and flushes the mesh to the GPU.
|
||||
*
|
||||
* @param loading Loading information for the asset being loaded.
|
||||
* @return Error code indicating success or failure.
|
||||
*/
|
||||
errorret_t assetDmfLoaderSync(assetloading_t *loading);
|
||||
|
||||
/**
|
||||
* Disposer for DMF mesh assets. Disposes the mesh and frees the vertex
|
||||
* buffer.
|
||||
*
|
||||
* @param entry Asset entry containing the DMF data to dispose.
|
||||
* @return Error code indicating success or failure.
|
||||
*/
|
||||
errorret_t assetDmfDispose(assetentry_t *entry);
|
||||
@@ -8,21 +8,21 @@
|
||||
#pragma once
|
||||
#include "rpg/overworld/tile.h"
|
||||
#include "worldpos.h"
|
||||
#include "display/mesh/mesh.h"
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
|
||||
#define CHUNK_MESH_COUNT_MAX 10
|
||||
#define CHUNK_VERTEX_COUNT 8192
|
||||
#define CHUNK_MESH_NAME_MAX 64
|
||||
#define CHUNK_ENTITY_COUNT_MAX 10
|
||||
|
||||
typedef struct assetentry_s assetentry_t;
|
||||
|
||||
typedef struct chunk_s {
|
||||
chunkpos_t position;
|
||||
tile_t tiles[CHUNK_TILE_COUNT];
|
||||
|
||||
uint8_t meshCount;
|
||||
uint32_t meshVertCounts[CHUNK_MESH_COUNT_MAX];
|
||||
meshvertex_t vertices[CHUNK_VERTEX_COUNT];
|
||||
mesh_t meshes[CHUNK_MESH_COUNT_MAX];
|
||||
char_t meshNames[CHUNK_MESH_COUNT_MAX][CHUNK_MESH_NAME_MAX];
|
||||
vec3 meshOffsets[CHUNK_MESH_COUNT_MAX];
|
||||
assetentry_t *meshEntries[CHUNK_MESH_COUNT_MAX];
|
||||
|
||||
uint8_t entities[CHUNK_ENTITY_COUNT_MAX];
|
||||
} chunk_t;
|
||||
@@ -37,9 +37,9 @@ uint32_t chunkGetTileIndex(const chunkpos_t position);
|
||||
|
||||
/**
|
||||
* Checks if two chunk positions are equal.
|
||||
*
|
||||
*
|
||||
* @param a The first chunk position.
|
||||
* @param b The second chunk position.
|
||||
* @return true if equal, false otherwise.
|
||||
*/
|
||||
bool_t chunkPositionIsEqual(const chunkpos_t a, const chunkpos_t b);
|
||||
bool_t chunkPositionIsEqual(const chunkpos_t a, const chunkpos_t b);
|
||||
|
||||
+45
-104
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "util/memory.h"
|
||||
#include "assert/assert.h"
|
||||
#include "asset/asset.h"
|
||||
#include "asset/loader/assetloader.h"
|
||||
#include "rpg/entity/entity.h"
|
||||
#include "util/string.h"
|
||||
|
||||
@@ -17,20 +18,6 @@ map_t MAP;
|
||||
errorret_t mapInit() {
|
||||
memoryZero(&MAP, sizeof(map_t));
|
||||
|
||||
// Setup chunk meshes
|
||||
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
||||
chunk_t *chunk = &MAP.chunks[i];
|
||||
for(uint8_t j = 0; j < CHUNK_MESH_COUNT_MAX; j++) {
|
||||
errorChain(meshInit(
|
||||
&chunk->meshes[j],
|
||||
MESH_PRIMITIVE_TYPE_TRIANGLES,
|
||||
CHUNK_VERTEX_COUNT,
|
||||
chunk->vertices
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Perform "initial load"
|
||||
MAP.loaded = true;
|
||||
int32_t i = 0;
|
||||
for(chunkunit_t z = 0; z < MAP_CHUNK_DEPTH; z++) {
|
||||
@@ -54,61 +41,6 @@ bool_t mapIsLoaded() {
|
||||
return MAP.loaded;
|
||||
}
|
||||
|
||||
// errorret_t mapLoad(const char_t *path, const chunkpos_t position) {
|
||||
// assertStrLenMin(path, 1, "Map file path cannot be empty");
|
||||
// assertStrLenMax(path, MAP_FILE_PATH_MAX - 1, "Map file path too long");
|
||||
|
||||
// if(stringCompare(MAP.filePath, path) == 0) {
|
||||
// // Same map, no need to reload
|
||||
// errorOk();
|
||||
// }
|
||||
|
||||
// chunkindex_t i;
|
||||
|
||||
// // Unload all loaded chunks
|
||||
// if(mapIsLoaded()) {
|
||||
// for(i = 0; i < MAP_CHUNK_COUNT; i++) {
|
||||
// mapChunkUnload(&MAP.chunks[i]);
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Store the map file path
|
||||
// stringCopy(MAP.filePath, path, MAP_FILE_PATH_MAX);
|
||||
|
||||
// // Determine directory path (it is dirname)
|
||||
// stringCopy(MAP.dirPath, path, MAP_FILE_PATH_MAX);
|
||||
// char_t *last = stringFindLastChar(MAP.dirPath, '/');
|
||||
// if(last == NULL) errorThrow("Invalid map file path");
|
||||
|
||||
// // Store filename, sans extension
|
||||
// stringCopy(MAP.fileName, last + 1, MAP_FILE_PATH_MAX);
|
||||
// *last = '\0'; // Terminate to get directory path
|
||||
|
||||
// last = stringFindLastChar(MAP.fileName, '.');
|
||||
// if(last == NULL) errorThrow("Map file name has no extension");
|
||||
// *last = '\0'; // Terminate to remove extension
|
||||
|
||||
// // Reset map position
|
||||
// MAP.chunkPosition = position;
|
||||
|
||||
// // Perform "initial load"
|
||||
// i = 0;
|
||||
// for(chunkunit_t z = 0; z < MAP_CHUNK_DEPTH; z++) {
|
||||
// for(chunkunit_t y = 0; y < MAP_CHUNK_HEIGHT; y++) {
|
||||
// for(chunkunit_t x = 0; x < MAP_CHUNK_WIDTH; x++) {
|
||||
// chunk_t *chunk = &MAP.chunks[i];
|
||||
// chunk->position.x = x + position.x;
|
||||
// chunk->position.y = y + position.y;
|
||||
// chunk->position.z = z + position.z;
|
||||
// MAP.chunkOrder[i] = chunk;
|
||||
// errorChain(mapChunkLoad(chunk));
|
||||
// i++;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// errorOk();
|
||||
// }
|
||||
|
||||
errorret_t mapPositionSet(const chunkpos_t newPos) {
|
||||
if(!mapIsLoaded()) errorThrow("No map loaded");
|
||||
|
||||
@@ -117,7 +49,6 @@ errorret_t mapPositionSet(const chunkpos_t newPos) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
// Determine which chunks remain loaded
|
||||
chunkindex_t chunksRemaining[MAP_CHUNK_COUNT] = {0};
|
||||
chunkindex_t chunksFreed[MAP_CHUNK_COUNT] = {0};
|
||||
|
||||
@@ -125,7 +56,6 @@ errorret_t mapPositionSet(const chunkpos_t newPos) {
|
||||
uint32_t freedCount = 0;
|
||||
|
||||
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
||||
// Will this chunk remain loaded?
|
||||
chunk_t *chunk = &MAP.chunks[i];
|
||||
if(
|
||||
chunk->position.x >= newPos.x &&
|
||||
@@ -137,23 +67,18 @@ errorret_t mapPositionSet(const chunkpos_t newPos) {
|
||||
chunk->position.z >= newPos.z &&
|
||||
chunk->position.z < newPos.z + MAP_CHUNK_DEPTH
|
||||
) {
|
||||
// Stays loaded
|
||||
chunksRemaining[remainingCount++] = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Not remaining loaded
|
||||
chunksFreed[freedCount++] = i;
|
||||
}
|
||||
|
||||
// Unload the freed chunks
|
||||
for(chunkindex_t i = 0; i < freedCount; i++) {
|
||||
chunk_t *chunk = &MAP.chunks[chunksFreed[i]];
|
||||
mapChunkUnload(chunk);
|
||||
}
|
||||
|
||||
// This can probably be optimized later, for now we check each chunk and see
|
||||
// if it needs loading or not, and update the chunk order
|
||||
chunkindex_t orderIndex = 0;
|
||||
for(chunkunit_t zOff = 0; zOff < MAP_CHUNK_DEPTH; zOff++) {
|
||||
for(chunkunit_t yOff = 0; yOff < MAP_CHUNK_HEIGHT; yOff++) {
|
||||
@@ -161,8 +86,7 @@ errorret_t mapPositionSet(const chunkpos_t newPos) {
|
||||
const chunkpos_t newChunkPos = {
|
||||
newPos.x + xOff, newPos.y + yOff, newPos.z + zOff
|
||||
};
|
||||
|
||||
// Is this chunk already loaded (was not unloaded earlier)?
|
||||
|
||||
chunkindex_t chunkIndex = -1;
|
||||
for(chunkindex_t i = 0; i < remainingCount; i++) {
|
||||
chunk_t *chunk = &MAP.chunks[chunksRemaining[i]];
|
||||
@@ -171,9 +95,7 @@ errorret_t mapPositionSet(const chunkpos_t newPos) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Need to load this chunk
|
||||
if(chunkIndex == -1) {
|
||||
// Find a freed chunk to reuse
|
||||
chunkIndex = chunksFreed[--freedCount];
|
||||
chunk_t *chunk = &MAP.chunks[chunkIndex];
|
||||
chunk->position = newChunkPos;
|
||||
@@ -185,27 +107,23 @@ errorret_t mapPositionSet(const chunkpos_t newPos) {
|
||||
}
|
||||
}
|
||||
|
||||
// Update map position
|
||||
MAP.chunkPosition = newPos;
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void mapUpdate() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
errorret_t mapDispose() {
|
||||
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
||||
mapChunkUnload(&MAP.chunks[i]);
|
||||
for(uint8_t j = 0; j < CHUNK_MESH_COUNT_MAX; j++) {
|
||||
errorChain(meshDispose(&MAP.chunks[i].meshes[j]));
|
||||
}
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void mapChunkUnload(chunk_t* chunk) {
|
||||
void mapChunkUnload(chunk_t *chunk) {
|
||||
uint8_t chunkIndex = (uint8_t)(chunk - MAP.chunks);
|
||||
for(uint8_t i = 0; i < CHUNK_ENTITY_COUNT_MAX; i++) {
|
||||
if(chunk->entities[i] == 0xFF) continue;
|
||||
@@ -220,10 +138,16 @@ void mapChunkUnload(chunk_t* chunk) {
|
||||
entity->type = ENTITY_TYPE_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for(uint8_t m = 0; m < chunk->meshCount; m++) {
|
||||
if(chunk->meshEntries[m] == NULL) continue;
|
||||
assetUnlockEntry(chunk->meshEntries[m]);
|
||||
chunk->meshEntries[m] = NULL;
|
||||
}
|
||||
chunk->meshCount = 0;
|
||||
}
|
||||
|
||||
errorret_t mapChunkLoad(chunk_t* chunk) {
|
||||
errorret_t mapChunkLoad(chunk_t *chunk) {
|
||||
if(!mapIsLoaded()) errorThrow("No map loaded");
|
||||
|
||||
memorySet(chunk->entities, 0xFF, sizeof(chunk->entities));
|
||||
@@ -253,25 +177,42 @@ errorret_t mapChunkLoad(chunk_t* chunk) {
|
||||
}
|
||||
|
||||
memoryCopy(chunk->tiles, entry->data.chunk.tiles, sizeof(chunk->tiles));
|
||||
memoryCopy(
|
||||
chunk->vertices, entry->data.chunk.vertices, sizeof(chunk->vertices)
|
||||
);
|
||||
memoryCopy(
|
||||
chunk->meshVertCounts,
|
||||
entry->data.chunk.meshVertCounts,
|
||||
sizeof(chunk->meshVertCounts)
|
||||
);
|
||||
uint8_t meshCount = entry->data.chunk.meshCount;
|
||||
for(uint8_t m = 0; m < meshCount; m++) {
|
||||
stringCopy(
|
||||
chunk->meshNames[m],
|
||||
entry->data.chunk.meshNames[m],
|
||||
CHUNK_MESH_NAME_MAX
|
||||
);
|
||||
glm_vec3_copy(entry->data.chunk.meshOffsets[m], chunk->meshOffsets[m]);
|
||||
}
|
||||
assetUnlockEntry(entry);
|
||||
|
||||
if(meshCount == 0) errorOk();
|
||||
chunk->meshCount = meshCount;
|
||||
for(uint8_t m = 0; m < meshCount; m++) {
|
||||
if(chunk->meshVertCounts[m] == 0) continue;
|
||||
errorChain(meshFlush(
|
||||
&chunk->meshes[m], 0, (int32_t)chunk->meshVertCounts[m]
|
||||
));
|
||||
assetentry_t *meshEntry = assetLock(
|
||||
chunk->meshNames[m], ASSET_LOADER_TYPE_DMF, NULL
|
||||
);
|
||||
if(meshEntry == NULL) {
|
||||
for(uint8_t j = 0; j < m; j++) {
|
||||
assetUnlockEntry(chunk->meshEntries[j]);
|
||||
chunk->meshEntries[j] = NULL;
|
||||
}
|
||||
errorThrow("Failed to lock mesh: %s", chunk->meshNames[m]);
|
||||
}
|
||||
|
||||
ret = assetRequireLoaded(meshEntry);
|
||||
if(errorIsNotOk(ret)) {
|
||||
assetUnlockEntry(meshEntry);
|
||||
for(uint8_t j = 0; j < m; j++) {
|
||||
assetUnlockEntry(chunk->meshEntries[j]);
|
||||
chunk->meshEntries[j] = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
chunk->meshEntries[m] = meshEntry;
|
||||
}
|
||||
chunk->meshCount = meshCount;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -296,7 +237,7 @@ chunkindex_t mapGetChunkIndexAt(const chunkpos_t position) {
|
||||
return chunkPosToIndex(&relPos);
|
||||
}
|
||||
|
||||
chunk_t* mapGetChunk(const uint8_t index) {
|
||||
chunk_t *mapGetChunk(const uint8_t index) {
|
||||
if(index >= MAP_CHUNK_COUNT) return NULL;
|
||||
if(!mapIsLoaded()) return NULL;
|
||||
return MAP.chunkOrder[index];
|
||||
@@ -314,4 +255,4 @@ tile_t mapGetTile(const worldpos_t position) {
|
||||
assertNotNull(chunk, "Chunk pointer cannot be NULL");
|
||||
chunktileindex_t tileIndex = worldPosToChunkTileIndex(&position);
|
||||
return chunk->tiles[tileIndex];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "rpg/overworld/chunk.h"
|
||||
|
||||
#define MAP_FILE_PATH_MAX 128
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
@@ -19,6 +19,9 @@
|
||||
#include "rpg/entity/entity.h"
|
||||
#include "rpg/rpgcamera.h"
|
||||
|
||||
#include "asset/loader/assetloader.h"
|
||||
#include "asset/loader/assetentry.h"
|
||||
|
||||
#define TEXTURE_CHUNK_SIZE 16
|
||||
|
||||
static texture_t TEXTURE_CHUNK;
|
||||
@@ -73,7 +76,7 @@ errorret_t sceneOverworldRender(scenedata_t *sceneData) {
|
||||
proj
|
||||
);
|
||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj));
|
||||
|
||||
|
||||
// Camera Eye
|
||||
float_t pixelsPerUnit = TILE_SIZE_PIXELS;
|
||||
float_t worldH = (float_t)SCREEN.height / pixelsPerUnit;
|
||||
@@ -95,7 +98,7 @@ errorret_t sceneOverworldRender(scenedata_t *sceneData) {
|
||||
eye
|
||||
);
|
||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, eye));
|
||||
|
||||
|
||||
// Chunks
|
||||
errorChain(sceneOverworldDrawChunks());
|
||||
|
||||
@@ -140,35 +143,33 @@ errorret_t sceneOverworldDrawChunks() {
|
||||
}
|
||||
};
|
||||
|
||||
// Pass 1: draw all base meshes with the shared chunk texture (no mid-loop
|
||||
// texture swaps).
|
||||
errorChain(shaderSetMaterial(&SHADER_UNLIT, &chunkMaterial));
|
||||
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
||||
chunk_t *chunk = &MAP.chunks[i];
|
||||
if(chunk->meshCount == 0) continue;
|
||||
if(chunk->meshVertCounts[0] == 0) continue;
|
||||
errorChain(meshDraw(
|
||||
&chunk->meshes[0], 0, (int32_t)chunk->meshVertCounts[0]
|
||||
));
|
||||
}
|
||||
|
||||
// Pass 2: draw each chunk's additional meshes (indices 1..meshCount-1).
|
||||
// Vertices are packed sequentially in the pool, so accumulate the offset.
|
||||
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
||||
chunk_t *chunk = &MAP.chunks[i];
|
||||
uint32_t vertOffset = chunk->meshVertCounts[0];
|
||||
for(uint8_t m = 1; m < chunk->meshCount; m++) {
|
||||
if(chunk->meshVertCounts[m] > 0) {
|
||||
errorChain(meshDraw(
|
||||
&chunk->meshes[m],
|
||||
(int32_t)vertOffset,
|
||||
(int32_t)chunk->meshVertCounts[m]
|
||||
));
|
||||
}
|
||||
vertOffset += chunk->meshVertCounts[m];
|
||||
worldpos_t wp;
|
||||
chunkPosToWorldPos(&chunk->position, &wp);
|
||||
vec3 wpf = { (float_t)wp.x, (float_t)wp.y, (float_t)wp.z };
|
||||
|
||||
for(uint8_t m = 0; m < chunk->meshCount; m++) {
|
||||
if(chunk->meshEntries[m] == NULL) continue;
|
||||
|
||||
vec3 pos;
|
||||
glm_vec3_add(wpf, chunk->meshOffsets[m], pos);
|
||||
mat4 model;
|
||||
glm_translate_make(model, pos);
|
||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model));
|
||||
errorChain(shaderSetMaterial(&SHADER_UNLIT, &chunkMaterial));
|
||||
errorChain(meshDraw(&chunk->meshEntries[m]->data.dmf.mesh, 0, -1));
|
||||
}
|
||||
}
|
||||
|
||||
// Restore identity model so subsequent renders (e.g. entities) are
|
||||
// not affected by the last chunk transform.
|
||||
mat4 identity;
|
||||
glm_mat4_identity(identity);
|
||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, identity));
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -178,4 +179,4 @@ errorret_t sceneOverworldDispose(scenedata_t *sceneData) {
|
||||
errorChain(textureDispose(&TEXTURE_CHUNK));
|
||||
|
||||
errorOk();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user