diff --git a/assets/chunks/0_0_0.dcf b/assets/chunks/0_0_0.dcf index c14c4c90..943bd015 100644 Binary files a/assets/chunks/0_0_0.dcf and b/assets/chunks/0_0_0.dcf differ diff --git a/assets/meshes/buildings/house_5_3.dmf b/assets/meshes/buildings/house_5_3.dmf new file mode 100644 index 00000000..5a1473e8 Binary files /dev/null and b/assets/meshes/buildings/house_5_3.dmf differ diff --git a/assets/meshes/chunk_0_0_0_0.dmf b/assets/meshes/chunk_0_0_0_0.dmf deleted file mode 100644 index ada49deb..00000000 Binary files a/assets/meshes/chunk_0_0_0_0.dmf and /dev/null differ diff --git a/assets/meshes/chunks/chunk_0_0_0_0.dmf b/assets/meshes/chunks/chunk_0_0_0_0.dmf new file mode 100644 index 00000000..d20086a7 Binary files /dev/null and b/assets/meshes/chunks/chunk_0_0_0_0.dmf differ diff --git a/assetsraw/chunks/chunk_0_0_0.json b/assetsraw/chunks/chunk_0_0_0.json new file mode 100644 index 00000000..91eceb33 --- /dev/null +++ b/assetsraw/chunks/chunk_0_0_0.json @@ -0,0 +1,26 @@ +{ + "meshes": [ + { + "file": "house_5_3.dmf", + "pos": [ 1, 1, 0 ] + } + ], + + "tiles": [ + { + "pos": [ 0, 0, 0 ], + "type": 1, + "tile": 0 + }, + { + "pos": [ 1, 0, 0 ], + "type": 1, + "tile": 0 + }, + { + "pos": [ 0, 1, 0 ], + "type": 1, + "tile": 0 + } + ] +} diff --git a/src/dusk/display/CMakeLists.txt b/src/dusk/display/CMakeLists.txt index 2597fc08..0f6d6c6b 100644 --- a/src/dusk/display/CMakeLists.txt +++ b/src/dusk/display/CMakeLists.txt @@ -21,7 +21,7 @@ add_subdirectory(texture) # Color definitions dusk_run_python( dusk_color_defs - tools.color.csv + tools.color --csv ${CMAKE_CURRENT_SOURCE_DIR}/color.csv --output ${DUSK_GENERATED_HEADERS_DIR}/display/color.h ) diff --git a/src/dusk/input/CMakeLists.txt b/src/dusk/input/CMakeLists.txt index fec62797..6cf2100f 100644 --- a/src/dusk/input/CMakeLists.txt +++ b/src/dusk/input/CMakeLists.txt @@ -14,7 +14,7 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} # Input Action Definitions dusk_run_python( dusk_input_csv_defs - tools.input.csv + tools.input --csv ${CMAKE_CURRENT_SOURCE_DIR}/input.csv --output ${DUSK_GENERATED_HEADERS_DIR}/input/inputactiondefs.h ) diff --git a/src/dusk/rpg/item/CMakeLists.txt b/src/dusk/rpg/item/CMakeLists.txt index 40cfb7c9..b8303a06 100644 --- a/src/dusk/rpg/item/CMakeLists.txt +++ b/src/dusk/rpg/item/CMakeLists.txt @@ -13,7 +13,7 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} # Item Definitions dusk_run_python( dusk_item_csv_defs - tools.item.csv + tools.item --csv ${CMAKE_CURRENT_SOURCE_DIR}/item.csv --output ${DUSK_GENERATED_HEADERS_DIR}/rpg/item/item.h ) diff --git a/src/dusk/rpg/story/CMakeLists.txt b/src/dusk/rpg/story/CMakeLists.txt index 1b03e602..6aace3e7 100644 --- a/src/dusk/rpg/story/CMakeLists.txt +++ b/src/dusk/rpg/story/CMakeLists.txt @@ -12,7 +12,7 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} # Story Flag Definitions dusk_run_python( dusk_story_defs - tools.story.csv + tools.story --csv ${CMAKE_CURRENT_SOURCE_DIR}/storyflag.csv --output ${DUSK_GENERATED_HEADERS_DIR}/rpg/story/storyflagvalue.h ) diff --git a/src/dusk/scene/overworld/sceneoverworld.c b/src/dusk/scene/overworld/sceneoverworld.c index 0f01870c..a10b58bb 100644 --- a/src/dusk/scene/overworld/sceneoverworld.c +++ b/src/dusk/scene/overworld/sceneoverworld.c @@ -9,6 +9,8 @@ #include "console/console.h" #include "assert/assert.h" +#include "display/display.h" +#include "display/displaystate.h" #include "display/shader/shader.h" #include "display/screen/screen.h" #include "display/shader/shaderunlit.h" @@ -57,6 +59,10 @@ errorret_t sceneOverworldUpdate(scenedata_t *sceneData) { errorret_t sceneOverworldRender(scenedata_t *sceneData) { assertNotNull(sceneData, "Scene data cannot be null"); + errorChain(displaySetState((displaystate_t){ + .flags = DISPLAY_STATE_FLAG_DEPTH_TEST | DISPLAY_STATE_FLAG_CULL + })); + mat4 proj, model, eye; diff --git a/tools/asset/chunk/__main__.py b/tools/asset/chunk/__main__.py index 8bc9c289..d27acd92 100644 --- a/tools/asset/chunk/__main__.py +++ b/tools/asset/chunk/__main__.py @@ -4,39 +4,54 @@ # https://opensource.org/licenses/MIT """ -Converts DCF chunk files (version 1 or 2) to version 3 and generates the -companion DMF (Dusk Mesh Format) files that version 3 references. +Generates DCF + companion DMF files from raw chunk JSON files, or upgrades +legacy DCF files (version 1 or 2) to version 3. -Version 1 format (after 8-byte header + tiles): - uint32_t vertCount - meshvertex_t vertices[vertCount] +JSON input (assetsraw/chunks/chunk_X_Y_Z.json): + { + "tiles": [ + { "pos": [x, y, z], "type": , "tile": } + ], + "meshes": [ + { "file": "house_5_3.dmf", "pos": [x, y, z] } + ] + } -Version 2 format (after 8-byte header + tiles): + Tiles absent from the array default to TILE_SHAPE_NULL. + Mesh files are located by searching under assets/meshes/ and referenced by + path from the assets root in the DCF. + + Output DCF is derived automatically: + assetsraw/chunks/chunk_X_Y_Z.json -> assets/chunks/X_Y_Z.dcf + +Version 3 DCF format (after 8-byte header + tiles): uint8_t meshCount for each mesh: - uint32_t vertCount - meshvertex_t vertices[vertCount] - -Version 3 format (after 8-byte header + tiles): - uint8_t meshCount - for each mesh: - null-terminated string (path to companion .dmf asset) + null-terminated string (relative asset path to .dmf) + float32[3] (x, y, z offset) DMF format: - Bytes 0-3: DMF\x00 + Bytes 0-3: DMF\\x00 Bytes 4-7: uint32_t version = 1 (little-endian) Bytes 8-11: uint32_t vertCount (little-endian) Bytes 12+: meshvertex_t vertices[vertCount] + Each vertex: uv[2] + pos[3] = 5 x float32 LE = 20 bytes Usage: - python3 -m tools.asset.chunk [output.dcf] - If output is omitted the input file is updated in place. - DMF files are written to assets/meshes/ beside the chunks/ directory. + python3 -m tools.asset.chunk # process all assetsraw/chunks/*.json + python3 -m tools.asset.chunk # process one JSON file + python3 -m tools.asset.chunk # upgrade legacy DCF in-place """ +import json +import os import struct import sys -import os + +_HERE = os.path.dirname(os.path.abspath(__file__)) +PROJECT_ROOT = os.path.normpath(os.path.join(_HERE, '..', '..', '..')) +ASSETSRAW_DIR = os.path.join(PROJECT_ROOT, 'assetsraw') +ASSETS_DIR = os.path.join(PROJECT_ROOT, 'assets') CHUNK_WIDTH = 16 CHUNK_HEIGHT = 16 @@ -44,62 +59,48 @@ CHUNK_DEPTH = 32 CHUNK_TILE_COUNT = CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH # 8192 CHUNK_MESH_COUNT_MAX = 10 -CHUNK_VERTEX_COUNT = 8192 CHUNK_MESH_NAME_MAX = 64 TILE_SIZE = 4 VERTEX_SIZE = 20 -FILE_MAGIC = b'DCF' -DMF_MAGIC = b'DMF\x00' -VERSION_OUT = 3 -DMF_VERSION = 1 +TILE_SHAPE_NULL = 0 +TILE_SHAPE_GROUND = 1 + +FILE_MAGIC = b'DCF' +DMF_MAGIC = b'DMF\x00' +VERSION_OUT = 3 +DMF_VERSION = 1 -def read_dcf(path): - """Read a v1 or v2 DCF file. Returns (tiles, meshes) where meshes is a - list of raw vertex byte strings, one per mesh.""" - with open(path, 'rb') as f: - data = f.read() +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- - if data[:3] != FILE_MAGIC: - raise ValueError(f"{path}: not a DCF file") +def tile_index(x, y, z): + return x + y * CHUNK_WIDTH + z * CHUNK_WIDTH * CHUNK_HEIGHT - version = struct.unpack_from(' 0: - meshes.append(verts) - else: - mesh_count = data[offset] - offset += 1 - for _ in range(mesh_count): - vert_count = struct.unpack_from(' 0: - meshes.append(verts) - - return tiles, meshes +def derive_dcf_path(json_path): + """assetsraw/chunks/chunk_X_Y_Z.json -> assets/chunks/X_Y_Z.dcf""" + base = os.path.splitext(os.path.basename(json_path))[0] + if base.startswith('chunk_'): + base = base[len('chunk_'):] + rel_dir = os.path.relpath(os.path.dirname(os.path.abspath(json_path)), ASSETSRAW_DIR) + return os.path.join(ASSETS_DIR, rel_dir, base + '.dcf') def write_dmf(path, vertex_bytes): @@ -111,18 +112,11 @@ def write_dmf(path, vertex_bytes): buf += vertex_bytes with open(path, 'wb') as f: f.write(buf) - print( - f' Wrote DMF {path}: ' - f'{vert_count} vertices, {len(buf)} bytes' - ) + print(f' Wrote DMF {path}: {vert_count} vertices, {len(buf)} bytes') def write_v3(dcf_path, tiles, mesh_names, mesh_offsets=None): - """Write a v3 DCF that references the given DMF asset paths. - - mesh_offsets is an optional list of (x, y, z) tuples, one per mesh. - Defaults to (0, 0, 0) for each mesh when omitted. - """ + """Write a v3 DCF referencing the given DMF asset paths.""" mesh_count = len(mesh_names) if mesh_offsets is None: mesh_offsets = [(0.0, 0.0, 0.0)] * mesh_count @@ -145,51 +139,179 @@ def write_v3(dcf_path, tiles, mesh_names, mesh_offsets=None): f.write(buf) print( f' Wrote DCF {dcf_path}: ' - f'version {VERSION_OUT}, {mesh_count} mesh(es)' + f'version {VERSION_OUT}, {mesh_count} mesh(es), {len(buf)} bytes' ) +# --------------------------------------------------------------------------- +# JSON -> DCF + DMF +# --------------------------------------------------------------------------- + +def build_terrain_verts(tiles_bytes): + """Generate top-face quads for every TILE_SHAPE_GROUND tile.""" + buf = bytearray() + for z in range(CHUNK_DEPTH): + for y in range(CHUNK_HEIGHT): + for x in range(CHUNK_WIDTH): + idx = tile_index(x, y, z) + tile_type = struct.unpack_from( + ' {rel}') + + write_v3(dcf_path, bytes(tiles), mesh_names, mesh_offsets) + + +def process_json(json_path): + dcf_path = derive_dcf_path(json_path) + os.makedirs(os.path.dirname(dcf_path), exist_ok=True) + print(f"{json_path} -> {dcf_path}") + from_json(json_path, dcf_path) + + +# --------------------------------------------------------------------------- +# Legacy DCF (v1/v2) -> v3 +# --------------------------------------------------------------------------- + +def read_legacy_dcf(path): + with open(path, 'rb') as f: + data = f.read() + if data[:3] != FILE_MAGIC: + raise ValueError(f"{path}: not a DCF file") + version = struct.unpack_from(' 0: + meshes.append(verts) + else: + mesh_count = data[offset] + offset += 1 + for _ in range(mesh_count): + vert_count = struct.unpack_from(' 0: + meshes.append(verts) + return tiles, meshes + + +def upgrade_dcf(path): + print(f"Upgrading legacy DCF {path} ...") + tiles, meshes = read_legacy_dcf(path) + print( + f" tiles={CHUNK_TILE_COUNT}, meshes={len(meshes)}, " + f"total_verts={sum(len(m) // VERTEX_SIZE for m in meshes)}" + ) + base = os.path.splitext(os.path.basename(path))[0] + terrain_dir = os.path.join(ASSETS_DIR, 'meshes', 'chunks') + os.makedirs(terrain_dir, exist_ok=True) + mesh_names = [] + for idx, verts in enumerate(meshes): + dmf_name = f'chunk_{base}_{idx}.dmf' + write_dmf(os.path.join(terrain_dir, dmf_name), verts) + mesh_names.append(f'meshes/chunks/{dmf_name}') + write_v3(path, tiles, mesh_names) + + +# --------------------------------------------------------------------------- +# Entry point +# --------------------------------------------------------------------------- + def main(): args = sys.argv[1:] + if not args: - print( - "Usage: python3 -m tools.asset.chunk " - " [output.dcf]" + chunks_dir = os.path.join(ASSETSRAW_DIR, 'chunks') + if not os.path.isdir(chunks_dir): + print(f"No directory found: {chunks_dir}") + sys.exit(1) + json_files = sorted( + os.path.join(chunks_dir, f) + for f in os.listdir(chunks_dir) + if f.endswith('.json') ) - sys.exit(1) + if not json_files: + print(f"No JSON files found in {chunks_dir}") + sys.exit(0) + for p in json_files: + process_json(p) + return src = args[0] - dst = args[1] if len(args) > 1 else src - - print(f"Reading {src} ...") - tiles, meshes = read_dcf(src) - print( - f" tiles={CHUNK_TILE_COUNT}, " - f"meshes={len(meshes)}, " - f"total_verts=" - f"{sum(len(m) // VERTEX_SIZE for m in meshes)}" - ) - - # Derive chunk base name from the DCF filename (e.g. "0_0_0" from - # "0_0_0.dcf") to name DMF files "chunk_0_0_0_0.dmf" etc. - base = os.path.splitext(os.path.basename(dst))[0] - - # assets/meshes/ sits beside assets/chunks/ (one dir up from the DCF). - meshes_dir = os.path.normpath( - os.path.join(os.path.dirname(os.path.abspath(dst)), '..', 'meshes') - ) - os.makedirs(meshes_dir, exist_ok=True) - - print(f"Writing DMF files to {meshes_dir} ...") - mesh_names = [] - for idx, verts in enumerate(meshes): - dmf_filename = f'chunk_{base}_{idx}.dmf' - dmf_path = os.path.join(meshes_dir, dmf_filename) - write_dmf(dmf_path, verts) - mesh_names.append(f'meshes/{dmf_filename}') - - print(f"Writing {dst} ...") - write_v3(dst, tiles, mesh_names) + if os.path.splitext(src)[1].lower() == '.json': + process_json(src) + else: + upgrade_dcf(src) if __name__ == '__main__': diff --git a/tools/color/csv/__main__.py b/tools/color.py similarity index 100% rename from tools/color/csv/__main__.py rename to tools/color.py diff --git a/tools/input/csv/__main__.py b/tools/input.py similarity index 100% rename from tools/input/csv/__main__.py rename to tools/input.py diff --git a/tools/item/csv/__main__.py b/tools/item.py similarity index 100% rename from tools/item/csv/__main__.py rename to tools/item.py diff --git a/tools/story/csv/__main__.py b/tools/story.py similarity index 100% rename from tools/story/csv/__main__.py rename to tools/story.py