diff --git a/editor/client/public/common/chunkterrain.js b/editor/client/public/common/chunkterrain.js index aa1290f4..4a19360e 100644 --- a/editor/client/public/common/chunkterrain.js +++ b/editor/client/public/common/chunkterrain.js @@ -105,6 +105,46 @@ const ChunkTerrain = (() => { return new Float32Array(out); } + // Four edges (SW-SE, SE-NE, NE-NW, NW-SW) of a tile's quad as line + // segments, lifted slightly above the terrain surface to avoid + // z-fighting - same corner-height convention as pushTileQuad(). + function pushTileGridLines(out, fx, fy, swZ, seZ, neZ, nwZ) { + const eps = 0.01; + const sw = [fx, fy, swZ + eps]; + const se = [fx + 1, fy, seZ + eps]; + const ne = [fx + 1, fy + 1, neZ + eps]; + const nw = [fx, fy + 1, nwZ + eps]; + const edges = [sw, se, se, ne, ne, nw, nw, sw]; + for(const [x, y, z] of edges) out.push(0, 0, x, y, z); + } + + // Generate grid-outline line segments for every non-null tile, following + // the same per-corner heights as buildTerrainVerts() so lines hug ramps + // instead of cutting through them. Returns a Float32Array of interleaved + // [u, v, x, y, z] vertices meant to be drawn with gl.LINES (pairs). + function buildGridLines(tiles) { + const out = []; + for(let z = 0; z < CHUNK_DEPTH; z++) { + for(let y = 0; y < CHUNK_HEIGHT; y++) { + for(let x = 0; x < CHUNK_WIDTH; x++) { + const type = tiles[tileIndex(x, y, z)] || TILE_SHAPE_NULL; + const corners = RAMP_CORNERS[type]; + if(!corners) continue; + + const [sw, se, ne, nw] = corners; + pushTileGridLines( + out, x, y, + (z + sw) * WORLD_LAYER_HEIGHT, + (z + se) * WORLD_LAYER_HEIGHT, + (z + ne) * WORLD_LAYER_HEIGHT, + (z + nw) * WORLD_LAYER_HEIGHT + ); + } + } + } + return new Float32Array(out); + } + return Object.freeze({ CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, TILE_COUNT, WORLD_LAYER_HEIGHT, TILE_SHAPE_NULL, TILE_SHAPE_GROUND, @@ -114,6 +154,6 @@ const ChunkTerrain = (() => { TILE_SHAPE_RAMP_SOUTHEAST, TILE_SHAPE_RAMP_SOUTHWEST, TILE_SHAPE_RAMP_NORTHEAST_INNER, TILE_SHAPE_RAMP_NORTHWEST_INNER, TILE_SHAPE_RAMP_SOUTHEAST_INNER, TILE_SHAPE_RAMP_SOUTHWEST_INNER, - RAMP_CORNERS, tileIndex, buildTerrainVerts, + RAMP_CORNERS, tileIndex, buildTerrainVerts, buildGridLines, }); })(); diff --git a/editor/client/public/map/map.js b/editor/client/public/map/map.js index a2202753..d5b2592f 100644 --- a/editor/client/public/map/map.js +++ b/editor/client/public/map/map.js @@ -146,6 +146,7 @@ let mapRenderer = null; let terrainMesh = null; + let gridLinesMesh = null; let terrainTexturePromise = null; let modelIndex = null; let neighborChunks = []; @@ -496,7 +497,7 @@ const sy = (ChunkTerrain.CHUNK_HEIGHT - 1 - y) * cell; ctx.fillStyle = SHAPE_COLORS[type] || "#15171b"; ctx.fillRect(x * cell, sy, cell, cell); - ctx.strokeStyle = "rgba(255, 255, 255, 0.08)"; + ctx.strokeStyle = "rgba(255, 255, 255, 0.25)"; ctx.strokeRect(x * cell, sy, cell, cell); const dir = SHAPE_DIRECTIONS[type]; @@ -599,6 +600,9 @@ function rebuildTerrainMesh() { const floats = ChunkTerrain.buildTerrainVerts(tiles); terrainMesh = floats.length ? mapRenderer.createMesh(floats) : null; + + const lineFloats = ChunkTerrain.buildGridLines(tiles); + gridLinesMesh = lineFloats.length ? mapRenderer.createMesh(lineFloats) : null; } // Keeps the preview canvas's backing-store resolution matched to its @@ -649,6 +653,7 @@ target: [ChunkTerrain.CHUNK_WIDTH / 2, ChunkTerrain.CHUNK_HEIGHT / 2, zLevelHeight], worldH: zoomWorldH, terrain: terrainMesh ? { mesh: terrainMesh, texture: terrainTexture } : null, + gridLines: gridLinesMesh ? { mesh: gridLinesMesh } : null, models, neighbors, highlight: hoverTile ? { x: hoverTile.x, y: hoverTile.y, z: zLevelHeight } : null, diff --git a/editor/client/public/map/renderer.js b/editor/client/public/map/renderer.js index 0c5976a7..fc9c7470 100644 --- a/editor/client/public/map/renderer.js +++ b/editor/client/public/map/renderer.js @@ -221,6 +221,7 @@ const MapRenderer = (() => { // scene = { // target: [x, y, z], worldH: number, // terrain: { mesh, texture } | null, + // gridLines: { mesh } | null, // models: [{ mesh, texture, color: [r,g,b,a in 0..1], offset: [x,y,z] }], // neighbors: [{ // offset: [x,y,z], @@ -248,6 +249,10 @@ const MapRenderer = (() => { drawMesh(scene.terrain.mesh, identity, scene.terrain.texture, [1, 1, 1, 1], true); } + if(scene.gridLines) { + drawMesh(scene.gridLines.mesh, identity, null, [0, 0, 0, 0.35], false, gl.LINES); + } + for(const model of scene.models) { const modelMatrix = mat4Translation(new Float32Array(16), model.offset); drawMesh(model.mesh, modelMatrix, model.texture, model.color, true);