diff --git a/assets/map/CMakeLists.txt b/assets/map/CMakeLists.txt index 03f4a37..656d3ba 100644 --- a/assets/map/CMakeLists.txt +++ b/assets/map/CMakeLists.txt @@ -3,4 +3,4 @@ # This software is released under the MIT License. # https://opensource.org/licenses/MIT -add_asset(MAP map.json) \ No newline at end of file +add_asset(MAP map) \ No newline at end of file diff --git a/assets/map/map.json b/assets/map/map/0_0.json similarity index 100% rename from assets/map/map.json rename to assets/map/map/0_0.json diff --git a/src/asset/assettype.h b/src/asset/assettype.h index e9de5e6..add66c0 100644 --- a/src/asset/assettype.h +++ b/src/asset/assettype.h @@ -10,6 +10,7 @@ #include "type/assetalphaimage.h" #include "type/assetlanguage.h" #include "type/assetmap.h" +#include "type/assetchunk.h" #include typedef enum { @@ -19,6 +20,7 @@ typedef enum { ASSET_TYPE_ALPHA_IMAGE, ASSET_TYPE_LANGUAGE, ASSET_TYPE_MAP, + ASSET_TYPE_CHUNK, ASSET_TYPE_COUNT, } assettype_t; @@ -73,5 +75,11 @@ static const assettypedef_t ASSET_TYPE_DEFINITIONS[ASSET_TYPE_COUNT] = { .loadStrategy = ASSET_LOAD_STRAT_ENTIRE, .dataSize = sizeof(assetmap_t), .entire = assetMapLoad + }, + + [ASSET_TYPE_CHUNK] = { + .header = "DCF", + .loadStrategy = ASSET_LOAD_STRAT_CUSTOM, + .custom = assetChunkLoad } }; \ No newline at end of file diff --git a/src/asset/type/CMakeLists.txt b/src/asset/type/CMakeLists.txt index fdf5db0..1011933 100644 --- a/src/asset/type/CMakeLists.txt +++ b/src/asset/type/CMakeLists.txt @@ -10,4 +10,5 @@ target_sources(${DUSK_TARGET_NAME} assetpaletteimage.c assetlanguage.c assetmap.c + assetchunk.c ) \ No newline at end of file diff --git a/src/asset/type/assetchunk.c b/src/asset/type/assetchunk.c new file mode 100644 index 0000000..533446e --- /dev/null +++ b/src/asset/type/assetchunk.c @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "asset/asset.h" +#include "assert/assert.h" + +#pragma pack(push, 1) +typedef struct { + uint32_t tileCount; + uint8_t modelCount; +} assetchunkheader_t; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + tile_t tile; +} assetchunktiledata_t; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32_t vertexCount; +} assetchunkmodelheader_t; +#pragma pack(pop) + +errorret_t assetChunkLoad(assetcustom_t custom) { + assertNotNull(custom.output, "Output pointer cannot be NULL"); + assertNotNull(custom.zipFile, "Zip file pointer cannot be NULL"); + + chunk_t *chunk = (chunk_t *)custom.output; + + // Read header + assetchunkheader_t header; + size_t bytesRead = zip_fread( + custom.zipFile, &header, sizeof(assetchunkheader_t) + ); + if(bytesRead != sizeof(assetchunkheader_t)) { + zip_fclose(custom.zipFile); + errorThrow("Failed to read chunk asset header."); + } + + if(header.tileCount != CHUNK_TILE_COUNT) { + zip_fclose(custom.zipFile); + errorThrow( + "Chunk asset has invalid tile count: %d (expected %d).", + header.tileCount, + CHUNK_TILE_COUNT + ); + } + + if(header.modelCount > CHUNK_MESH_COUNT_MAX) { + zip_fclose(custom.zipFile); + errorThrow( + "Chunk asset has too many models: %d (max %d).", + header.modelCount, + CHUNK_MESH_COUNT_MAX + ); + } + + chunk->meshCount = header.modelCount; + + // Read tile data + bytesRead = zip_fread( + custom.zipFile, + chunk->tiles, + sizeof(assetchunktiledata_t) * header.tileCount + ); + if(bytesRead != sizeof(assetchunktiledata_t) * header.tileCount) { + zip_fclose(custom.zipFile); + errorThrow("Failed to read chunk tile data."); + } + + // For each model... + uint32_t vertexIndex = 0; + for(uint8_t i = 0; i < header.modelCount; i++) { + assetchunkmodelheader_t modelHeader; + bytesRead = zip_fread( + custom.zipFile, &modelHeader, sizeof(assetchunkmodelheader_t) + ); + if(bytesRead != sizeof(assetchunkmodelheader_t)) { + zip_fclose(custom.zipFile); + errorThrow("Failed to read chunk model header."); + } + + if( + vertexIndex + modelHeader.vertexCount > + CHUNK_VERTEX_COUNT_MAX + ) { + zip_fclose(custom.zipFile); + errorThrow("Chunk model vertex count exceeds maximum."); + } + + // Read vertex data. + bytesRead = zip_fread( + custom.zipFile, + &chunk->vertices[vertexIndex], + sizeof(meshvertex_t) * modelHeader.vertexCount + ); + if(bytesRead != sizeof(meshvertex_t) * modelHeader.vertexCount) { + zip_fclose(custom.zipFile); + errorThrow("Failed to read chunk model vertex data."); + } + + + // Init the mesh + mesh_t *mesh = &chunk->meshes[i]; + meshInit( + mesh, + MESH_PRIMITIVE_TRIANGLES, + modelHeader.vertexCount, + &chunk->vertices[vertexIndex] + ); + + vertexIndex += modelHeader.vertexCount; + } + + errorOk(); +} \ No newline at end of file diff --git a/src/asset/type/assetchunk.h b/src/asset/type/assetchunk.h new file mode 100644 index 0000000..98c94ee --- /dev/null +++ b/src/asset/type/assetchunk.h @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "error/error.h" +#include "rpg/world/chunk.h" + +typedef struct assetcustom_s assetcustom_t; + +/** + * Handles loading of chunk data from a chunk asset file. + * + * @param custom The custom asset loading parameters. + * @return An error code. + */ +errorret_t assetChunkLoad(assetcustom_t custom); \ No newline at end of file diff --git a/src/rpg/world/chunk.h b/src/rpg/world/chunk.h index 54b4d34..2e3b457 100644 --- a/src/rpg/world/chunk.h +++ b/src/rpg/world/chunk.h @@ -8,10 +8,18 @@ #pragma once #include "rpg/world/tile.h" #include "worldpos.h" +#include "display/mesh/mesh.h" + +#define CHUNK_VERTEX_COUNT_MAX (6 * CHUNK_TILE_COUNT * 3) +#define CHUNK_MESH_COUNT_MAX 16 typedef struct chunk_s { chunkpos_t position; tile_t tiles[CHUNK_TILE_COUNT]; + + uint8_t meshCount; + meshvertex_t vertices[CHUNK_VERTEX_COUNT_MAX]; + mesh_t meshes[CHUNK_MESH_COUNT_MAX]; } chunk_t; /** diff --git a/src/rpg/world/map.c b/src/rpg/world/map.c index 60298d1..5c2dbda 100644 --- a/src/rpg/world/map.c +++ b/src/rpg/world/map.c @@ -8,7 +8,7 @@ #include "map.h" #include "util/memory.h" #include "assert/assert.h" -#include "scene/scene/scenemap.h" +#include "asset/asset.h" map_t MAP; @@ -122,20 +122,7 @@ void mapChunkUnload(chunk_t* chunk) { } void mapChunkLoad(chunk_t* chunk) { - // printf("Loading chunk at (%d, %d, %d)\n", - // chunk->position.x, - // chunk->position.y, - // chunk->position.z - // ); - - memoryZero(chunk->tiles, sizeof(tile_t) * CHUNK_TILE_COUNT); - - if(chunk->position.x == 0 && chunk->position.y == 0 && chunk->position.z == 0) { - if(TEST_MAP_READY) { - - } - printf("LOAD CHUNK\n"); - } + errorCatch(errorPrint(assetLoad("map/map/0_0.dcf", chunk))); } chunkindex_t mapGetChunkIndexAt(const chunkpos_t position) { diff --git a/src/scene/scene/scenemap.c b/src/scene/scene/scenemap.c index e1dbe26..4a4e968 100644 --- a/src/scene/scene/scenemap.c +++ b/src/scene/scene/scenemap.c @@ -19,8 +19,6 @@ #define TILE_WIDTH 16.0f #define TILE_HEIGHT 16.0f #define TILE_DEPTH 11.36f -assetmap_t TEST_MAP; -bool_t TEST_MAP_READY = false; errorret_t sceneMapInit(scenedata_t *data) { // Init the camera. @@ -39,9 +37,6 @@ errorret_t sceneMapInit(scenedata_t *data) { ); data->sceneMap.camera.lookatPixelPerfect.pixelsPerUnit = 1.0f; - - errorChain(assetLoad("map/map.dmf", &TEST_MAP)); - TEST_MAP_READY = true; errorOk(); } @@ -118,10 +113,7 @@ void sceneMapRender(scenedata_t *data) { cameraPushMatrix(&data->sceneMap.camera); // Render map probably. - // sceneMapRenderMap(); - - textureBind(NULL); - meshDraw(&TEST_MAP.models[0].mesh, -1, -1); + sceneMapRenderMap(); // Render ents entity_t *ent = ENTITIES; @@ -175,33 +167,38 @@ void sceneMapRenderMap() { for(uint32_t i = 0; i < MAP_CHUNK_COUNT; i++) { chunk_t *chunk = MAP.chunkOrder[i]; - vec3 min, max; - min[0] = chunk->position.x * CHUNK_WIDTH * TILE_WIDTH; - min[1] = chunk->position.y * CHUNK_HEIGHT * TILE_HEIGHT; - min[2] = chunk->position.z * CHUNK_DEPTH * TILE_DEPTH; - - max[0] = min[0] + (CHUNK_WIDTH * TILE_WIDTH); - max[1] = min[1] + (CHUNK_HEIGHT * TILE_HEIGHT); - max[2] = min[2]; - - color_t color = COLOR_WHITE; - if(chunk->position.x % 2 == 0) { - color = (chunk->position.y % 2 == 0) ? COLOR_BLACK : COLOR_WHITE; - } else { - color = (chunk->position.y % 2 == 0) ? COLOR_WHITE : COLOR_BLACK; + for(uint8_t j = 0; j < chunk->meshCount; j++) { + mesh_t *mesh = &chunk->meshes[j]; + textureBind(NULL); + meshDraw(mesh, -1, -1); } - spriteBatchPush3D( - NULL, - min, - max, - color, - (vec2){ 0.0f, 0.0f }, - (vec2){ 1.0f, 1.0f } - ); + // vec3 min, max; + // min[0] = chunk->position.x * CHUNK_WIDTH * TILE_WIDTH; + // min[1] = chunk->position.y * CHUNK_HEIGHT * TILE_HEIGHT; + // min[2] = chunk->position.z * CHUNK_DEPTH * TILE_DEPTH; + + // max[0] = min[0] + (CHUNK_WIDTH * TILE_WIDTH); + // max[1] = min[1] + (CHUNK_HEIGHT * TILE_HEIGHT); + // max[2] = min[2]; + + // color_t color = COLOR_WHITE; + // if(chunk->position.x % 2 == 0) { + // color = (chunk->position.y % 2 == 0) ? COLOR_BLACK : COLOR_WHITE; + // } else { + // color = (chunk->position.y % 2 == 0) ? COLOR_WHITE : COLOR_BLACK; + // } + + // spriteBatchPush3D( + // NULL, + // min, + // max, + // color, + // (vec2){ 0.0f, 0.0f }, + // (vec2){ 1.0f, 1.0f } + // ); } } void sceneMapDispose(scenedata_t *data) { - meshDispose(&TEST_MAP.models[0].mesh); } diff --git a/src/scene/scene/scenemap.h b/src/scene/scene/scenemap.h index 9acf598..86f8766 100644 --- a/src/scene/scene/scenemap.h +++ b/src/scene/scene/scenemap.h @@ -15,9 +15,6 @@ typedef struct { camera_t camera; } scenemap_t; -extern assetmap_t TEST_MAP; -extern bool_t TEST_MAP_READY; - errorret_t sceneMapInit(scenedata_t *data); void sceneMapUpdate(scenedata_t *data); void sceneMapRender(scenedata_t *data); diff --git a/tools/assetstool/processmap.py b/tools/assetstool/processmap.py index a4b758b..ff81202 100644 --- a/tools/assetstool/processmap.py +++ b/tools/assetstool/processmap.py @@ -14,7 +14,7 @@ TILE_WIDTH = 16.0 TILE_HEIGHT = 16.0 TILE_DEPTH = 11.36 -def createQuadForTile(model, tileIndex, x=0, y=0, z=0): +def createQuadForTile(tileIndex, x=0, y=0, z=0): vertices = [] indices = [] @@ -48,35 +48,33 @@ def createQuadForTile(model, tileIndex, x=0, y=0, z=0): 'indices': indices } -def processMap(asset): - cache = assetGetCache(asset['path']) - if cache is not None: +def processChunk(path): + cache = assetGetCache(path) + if cache: return cache - + # Read input file as JSON - with open(asset['path'], 'r') as f: + with open(path, 'r') as f: inData = json.load(f) - # Create output object 'map' with default tile indexes and models array - map = { + chunk = { 'tiles': [0] * CHUNK_TILE_COUNT, 'models': [] } - # Create a simple 3D model object - model = { + baseModel = { 'vertices': [], 'indices': [], 'vertexCount': 0, 'indexCount': 0 } - # Append the model to map.models - map['models'].append(model) + # Append the model to chunk.models + chunk['models'].append(baseModel) for i, tile in enumerate(inData['tiles']): - # Set to map - map['tiles'][i] = tile + # Set to chunk + chunk['tiles'][i] = tile # Calculate x, y, z from i x = i % CHUNK_WIDTH @@ -84,27 +82,27 @@ def processMap(asset): z = i // (CHUNK_WIDTH * CHUNK_HEIGHT) # Add tile 3D model - result = createQuadForTile(model, tile, x, y, z) + result = createQuadForTile(tile, x, y, z) if len(result['vertices']) > 0: - base = len(model['vertices']) + base = len(baseModel['vertices']) quad_indices = [base + idx for idx in result['indices']] - model['vertices'].extend(result['vertices']) - model['indices'].extend(quad_indices) - model['vertexCount'] = len(model['vertices']) - model['indexCount'] = len(model['indices']) + baseModel['vertices'].extend(result['vertices']) + baseModel['indices'].extend(quad_indices) + baseModel['vertexCount'] = len(baseModel['vertices']) + baseModel['indexCount'] = len(baseModel['indices']) # Generate binary buffer for efficient output buffer = bytearray() - buffer.extend(b'DMF')# Header - buffer.extend(len(map['tiles']).to_bytes(4, 'little')) # Number of tiles - buffer.extend(len(map['models']).to_bytes(1, 'little')) # Number of models + buffer.extend(b'DCF')# Header + buffer.extend(len(chunk['tiles']).to_bytes(4, 'little')) # Number of tiles + buffer.extend(len(chunk['models']).to_bytes(1, 'little')) # Number of models # Buffer tile data as array of uint8_t - for tileIndex in map['tiles']: + for tileIndex in chunk['tiles']: buffer.append(tileIndex.to_bytes(1, 'little')[0]) # For each model - for model in map['models']: + for model in chunk['models']: # Write vertex count and index count buffer.extend(model['vertexCount'].to_bytes(4, 'little')) # buffer.extend(model['indexCount'].to_bytes(4, 'little')) @@ -129,17 +127,42 @@ def processMap(asset): buffer.extend(bytearray(struct.pack('