diff --git a/assets/chunks/2_0_0.dcf b/assets/chunks/2_0_0.dcf index 51ef0e9c..6d3eb0e2 100644 Binary files a/assets/chunks/2_0_0.dcf and b/assets/chunks/2_0_0.dcf differ diff --git a/assets/meshes/chunks/chunk_0_0_0_0.dmf b/assets/meshes/chunks/chunk_0_0_0_0.dmf index d443df1b..3d4a13bb 100644 Binary files a/assets/meshes/chunks/chunk_0_0_0_0.dmf and b/assets/meshes/chunks/chunk_0_0_0_0.dmf differ diff --git a/assets/meshes/chunks/chunk_2_0_0_0.dmf b/assets/meshes/chunks/chunk_2_0_0_0.dmf index 53ea64dd..119620ea 100644 Binary files a/assets/meshes/chunks/chunk_2_0_0_0.dmf and b/assets/meshes/chunks/chunk_2_0_0_0.dmf differ diff --git a/assetsraw/chunks/chunk_2_0_0.json b/assetsraw/chunks/chunk_2_0_0.json index 558b76ca..7ad75503 100644 --- a/assetsraw/chunks/chunk_2_0_0.json +++ b/assetsraw/chunks/chunk_2_0_0.json @@ -1464,7 +1464,7 @@ 10, 0 ], - "type": 1, + "type": 4, "tile": 0 }, { @@ -1473,7 +1473,7 @@ 10, 0 ], - "type": 1, + "type": 4, "tile": 0 }, { @@ -1482,7 +1482,7 @@ 10, 0 ], - "type": 1, + "type": 4, "tile": 0 }, { @@ -1491,7 +1491,7 @@ 10, 0 ], - "type": 1, + "type": 4, "tile": 0 }, { @@ -1500,7 +1500,7 @@ 10, 0 ], - "type": 1, + "type": 4, "tile": 0 }, { @@ -1509,7 +1509,7 @@ 10, 0 ], - "type": 1, + "type": 4, "tile": 0 }, { @@ -2267,6 +2267,276 @@ ], "type": 1, "tile": 0 + }, + { + "pos": [ + 6, + 9, + 1 + ], + "type": 4, + "tile": 0 + }, + { + "pos": [ + 7, + 9, + 1 + ], + "type": 4, + "tile": 0 + }, + { + "pos": [ + 8, + 9, + 1 + ], + "type": 4, + "tile": 0 + }, + { + "pos": [ + 9, + 9, + 1 + ], + "type": 4, + "tile": 0 + }, + { + "pos": [ + 10, + 9, + 1 + ], + "type": 4, + "tile": 0 + }, + { + "pos": [ + 11, + 9, + 1 + ], + "type": 4, + "tile": 0 + }, + { + "pos": [ + 6, + 8, + 2 + ], + "type": 4, + "tile": 0 + }, + { + "pos": [ + 7, + 8, + 2 + ], + "type": 4, + "tile": 0 + }, + { + "pos": [ + 8, + 8, + 2 + ], + "type": 4, + "tile": 0 + }, + { + "pos": [ + 9, + 8, + 2 + ], + "type": 4, + "tile": 0 + }, + { + "pos": [ + 10, + 8, + 2 + ], + "type": 4, + "tile": 0 + }, + { + "pos": [ + 11, + 8, + 2 + ], + "type": 4, + "tile": 0 + }, + { + "pos": [ + 6, + 5, + 3 + ], + "type": 1, + "tile": 0 + }, + { + "pos": [ + 7, + 5, + 3 + ], + "type": 1, + "tile": 0 + }, + { + "pos": [ + 8, + 5, + 3 + ], + "type": 1, + "tile": 0 + }, + { + "pos": [ + 9, + 5, + 3 + ], + "type": 1, + "tile": 0 + }, + { + "pos": [ + 10, + 5, + 3 + ], + "type": 1, + "tile": 0 + }, + { + "pos": [ + 11, + 5, + 3 + ], + "type": 1, + "tile": 0 + }, + { + "pos": [ + 6, + 6, + 3 + ], + "type": 1, + "tile": 0 + }, + { + "pos": [ + 7, + 6, + 3 + ], + "type": 1, + "tile": 0 + }, + { + "pos": [ + 8, + 6, + 3 + ], + "type": 1, + "tile": 0 + }, + { + "pos": [ + 9, + 6, + 3 + ], + "type": 1, + "tile": 0 + }, + { + "pos": [ + 10, + 6, + 3 + ], + "type": 1, + "tile": 0 + }, + { + "pos": [ + 11, + 6, + 3 + ], + "type": 1, + "tile": 0 + }, + { + "pos": [ + 6, + 7, + 3 + ], + "type": 1, + "tile": 0 + }, + { + "pos": [ + 7, + 7, + 3 + ], + "type": 1, + "tile": 0 + }, + { + "pos": [ + 8, + 7, + 3 + ], + "type": 1, + "tile": 0 + }, + { + "pos": [ + 9, + 7, + 3 + ], + "type": 1, + "tile": 0 + }, + { + "pos": [ + 10, + 7, + 3 + ], + "type": 1, + "tile": 0 + }, + { + "pos": [ + 11, + 7, + 3 + ], + "type": 1, + "tile": 0 } ], "meshes": [] diff --git a/editor/client/public/common/chunkterrain.js b/editor/client/public/common/chunkterrain.js index b01af727..aa1290f4 100644 --- a/editor/client/public/common/chunkterrain.js +++ b/editor/client/public/common/chunkterrain.js @@ -17,6 +17,10 @@ const ChunkTerrain = (() => { const CHUNK_DEPTH = 4; const TILE_COUNT = CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH; + // World-space Z distance of one Z-layer/story, in world-float space. + // Must match WORLD_LAYER_HEIGHT in src/dusk/rpg/overworld/worldpos.h. + const WORLD_LAYER_HEIGHT = Math.SQRT1_2; + const TILE_SHAPE_NULL = 0; const TILE_SHAPE_GROUND = 1; const TILE_SHAPE_RAMP_NORTH = 2; @@ -88,7 +92,13 @@ const ChunkTerrain = (() => { const v1 = (y + 1) / CHUNK_HEIGHT; const [sw, se, ne, nw] = corners; - pushTileQuad(out, u0, u1, v0, v1, x, y, z + sw, z + se, z + ne, z + nw); + pushTileQuad( + out, u0, u1, v0, v1, x, y, + (z + sw) * WORLD_LAYER_HEIGHT, + (z + se) * WORLD_LAYER_HEIGHT, + (z + ne) * WORLD_LAYER_HEIGHT, + (z + nw) * WORLD_LAYER_HEIGHT + ); } } } @@ -96,7 +106,7 @@ const ChunkTerrain = (() => { } return Object.freeze({ - CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, TILE_COUNT, + CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, TILE_COUNT, WORLD_LAYER_HEIGHT, TILE_SHAPE_NULL, TILE_SHAPE_GROUND, TILE_SHAPE_RAMP_NORTH, TILE_SHAPE_RAMP_EAST, TILE_SHAPE_RAMP_SOUTH, TILE_SHAPE_RAMP_WEST, diff --git a/editor/client/public/map/map.js b/editor/client/public/map/map.js index 62177869..a2202753 100644 --- a/editor/client/public/map/map.js +++ b/editor/client/public/map/map.js @@ -262,6 +262,13 @@ return modelIndex; } + // Scales a mesh's stored [x, y, z] offset for preview, matching the + // Z scaling mapChunkLoaded() applies at runtime (chunk.meshOffsets[m][2] + // * WORLD_LAYER_HEIGHT) - x/y stay 1:1 since only Z is height-scaled. + function scaledMeshOffset(pos) { + return [pos[0], pos[1], pos[2] * ChunkTerrain.WORLD_LAYER_HEIGHT]; + } + // Resolves a chunk mesh entry's bare filename (e.g. "house_5_3.dmf") to // its model JSON by basename, mirroring find_model() in // tools/asset/chunk/__main__.py - the chunk JSON only ever stores a bare @@ -330,7 +337,7 @@ for(const m of neighborMeshes) { try { const resolved = await resolveModelForMeshFile(m.file); - if(resolved) models.push({ ...resolved, offset: m.pos }); + if(resolved) models.push({ ...resolved, offset: scaledMeshOffset(m.pos) }); } catch(e) { console.warn("Failed to load neighbor mesh preview", m.file, e); } @@ -619,7 +626,7 @@ for(const m of meshes) { try { const resolved = await resolveModelForMeshFile(m.file); - if(resolved) models.push({ ...resolved, offset: m.pos }); + if(resolved) models.push({ ...resolved, offset: scaledMeshOffset(m.pos) }); } catch(e) { console.warn("Failed to load mesh preview", m.file, e); } @@ -637,13 +644,14 @@ models: n.models, })); + const zLevelHeight = currentZLevel * ChunkTerrain.WORLD_LAYER_HEIGHT; mapRenderer.render({ - target: [ChunkTerrain.CHUNK_WIDTH / 2, ChunkTerrain.CHUNK_HEIGHT / 2, currentZLevel], + target: [ChunkTerrain.CHUNK_WIDTH / 2, ChunkTerrain.CHUNK_HEIGHT / 2, zLevelHeight], worldH: zoomWorldH, terrain: terrainMesh ? { mesh: terrainMesh, texture: terrainTexture } : null, models, neighbors, - highlight: hoverTile ? { x: hoverTile.x, y: hoverTile.y, z: currentZLevel } : null, + highlight: hoverTile ? { x: hoverTile.x, y: hoverTile.y, z: zLevelHeight } : null, }); } diff --git a/src/dusk/rpg/entity/anim/entityanimidle.c b/src/dusk/rpg/entity/anim/entityanimidle.c index af4b493c..fb9f5d4a 100644 --- a/src/dusk/rpg/entity/anim/entityanimidle.c +++ b/src/dusk/rpg/entity/anim/entityanimidle.c @@ -11,6 +11,7 @@ void entityAnimIdleUpdate(entity_t *entity) { entity->renderPosition[0] = (float_t)entity->position.x; entity->renderPosition[1] = (float_t)entity->position.y; - entity->renderPosition[2] = (float_t)entity->position.z - + entityAnimTileZOffset(entity->position); + entity->renderPosition[2] = ( + (float_t)entity->position.z + entityAnimTileZOffset(entity->position) + ) * WORLD_LAYER_HEIGHT; } diff --git a/src/dusk/rpg/entity/anim/entityanimrun.c b/src/dusk/rpg/entity/anim/entityanimrun.c index abab5e6d..27a32c2c 100644 --- a/src/dusk/rpg/entity/anim/entityanimrun.c +++ b/src/dusk/rpg/entity/anim/entityanimrun.c @@ -20,5 +20,5 @@ void entityAnimRunUpdate(entity_t *entity) { entity->renderPosition[1] = (float_t)entity->lastPosition.y + t * ( (float_t)entity->position.y - (float_t)entity->lastPosition.y ); - entity->renderPosition[2] = zFrom + t * (zTo - zFrom); + entity->renderPosition[2] = (zFrom + t * (zTo - zFrom)) * WORLD_LAYER_HEIGHT; } diff --git a/src/dusk/rpg/entity/anim/entityanimturn.c b/src/dusk/rpg/entity/anim/entityanimturn.c index 358de4c1..d31ad814 100644 --- a/src/dusk/rpg/entity/anim/entityanimturn.c +++ b/src/dusk/rpg/entity/anim/entityanimturn.c @@ -11,6 +11,7 @@ void entityAnimTurnUpdate(entity_t *entity) { entity->renderPosition[0] = (float_t)entity->position.x; entity->renderPosition[1] = (float_t)entity->position.y; - entity->renderPosition[2] = (float_t)entity->position.z - + entityAnimTileZOffset(entity->position); + entity->renderPosition[2] = ( + (float_t)entity->position.z + entityAnimTileZOffset(entity->position) + ) * WORLD_LAYER_HEIGHT; } diff --git a/src/dusk/rpg/entity/anim/entityanimwalk.c b/src/dusk/rpg/entity/anim/entityanimwalk.c index 52c9a488..e8aa2833 100644 --- a/src/dusk/rpg/entity/anim/entityanimwalk.c +++ b/src/dusk/rpg/entity/anim/entityanimwalk.c @@ -20,5 +20,5 @@ void entityAnimWalkUpdate(entity_t *entity) { entity->renderPosition[1] = (float_t)entity->lastPosition.y + t * ( (float_t)entity->position.y - (float_t)entity->lastPosition.y ); - entity->renderPosition[2] = zFrom + t * (zTo - zFrom); + entity->renderPosition[2] = (zFrom + t * (zTo - zFrom)) * WORLD_LAYER_HEIGHT; } diff --git a/src/dusk/rpg/overworld/map.c b/src/dusk/rpg/overworld/map.c index ed6d5978..c81bd1d0 100644 --- a/src/dusk/rpg/overworld/map.c +++ b/src/dusk/rpg/overworld/map.c @@ -296,7 +296,9 @@ void mapChunkLoaded(void *params, void *user) { ); worldpos_t wp; chunkPosToWorldPos(&chunk->position, &wp); - vec3 wpf = { (float_t)wp.x, (float_t)wp.y, (float_t)wp.z }; + vec3 wpf = { + (float_t)wp.x, (float_t)wp.y, (float_t)wp.z * WORLD_LAYER_HEIGHT + }; for(uint8_t m = 0; m < meshCount; m++) { stringCopy( chunk->modelNames[m], @@ -307,8 +309,13 @@ void mapChunkLoaded(void *params, void *user) { entry->data.chunk.meshOffsets[m], chunk->meshOffsets[m] ); + vec3 scaledOffset = { + chunk->meshOffsets[m][0], + chunk->meshOffsets[m][1], + chunk->meshOffsets[m][2] * WORLD_LAYER_HEIGHT + }; vec3 pos; - glm_vec3_add(wpf, chunk->meshOffsets[m], pos); + glm_vec3_add(wpf, scaledOffset, pos); glm_translate_make(chunk->meshModels[m], pos); chunk->modelEntries[m] = entry->data.chunk.modelEntries[m]; entry->data.chunk.modelEntries[m] = NULL; diff --git a/src/dusk/rpg/overworld/worldpos.h b/src/dusk/rpg/overworld/worldpos.h index 4e3f8c2d..cb8c7575 100644 --- a/src/dusk/rpg/overworld/worldpos.h +++ b/src/dusk/rpg/overworld/worldpos.h @@ -11,6 +11,12 @@ #define TILE_SIZE_PIXELS 24 +// World-space Z distance of one Z-layer/story, in render/world-float +// space. A ramp rises this much over a horizontal run of 1 tile-width; +// chosen as 1/sqrt(2) so the ramp's slope, derived from a 45-45-90 +// triangle with hypotenuse 1, matches the length of a flat tile edge. +#define WORLD_LAYER_HEIGHT 0.70710678f + #define CHUNK_WIDTH 16 #define CHUNK_HEIGHT 16 #define CHUNK_DEPTH 4 diff --git a/src/dusk/rpg/rpgcamera.c b/src/dusk/rpg/rpgcamera.c index 6a247fca..7660be51 100644 --- a/src/dusk/rpg/rpgcamera.c +++ b/src/dusk/rpg/rpgcamera.c @@ -65,7 +65,7 @@ errorret_t rpgCameraUpdate(void) { chunkpos_t chunkPos = { .x = (chunkunit_t)floorf(pos[0] / CHUNK_WIDTH), .y = (chunkunit_t)floorf(pos[1] / CHUNK_HEIGHT), - .z = (chunkunit_t)floorf(pos[2] / CHUNK_DEPTH) + .z = (chunkunit_t)floorf(pos[2] / WORLD_LAYER_HEIGHT / CHUNK_DEPTH) }; errorChain(mapPositionSet((chunkpos_t){ diff --git a/src/dusk/scene/overworld/sceneoverworld.c b/src/dusk/scene/overworld/sceneoverworld.c index c602699b..b005abb0 100644 --- a/src/dusk/scene/overworld/sceneoverworld.c +++ b/src/dusk/scene/overworld/sceneoverworld.c @@ -92,7 +92,7 @@ errorret_t sceneOverworldRender(scenedata_t *sceneData) { spritebatchsprite_t sprite; glm_vec3_copy(ent->renderPosition, sprite.min); - glm_vec3_add(sprite.min, (vec3){ 0, 0, 0.05f }, sprite.min);// Stop Fight + glm_vec3_add(sprite.min, (vec3){ 0, -0.05f, 0.05f }, sprite.min);// Stop Fight glm_vec3_copy(sprite.min, sprite.max); glm_vec3_add(sprite.max, (vec3){ 1, 1, 0 }, sprite.max); glm_vec2_copy((vec2){ 0, 0 }, sprite.uvMin); diff --git a/tools/asset/chunk/__main__.py b/tools/asset/chunk/__main__.py index 50e03110..1c207cee 100644 --- a/tools/asset/chunk/__main__.py +++ b/tools/asset/chunk/__main__.py @@ -44,6 +44,7 @@ Usage: """ import json +import math import os import struct import sys @@ -58,6 +59,10 @@ CHUNK_HEIGHT = 16 CHUNK_DEPTH = 4 CHUNK_TILE_COUNT = CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH # 1024 +# World-space Z distance of one Z-layer/story, in world-float space. +# Must match WORLD_LAYER_HEIGHT in src/dusk/rpg/overworld/worldpos.h. +WORLD_LAYER_HEIGHT = 1.0 / math.sqrt(2) + CHUNK_MESH_COUNT_MAX = 10 CHUNK_MESH_NAME_MAX = 64 @@ -231,7 +236,10 @@ def build_terrain_verts(tiles_bytes): sw, se, ne, nw = corners buf += _tile_quad( u0, u1, v0, v1, fx, fy, - fz + sw, fz + se, fz + ne, fz + nw + (fz + sw) * WORLD_LAYER_HEIGHT, + (fz + se) * WORLD_LAYER_HEIGHT, + (fz + ne) * WORLD_LAYER_HEIGHT, + (fz + nw) * WORLD_LAYER_HEIGHT ) return bytes(buf)