Tiles
This commit is contained in:
Binary file not shown.
@@ -85,19 +85,30 @@ errorret_t assetChunkLoaderSync(assetloading_t *loading) {
|
|||||||
memoryCopy(out->tiles, data + offset, tileSize);
|
memoryCopy(out->tiles, data + offset, tileSize);
|
||||||
offset += tileSize;
|
offset += tileSize;
|
||||||
|
|
||||||
out->vertCount = endianLittleToHost32(*(uint32_t *)(data + offset));
|
out->meshCount = data[offset];
|
||||||
offset += sizeof(uint32_t);
|
offset += sizeof(uint8_t);
|
||||||
|
|
||||||
assertTrue(
|
assertTrue(
|
||||||
out->vertCount <= CHUNK_VERTEX_COUNT,
|
out->meshCount <= CHUNK_MESH_COUNT_MAX,
|
||||||
"Chunk vertex count exceeds maximum."
|
"Chunk mesh count exceeds maximum."
|
||||||
);
|
);
|
||||||
|
|
||||||
memoryCopy(
|
uint32_t poolOffset = 0;
|
||||||
out->vertices,
|
for(uint8_t m = 0; m < out->meshCount; m++) {
|
||||||
data + offset,
|
uint32_t vertCount = endianLittleToHost32(*(uint32_t *)(data + offset));
|
||||||
out->vertCount * sizeof(meshvertex_t)
|
offset += sizeof(uint32_t);
|
||||||
);
|
assertTrue(
|
||||||
|
poolOffset + vertCount <= CHUNK_VERTEX_COUNT,
|
||||||
|
"Chunk vertex data exceeds pool."
|
||||||
|
);
|
||||||
|
out->meshVertCounts[m] = vertCount;
|
||||||
|
memoryCopy(
|
||||||
|
&out->vertices[poolOffset],
|
||||||
|
data + offset,
|
||||||
|
vertCount * sizeof(meshvertex_t)
|
||||||
|
);
|
||||||
|
offset += vertCount * sizeof(meshvertex_t);
|
||||||
|
poolOffset += vertCount;
|
||||||
|
}
|
||||||
|
|
||||||
memoryFree(data);
|
memoryFree(data);
|
||||||
loading->loading.chunk.data = NULL;
|
loading->loading.chunk.data = NULL;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include "asset/assetfile.h"
|
#include "asset/assetfile.h"
|
||||||
#include "rpg/overworld/chunk.h"
|
#include "rpg/overworld/chunk.h"
|
||||||
|
|
||||||
#define ASSET_CHUNK_FILE_VERSION 1
|
#define ASSET_CHUNK_FILE_VERSION 2
|
||||||
|
|
||||||
typedef struct assetloading_s assetloading_t;
|
typedef struct assetloading_s assetloading_t;
|
||||||
typedef struct assetentry_s assetentry_t;
|
typedef struct assetentry_s assetentry_t;
|
||||||
@@ -33,7 +33,8 @@ typedef struct {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
tile_t tiles[CHUNK_TILE_COUNT];
|
tile_t tiles[CHUNK_TILE_COUNT];
|
||||||
uint32_t vertCount;
|
uint8_t meshCount;
|
||||||
|
uint32_t meshVertCounts[CHUNK_MESH_COUNT_MAX];
|
||||||
meshvertex_t vertices[CHUNK_VERTEX_COUNT];
|
meshvertex_t vertices[CHUNK_VERTEX_COUNT];
|
||||||
} assetchunkoutput_t;
|
} assetchunkoutput_t;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (c) 2025 Dominic Masters
|
* Copyright (c) 2026 Dominic Masters
|
||||||
*
|
*
|
||||||
* This software is released under the MIT License.
|
* This software is released under the MIT License.
|
||||||
* https://opensource.org/licenses/MIT
|
* https://opensource.org/licenses/MIT
|
||||||
@@ -8,24 +8,22 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "rpg/overworld/tile.h"
|
#include "rpg/overworld/tile.h"
|
||||||
#include "worldpos.h"
|
#include "worldpos.h"
|
||||||
#include "display/mesh/quad.h"
|
#include "display/mesh/mesh.h"
|
||||||
#include "display/spritebatch/spritebatch.h"
|
#include "display/spritebatch/spritebatch.h"
|
||||||
|
|
||||||
// #define CHUNK_MESH_COUNT_MAX 3
|
#define CHUNK_MESH_COUNT_MAX 10
|
||||||
#define CHUNK_VERTEX_COUNT (QUAD_VERTEX_COUNT * CHUNK_WIDTH * CHUNK_HEIGHT * 2)
|
#define CHUNK_VERTEX_COUNT 8192
|
||||||
#define CHUNK_ENTITY_COUNT_MAX 10
|
#define CHUNK_ENTITY_COUNT_MAX 10
|
||||||
|
|
||||||
typedef struct chunk_s {
|
typedef struct chunk_s {
|
||||||
chunkpos_t position;
|
chunkpos_t position;
|
||||||
tile_t tiles[CHUNK_TILE_COUNT];
|
tile_t tiles[CHUNK_TILE_COUNT];
|
||||||
|
|
||||||
|
uint8_t meshCount;
|
||||||
|
uint32_t meshVertCounts[CHUNK_MESH_COUNT_MAX];
|
||||||
meshvertex_t vertices[CHUNK_VERTEX_COUNT];
|
meshvertex_t vertices[CHUNK_VERTEX_COUNT];
|
||||||
uint32_t vertCount;
|
mesh_t meshes[CHUNK_MESH_COUNT_MAX];
|
||||||
mesh_t mesh;
|
|
||||||
|
|
||||||
// uint8_t meshCount;
|
|
||||||
// meshvertex_t vertices[CHUNK_VERTEX_COUNT_MAX];
|
|
||||||
// mesh_t meshes[CHUNK_MESH_COUNT_MAX];
|
|
||||||
uint8_t entities[CHUNK_ENTITY_COUNT_MAX];
|
uint8_t entities[CHUNK_ENTITY_COUNT_MAX];
|
||||||
} chunk_t;
|
} chunk_t;
|
||||||
|
|
||||||
|
|||||||
@@ -20,12 +20,14 @@ errorret_t mapInit() {
|
|||||||
// Setup chunk meshes
|
// Setup chunk meshes
|
||||||
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
||||||
chunk_t *chunk = &MAP.chunks[i];
|
chunk_t *chunk = &MAP.chunks[i];
|
||||||
errorChain(meshInit(
|
for(uint8_t j = 0; j < CHUNK_MESH_COUNT_MAX; j++) {
|
||||||
&chunk->mesh,
|
errorChain(meshInit(
|
||||||
MESH_PRIMITIVE_TYPE_TRIANGLES,
|
&chunk->meshes[j],
|
||||||
CHUNK_VERTEX_COUNT,
|
MESH_PRIMITIVE_TYPE_TRIANGLES,
|
||||||
chunk->vertices
|
CHUNK_VERTEX_COUNT,
|
||||||
));
|
chunk->vertices
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform "initial load"
|
// Perform "initial load"
|
||||||
@@ -196,7 +198,9 @@ void mapUpdate() {
|
|||||||
errorret_t mapDispose() {
|
errorret_t mapDispose() {
|
||||||
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
||||||
mapChunkUnload(&MAP.chunks[i]);
|
mapChunkUnload(&MAP.chunks[i]);
|
||||||
errorChain(meshDispose(&MAP.chunks[i].mesh));
|
for(uint8_t j = 0; j < CHUNK_MESH_COUNT_MAX; j++) {
|
||||||
|
errorChain(meshDispose(&MAP.chunks[i].meshes[j]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
@@ -216,14 +220,14 @@ void mapChunkUnload(chunk_t* chunk) {
|
|||||||
entity->type = ENTITY_TYPE_NULL;
|
entity->type = ENTITY_TYPE_NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chunk->vertCount = 0;
|
chunk->meshCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t mapChunkLoad(chunk_t* chunk) {
|
errorret_t mapChunkLoad(chunk_t* chunk) {
|
||||||
if(!mapIsLoaded()) errorThrow("No map loaded");
|
if(!mapIsLoaded()) errorThrow("No map loaded");
|
||||||
|
|
||||||
memorySet(chunk->entities, 0xFF, sizeof(chunk->entities));
|
memorySet(chunk->entities, 0xFF, sizeof(chunk->entities));
|
||||||
chunk->vertCount = 0;
|
chunk->meshCount = 0;
|
||||||
|
|
||||||
char_t name[64];
|
char_t name[64];
|
||||||
stringFormat(
|
stringFormat(
|
||||||
@@ -248,19 +252,26 @@ errorret_t mapChunkLoad(chunk_t* chunk) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memoryCopy(chunk->tiles, entry->data.chunk.tiles, sizeof(chunk->tiles));
|
||||||
memoryCopy(
|
memoryCopy(
|
||||||
chunk->tiles, entry->data.chunk.tiles, sizeof(chunk->tiles)
|
chunk->vertices, entry->data.chunk.vertices, sizeof(chunk->vertices)
|
||||||
);
|
);
|
||||||
chunk->vertCount = entry->data.chunk.vertCount;
|
|
||||||
memoryCopy(
|
memoryCopy(
|
||||||
chunk->vertices,
|
chunk->meshVertCounts,
|
||||||
entry->data.chunk.vertices,
|
entry->data.chunk.meshVertCounts,
|
||||||
chunk->vertCount * sizeof(meshvertex_t)
|
sizeof(chunk->meshVertCounts)
|
||||||
);
|
);
|
||||||
|
uint8_t meshCount = entry->data.chunk.meshCount;
|
||||||
assetUnlockEntry(entry);
|
assetUnlockEntry(entry);
|
||||||
|
|
||||||
if(chunk->vertCount == 0) errorOk();
|
if(meshCount == 0) errorOk();
|
||||||
errorChain(meshFlush(&chunk->mesh, 0, chunk->vertCount));
|
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]
|
||||||
|
));
|
||||||
|
}
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,31 +97,7 @@ errorret_t sceneOverworldRender(scenedata_t *sceneData) {
|
|||||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, eye));
|
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, eye));
|
||||||
|
|
||||||
// Chunks
|
// Chunks
|
||||||
{
|
errorChain(sceneOverworldDrawChunks());
|
||||||
shadermaterial_t chunkMaterial = {
|
|
||||||
.unlit = {
|
|
||||||
.color = COLOR_WHITE,
|
|
||||||
.texture = &TEXTURE_CHUNK
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t i = 0;
|
|
||||||
for(uint8_t x = 0; x < MAP_CHUNK_WIDTH; x++) {
|
|
||||||
for(uint8_t y = 0; y < MAP_CHUNK_HEIGHT; y++) {
|
|
||||||
for(uint8_t z = 0; z < MAP_CHUNK_DEPTH; z++) {
|
|
||||||
chunk_t *chunk = &MAP.chunks[i];
|
|
||||||
if(chunk->vertCount == 0) {
|
|
||||||
i++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
errorChain(shaderSetMaterial(&SHADER_UNLIT, &chunkMaterial));
|
|
||||||
errorChain(meshDraw(&chunk->mesh, 0, chunk->vertCount));
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entities
|
// Entities
|
||||||
{
|
{
|
||||||
@@ -156,6 +132,46 @@ errorret_t sceneOverworldRender(scenedata_t *sceneData) {
|
|||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
errorret_t sceneOverworldDrawChunks() {
|
||||||
|
shadermaterial_t chunkMaterial = {
|
||||||
|
.unlit = {
|
||||||
|
.color = COLOR_WHITE,
|
||||||
|
.texture = &TEXTURE_CHUNK
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
errorret_t sceneOverworldDispose(scenedata_t *sceneData) {
|
errorret_t sceneOverworldDispose(scenedata_t *sceneData) {
|
||||||
assertNotNull(sceneData, "Scene data cannot be null");
|
assertNotNull(sceneData, "Scene data cannot be null");
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,14 @@ errorret_t sceneOverworldInit(scenedata_t *sceneData);
|
|||||||
*/
|
*/
|
||||||
errorret_t sceneOverworldUpdate(scenedata_t *sceneData);
|
errorret_t sceneOverworldUpdate(scenedata_t *sceneData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws all loaded chunks in two passes: base meshes first (shared texture,
|
||||||
|
* no binds between chunks), then each chunk's additional meshes.
|
||||||
|
*
|
||||||
|
* @return An error if drawing failed, or errorOk() on success.
|
||||||
|
*/
|
||||||
|
errorret_t sceneOverworldDrawChunks();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the overworld scene.
|
* Renders the overworld scene.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
# Copyright (c) 2026 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
"""
|
||||||
|
Converts DCF chunk files from version 1 to version 2.
|
||||||
|
|
||||||
|
Version 1 format (after 8-byte header + tiles):
|
||||||
|
uint32_t vertCount
|
||||||
|
meshvertex_t vertices[vertCount]
|
||||||
|
|
||||||
|
Version 2 format (after 8-byte header + tiles):
|
||||||
|
uint8_t meshCount
|
||||||
|
for each mesh:
|
||||||
|
uint32_t vertCount
|
||||||
|
meshvertex_t vertices[vertCount]
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 -m tools.asset.chunk <input.dcf> [output.dcf]
|
||||||
|
If output is omitted the input file is updated in place.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Must match src/dusk/rpg/overworld/chunk.h
|
||||||
|
CHUNK_WIDTH = 16
|
||||||
|
CHUNK_HEIGHT = 16
|
||||||
|
CHUNK_DEPTH = 32
|
||||||
|
CHUNK_TILE_COUNT = CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH # 8192
|
||||||
|
|
||||||
|
CHUNK_MESH_COUNT_MAX = 10
|
||||||
|
CHUNK_VERTEX_COUNT = 8192
|
||||||
|
|
||||||
|
# C enum (int) = 4 bytes; meshvertex_t = uv[2]+pos[3] floats = 20 bytes
|
||||||
|
TILE_SIZE = 4
|
||||||
|
VERTEX_SIZE = 20 # 2 floats UV + 3 floats pos, MESH_ENABLE_COLOR=0
|
||||||
|
|
||||||
|
FILE_MAGIC = b'DCF'
|
||||||
|
VERSION_IN = 1
|
||||||
|
VERSION_OUT = 2
|
||||||
|
|
||||||
|
|
||||||
|
def read_v1(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('<I', data, 4)[0]
|
||||||
|
if version != VERSION_IN:
|
||||||
|
raise ValueError(f"{path}: expected version {VERSION_IN}, got {version}")
|
||||||
|
|
||||||
|
offset = 8
|
||||||
|
tiles_bytes = CHUNK_TILE_COUNT * TILE_SIZE
|
||||||
|
tiles = data[offset:offset + tiles_bytes]
|
||||||
|
offset += tiles_bytes
|
||||||
|
|
||||||
|
vert_count = struct.unpack_from('<I', data, offset)[0]
|
||||||
|
offset += 4
|
||||||
|
|
||||||
|
verts = data[offset:offset + vert_count * VERTEX_SIZE]
|
||||||
|
if len(verts) != vert_count * VERTEX_SIZE:
|
||||||
|
raise ValueError(f"{path}: truncated vertex data")
|
||||||
|
|
||||||
|
return tiles, vert_count, verts
|
||||||
|
|
||||||
|
|
||||||
|
def write_v2(path, tiles, vert_count, verts):
|
||||||
|
if vert_count > CHUNK_VERTEX_COUNT:
|
||||||
|
print(
|
||||||
|
f" Warning: {vert_count} vertices exceeds pool "
|
||||||
|
f"({CHUNK_VERTEX_COUNT}); truncating."
|
||||||
|
)
|
||||||
|
vert_count = CHUNK_VERTEX_COUNT
|
||||||
|
verts = verts[:vert_count * VERTEX_SIZE]
|
||||||
|
|
||||||
|
mesh_count = 1 if vert_count > 0 else 0
|
||||||
|
|
||||||
|
buf = bytearray()
|
||||||
|
buf += FILE_MAGIC
|
||||||
|
buf += b'\x00'
|
||||||
|
buf += struct.pack('<I', VERSION_OUT)
|
||||||
|
buf += tiles
|
||||||
|
buf += struct.pack('<B', mesh_count)
|
||||||
|
if mesh_count > 0:
|
||||||
|
buf += struct.pack('<I', vert_count)
|
||||||
|
buf += verts
|
||||||
|
|
||||||
|
with open(path, 'wb') as f:
|
||||||
|
f.write(buf)
|
||||||
|
|
||||||
|
print(
|
||||||
|
f" Wrote {path}: version {VERSION_OUT}, "
|
||||||
|
f"{mesh_count} mesh(es), {vert_count} vertices."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = sys.argv[1:]
|
||||||
|
if not args:
|
||||||
|
print("Usage: python3 -m tools.asset.chunk <input.dcf> [output.dcf]")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
src = args[0]
|
||||||
|
dst = args[1] if len(args) > 1 else src
|
||||||
|
|
||||||
|
print(f"Reading {src} ...")
|
||||||
|
tiles, vert_count, verts = read_v1(src)
|
||||||
|
print(f" tiles={CHUNK_TILE_COUNT}, vertices={vert_count}")
|
||||||
|
|
||||||
|
print(f"Writing {dst} ...")
|
||||||
|
write_v2(dst, tiles, vert_count, verts)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
# Copyright (c) 2026 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
"""
|
||||||
|
Generates chunk 0_0_0.dcf with a small hill in the centre.
|
||||||
|
|
||||||
|
Hill layout (tile coordinates, 0-based):
|
||||||
|
y=5: . . . . . . N N . . . . . . . . RAMP_NORTH (south slope)
|
||||||
|
y=6: . . . . . E H H W . . . . . . . Hill top (H), RAMP_EAST/WEST
|
||||||
|
y=7: . . . . . E H H W . . . . . . .
|
||||||
|
y=8: . . . . . . S S . . . . . . . . RAMP_SOUTH (north slope)
|
||||||
|
x=6 x=7
|
||||||
|
"""
|
||||||
|
|
||||||
|
import struct, os
|
||||||
|
|
||||||
|
# Must match src/dusk/rpg/overworld/chunk.h and tile.h
|
||||||
|
CHUNK_WIDTH = 16
|
||||||
|
CHUNK_HEIGHT = 16
|
||||||
|
CHUNK_DEPTH = 32
|
||||||
|
CHUNK_W_F = float(CHUNK_WIDTH)
|
||||||
|
|
||||||
|
TILE_NULL = 0
|
||||||
|
TILE_GROUND = 1
|
||||||
|
TILE_RAMP_NORTH = 2
|
||||||
|
TILE_RAMP_SOUTH = 3
|
||||||
|
TILE_RAMP_EAST = 4
|
||||||
|
TILE_RAMP_WEST = 5
|
||||||
|
|
||||||
|
TILE_SIZE = 4 # sizeof(tile_t) = sizeof(int)
|
||||||
|
VERT_SIZE = 20 # sizeof(meshvertex_t): uv[2] + pos[3] floats
|
||||||
|
FILE_VER = 2
|
||||||
|
|
||||||
|
# Hill geometry parameters
|
||||||
|
HILL_X = frozenset({6, 7})
|
||||||
|
HILL_Y = frozenset({6, 7})
|
||||||
|
HILL_H = 1.0
|
||||||
|
|
||||||
|
|
||||||
|
def tile_idx(cx, cy, cz):
|
||||||
|
return cz * CHUNK_WIDTH * CHUNK_HEIGHT + cy * CHUNK_WIDTH + cx
|
||||||
|
|
||||||
|
|
||||||
|
def make_vert(u, v, px, py, pz):
|
||||||
|
return struct.pack('<5f', u, v, px, py, pz)
|
||||||
|
|
||||||
|
|
||||||
|
def quad_verts(cx, cy, z_sw, z_se, z_ne, z_nw):
|
||||||
|
"""
|
||||||
|
Build 6 vertices (2 triangles) for a tile quad.
|
||||||
|
Heights at each corner: SW=south-west, SE=south-east,
|
||||||
|
NE=north-east, NW=north-west.
|
||||||
|
UV formula (verified against existing DCF data):
|
||||||
|
u = (cy + within_x) / CHUNK_WIDTH where within_x in {0,1}
|
||||||
|
v = (cx + within_y) / CHUNK_HEIGHT where within_y in {0,1}
|
||||||
|
"""
|
||||||
|
u0 = cy / CHUNK_W_F
|
||||||
|
u1 = (cy + 1) / CHUNK_W_F
|
||||||
|
v0 = cx / CHUNK_W_F
|
||||||
|
v1 = (cx + 1) / CHUNK_W_F
|
||||||
|
x0, x1 = float(cx), float(cx + 1)
|
||||||
|
y0, y1 = float(cy), float(cy + 1)
|
||||||
|
|
||||||
|
SW = make_vert(u0, v0, x0, y0, float(z_sw))
|
||||||
|
SE = make_vert(u1, v0, x1, y0, float(z_se))
|
||||||
|
NE = make_vert(u1, v1, x1, y1, float(z_ne))
|
||||||
|
NW = make_vert(u0, v1, x0, y1, float(z_nw))
|
||||||
|
|
||||||
|
return SW + SE + NE + SW + NE + NW
|
||||||
|
|
||||||
|
|
||||||
|
def flat(cx, cy, z):
|
||||||
|
return quad_verts(cx, cy, z, z, z, z)
|
||||||
|
|
||||||
|
|
||||||
|
def ramp_north(cx, cy):
|
||||||
|
return quad_verts(cx, cy, 0, 0, HILL_H, HILL_H)
|
||||||
|
|
||||||
|
|
||||||
|
def ramp_south(cx, cy):
|
||||||
|
return quad_verts(cx, cy, HILL_H, HILL_H, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def ramp_east(cx, cy):
|
||||||
|
return quad_verts(cx, cy, 0, HILL_H, HILL_H, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def ramp_west(cx, cy):
|
||||||
|
return quad_verts(cx, cy, HILL_H, 0, 0, HILL_H)
|
||||||
|
|
||||||
|
|
||||||
|
def generate():
|
||||||
|
tiles = [TILE_GROUND] * (CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH)
|
||||||
|
|
||||||
|
ramps_n = frozenset((cx, 5) for cx in HILL_X)
|
||||||
|
ramps_s = frozenset((cx, 8) for cx in HILL_X)
|
||||||
|
ramps_e = frozenset((5, cy) for cy in HILL_Y)
|
||||||
|
ramps_w = frozenset((8, cy) for cy in HILL_Y)
|
||||||
|
|
||||||
|
for cx, cy in ramps_n:
|
||||||
|
tiles[tile_idx(cx, cy, 0)] = TILE_RAMP_NORTH
|
||||||
|
for cx, cy in ramps_s:
|
||||||
|
tiles[tile_idx(cx, cy, 0)] = TILE_RAMP_SOUTH
|
||||||
|
for cx, cy in ramps_e:
|
||||||
|
tiles[tile_idx(cx, cy, 0)] = TILE_RAMP_EAST
|
||||||
|
for cx, cy in ramps_w:
|
||||||
|
tiles[tile_idx(cx, cy, 0)] = TILE_RAMP_WEST
|
||||||
|
|
||||||
|
for cx in HILL_X:
|
||||||
|
for cy in HILL_Y:
|
||||||
|
tiles[tile_idx(cx, cy, 1)] = TILE_GROUND
|
||||||
|
|
||||||
|
verts = bytearray()
|
||||||
|
|
||||||
|
for cx in range(CHUNK_WIDTH):
|
||||||
|
for cy in range(CHUNK_HEIGHT):
|
||||||
|
pos = (cx, cy)
|
||||||
|
if cx in HILL_X and cy in HILL_Y:
|
||||||
|
continue
|
||||||
|
if pos in ramps_n:
|
||||||
|
verts += ramp_north(cx, cy)
|
||||||
|
elif pos in ramps_s:
|
||||||
|
verts += ramp_south(cx, cy)
|
||||||
|
elif pos in ramps_e:
|
||||||
|
verts += ramp_east(cx, cy)
|
||||||
|
elif pos in ramps_w:
|
||||||
|
verts += ramp_west(cx, cy)
|
||||||
|
else:
|
||||||
|
verts += flat(cx, cy, 0)
|
||||||
|
|
||||||
|
for cx in sorted(HILL_X):
|
||||||
|
for cy in sorted(HILL_Y):
|
||||||
|
verts += flat(cx, cy, HILL_H)
|
||||||
|
|
||||||
|
vert_count = len(verts) // VERT_SIZE
|
||||||
|
tile_bytes = struct.pack(f'<{len(tiles)}i', *tiles)
|
||||||
|
|
||||||
|
buf = bytearray()
|
||||||
|
buf += b'DCF\x00'
|
||||||
|
buf += struct.pack('<I', FILE_VER)
|
||||||
|
buf += tile_bytes
|
||||||
|
buf += struct.pack('<B', 1)
|
||||||
|
buf += struct.pack('<I', vert_count)
|
||||||
|
buf += verts
|
||||||
|
|
||||||
|
return buf, vert_count
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
out = os.path.join(
|
||||||
|
os.path.dirname(__file__), '..', '..', '..', 'assets', 'chunks',
|
||||||
|
'0_0_0.dcf'
|
||||||
|
)
|
||||||
|
out = os.path.normpath(out)
|
||||||
|
buf, vert_count = generate()
|
||||||
|
with open(out, 'wb') as f:
|
||||||
|
f.write(buf)
|
||||||
|
print(f'Wrote {out}: {vert_count} vertices, {len(buf)} bytes')
|
||||||
Reference in New Issue
Block a user