Reintroduce multiple maps: per-map chunk directories + map_t name/mapSetMap
Move chunk assets from a single flat assetsraw/chunks (and assets/chunks) into per-map assetsraw/maps/<map>/chunks (assets/maps/<map>/chunks), so terrain mesh/model output no longer collides across maps sharing chunk coordinates. map_t gains a name[MAP_NAME_MAX] field plus mapSetMap()/ mapIsLoaded() (checks name[0] == '\0') in place of the old bool loaded flag; mapInit() now just resets state and mapSetMap() does the chunk grid load. Updated the chunk asset tool and the map editor's dev server/client to match.
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
Generates DCF + companion DMF files from raw chunk JSON files, or upgrades
|
||||
legacy DCF files (version 1 or 2) to the current version.
|
||||
|
||||
JSON input (assetsraw/chunks/chunk_X_Y_Z.json):
|
||||
JSON input (assetsraw/maps/<map>/chunks/chunk_X_Y_Z.json):
|
||||
{
|
||||
"tiles": [
|
||||
{ "pos": [x, y, z], "type": <tile_shape_int>, "tile": <uv_tile_int> }
|
||||
@@ -35,6 +35,9 @@ JSON input (assetsraw/chunks/chunk_X_Y_Z.json):
|
||||
Mesh files are located by searching under assets/meshes/ and referenced by
|
||||
path from the assets root in the DCF.
|
||||
|
||||
<map> is the map's name, up to 9 characters (MAP_NAME_MAX in
|
||||
src/dusk/rpg/overworld/map.h reserves 1 byte for the null terminator).
|
||||
|
||||
"entities" spawns things into the world when this chunk loads. A "global"
|
||||
entity is spawned via mapSpawnEntity() - globalId indexes
|
||||
ENTITY_GLOBAL_LIST (src/dusk/rpg/entity/global/entitygloballist.h) and is
|
||||
@@ -53,7 +56,8 @@ JSON input (assetsraw/chunks/chunk_X_Y_Z.json):
|
||||
trigger is bitwise MAP_TRIGGER_STEP(1)|ENTER(2)|EXIT(4).
|
||||
|
||||
Output DCF is derived automatically:
|
||||
assetsraw/chunks/chunk_X_Y_Z.json -> assets/chunks/X_Y_Z.dcf
|
||||
assetsraw/maps/<map>/chunks/chunk_X_Y_Z.json
|
||||
-> assets/maps/<map>/chunks/X_Y_Z.dcf
|
||||
|
||||
Version 5 DCF format (after 8-byte header):
|
||||
tile_t tiles[CHUNK_WIDTH * CHUNK_HEIGHT] (one per x/y column)
|
||||
@@ -85,7 +89,7 @@ DMF format:
|
||||
Each vertex: uv[2] + pos[3] = 5 x float32 LE = 20 bytes
|
||||
|
||||
Usage:
|
||||
python3 -m tools.asset.chunk # process all assetsraw/chunks/*.json
|
||||
python3 -m tools.asset.chunk # process all assetsraw/maps/*/chunks/*.json
|
||||
python3 -m tools.asset.chunk <file.json> # process one JSON file
|
||||
python3 -m tools.asset.chunk <file.dcf> # upgrade legacy (v1/v2) DCF in-place
|
||||
"""
|
||||
@@ -118,6 +122,10 @@ CHUNK_MESH_NAME_MAX = 64
|
||||
CHUNK_ENTITY_SPAWN_COUNT_MAX = 8
|
||||
CHUNK_AREA_COUNT_MAX = 4
|
||||
|
||||
# Matches MAP_NAME_MAX in src/dusk/rpg/overworld/map.h (buffer size,
|
||||
# including the null terminator - so map names are at most 9 characters).
|
||||
MAP_NAME_MAX = 10
|
||||
|
||||
ENTITY_SPAWN_KIND_GLOBAL = 0
|
||||
ENTITY_SPAWN_KIND_ITEM = 1
|
||||
|
||||
@@ -188,8 +196,29 @@ def write_model_json(path, mesh_rel, texture_rel, color):
|
||||
print(f' Wrote model JSON {path}')
|
||||
|
||||
|
||||
def chunk_map_name(json_path):
|
||||
"""assetsraw/maps/<map>/chunks/chunk_X_Y_Z.json -> <map>
|
||||
|
||||
Raises ValueError if json_path isn't under that structure, or if <map>
|
||||
is longer than MAP_NAME_MAX - 1 characters."""
|
||||
rel = os.path.relpath(os.path.abspath(json_path), ASSETSRAW_DIR)
|
||||
parts = rel.split(os.sep)
|
||||
if len(parts) != 4 or parts[0] != 'maps' or parts[2] != 'chunks':
|
||||
raise ValueError(
|
||||
f"Chunk JSON must live at assetsraw/maps/<map>/chunks/*.json, got: "
|
||||
f"{json_path}"
|
||||
)
|
||||
map_name = parts[1]
|
||||
if len(map_name) > MAP_NAME_MAX - 1:
|
||||
raise ValueError(
|
||||
f"Map name too long (> {MAP_NAME_MAX - 1} characters): {map_name}"
|
||||
)
|
||||
return map_name
|
||||
|
||||
|
||||
def derive_dcf_path(json_path):
|
||||
"""assetsraw/chunks/chunk_X_Y_Z.json -> assets/chunks/X_Y_Z.dcf"""
|
||||
"""assetsraw/maps/<map>/chunks/chunk_X_Y_Z.json
|
||||
-> assets/maps/<map>/chunks/X_Y_Z.dcf"""
|
||||
base = os.path.splitext(os.path.basename(json_path))[0]
|
||||
if base.startswith('chunk_'):
|
||||
base = base[len('chunk_'):]
|
||||
@@ -351,6 +380,8 @@ def build_terrain_verts(tiles_bytes):
|
||||
|
||||
|
||||
def from_json(json_path, dcf_path):
|
||||
map_name = chunk_map_name(json_path)
|
||||
|
||||
with open(json_path) as f:
|
||||
data = json.load(f)
|
||||
|
||||
@@ -377,15 +408,17 @@ def from_json(json_path, dcf_path):
|
||||
|
||||
dcf_base = os.path.splitext(os.path.basename(dcf_path))[0]
|
||||
|
||||
# Terrain mesh + model (only written if non-empty)
|
||||
terrain_dir = os.path.join(ASSETS_DIR, 'meshes', 'chunks')
|
||||
models_dir = os.path.join(ASSETS_DIR, 'models', 'chunks')
|
||||
# Terrain mesh + model (only written if non-empty). Namespaced under the
|
||||
# owning map so two maps can each have a chunk at the same (x, y, z)
|
||||
# without their generated terrain assets colliding.
|
||||
terrain_dir = os.path.join(ASSETS_DIR, 'maps', map_name, 'meshes', 'chunks')
|
||||
models_dir = os.path.join(ASSETS_DIR, 'maps', map_name, 'models', 'chunks')
|
||||
os.makedirs(terrain_dir, exist_ok=True)
|
||||
os.makedirs(models_dir, exist_ok=True)
|
||||
if terrain_verts:
|
||||
dmf_name = f'chunk_{dcf_base}_0.dmf'
|
||||
write_dmf(os.path.join(terrain_dir, dmf_name), terrain_verts)
|
||||
mesh_rel = f'meshes/chunks/{dmf_name}'
|
||||
mesh_rel = f'maps/{map_name}/meshes/chunks/{dmf_name}'
|
||||
json_name = f'chunk_{dcf_base}_0.json'
|
||||
write_model_json(
|
||||
os.path.join(models_dir, json_name),
|
||||
@@ -393,7 +426,7 @@ def from_json(json_path, dcf_path):
|
||||
'tiles.png',
|
||||
[255, 255, 255, 255]
|
||||
)
|
||||
model_names.append(f'models/chunks/{json_name}')
|
||||
model_names.append(f'maps/{map_name}/models/chunks/{json_name}')
|
||||
mesh_offsets.append((0.0, 0.0, 0.0))
|
||||
|
||||
# External mesh references -> look up matching model JSON
|
||||
@@ -555,22 +588,27 @@ def main():
|
||||
parser.add_argument(
|
||||
'path', nargs='?',
|
||||
help='Chunk JSON file to process, or legacy DCF file to upgrade. '
|
||||
'If omitted, processes all assetsraw/chunks/*.json'
|
||||
'If omitted, processes all assetsraw/maps/*/chunks/*.json'
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.path is None:
|
||||
chunks_dir = os.path.join(ASSETSRAW_DIR, 'chunks')
|
||||
if not os.path.isdir(chunks_dir):
|
||||
print(f"No directory found: {chunks_dir}")
|
||||
maps_dir = os.path.join(ASSETSRAW_DIR, 'maps')
|
||||
if not os.path.isdir(maps_dir):
|
||||
print(f"No directory found: {maps_dir}")
|
||||
sys.exit(1)
|
||||
json_files = sorted(
|
||||
os.path.join(chunks_dir, f)
|
||||
for f in os.listdir(chunks_dir)
|
||||
if f.endswith('.json')
|
||||
)
|
||||
json_files = []
|
||||
for map_name in sorted(os.listdir(maps_dir)):
|
||||
chunks_dir = os.path.join(maps_dir, map_name, 'chunks')
|
||||
if not os.path.isdir(chunks_dir):
|
||||
continue
|
||||
json_files.extend(sorted(
|
||||
os.path.join(chunks_dir, f)
|
||||
for f in os.listdir(chunks_dir)
|
||||
if f.endswith('.json')
|
||||
))
|
||||
if not json_files:
|
||||
print(f"No JSON files found in {chunks_dir}")
|
||||
print(f"No JSON files found under {maps_dir}")
|
||||
sys.exit(0)
|
||||
for p in json_files:
|
||||
process_json(p)
|
||||
|
||||
Reference in New Issue
Block a user