more editor stuff
This commit is contained in:
@@ -74,6 +74,7 @@
|
||||
let zoomWorldH = 20;
|
||||
let dirty = false;
|
||||
let hoverTile = null;
|
||||
let fillMode = false;
|
||||
|
||||
let mapRenderer = null;
|
||||
let terrainMesh = null;
|
||||
@@ -462,13 +463,63 @@
|
||||
renderPreview();
|
||||
}
|
||||
|
||||
// 4-connected flood fill of same-type tiles at the current Z-level,
|
||||
// starting from (startX, startY), replacing them with selectedShape.
|
||||
function floodFill(startX, startY) {
|
||||
const targetType = tiles[ChunkTerrain.tileIndex(startX, startY, currentZLevel)];
|
||||
if(targetType === selectedShape) return;
|
||||
|
||||
const stack = [[startX, startY]];
|
||||
const visited = new Set();
|
||||
while(stack.length) {
|
||||
const [x, y] = stack.pop();
|
||||
const key = `${x},${y}`;
|
||||
if(visited.has(key)) continue;
|
||||
if(x < 0 || x >= ChunkTerrain.CHUNK_WIDTH || y < 0 || y >= ChunkTerrain.CHUNK_HEIGHT) continue;
|
||||
visited.add(key);
|
||||
|
||||
const idx = ChunkTerrain.tileIndex(x, y, currentZLevel);
|
||||
if(tiles[idx] !== targetType) continue;
|
||||
tiles[idx] = selectedShape;
|
||||
stack.push([x + 1, y], [x - 1, y], [x, y + 1], [x, y - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
function fillAt(e) {
|
||||
const canvas = document.getElementById("gridCanvas");
|
||||
const t = canvasToTile(e, canvas);
|
||||
if(!t) return;
|
||||
pushUndo();
|
||||
floodFill(t.x, t.y);
|
||||
markDirty();
|
||||
renderGrid();
|
||||
renderPreview();
|
||||
}
|
||||
|
||||
function rebuildTerrainMesh() {
|
||||
const floats = ChunkTerrain.buildTerrainVerts(tiles);
|
||||
terrainMesh = floats.length ? mapRenderer.createMesh(floats) : null;
|
||||
}
|
||||
|
||||
// Keeps the preview canvas's backing-store resolution matched to its
|
||||
// actual displayed CSS size (times devicePixelRatio) - the canvas
|
||||
// otherwise stays at its fixed width/height attributes (512x512) while
|
||||
// CSS stretches it to fill the column, which upscales and blurs the
|
||||
// WebGL output on wider or high-DPI screens.
|
||||
function resizePreviewCanvas() {
|
||||
const canvas = document.getElementById("previewCanvas");
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const displayWidth = Math.max(1, Math.round(canvas.clientWidth * dpr));
|
||||
const displayHeight = Math.max(1, Math.round(canvas.clientHeight * dpr));
|
||||
if(canvas.width !== displayWidth || canvas.height !== displayHeight) {
|
||||
canvas.width = displayWidth;
|
||||
canvas.height = displayHeight;
|
||||
}
|
||||
}
|
||||
|
||||
async function renderPreview() {
|
||||
if(!mapRenderer) return;
|
||||
resizePreviewCanvas();
|
||||
rebuildTerrainMesh();
|
||||
|
||||
const models = [];
|
||||
@@ -604,6 +655,11 @@
|
||||
let draggingMeshIndex = -1;
|
||||
|
||||
gridCanvas.addEventListener("mousedown", (e) => {
|
||||
if(fillMode) {
|
||||
fillAt(e);
|
||||
return;
|
||||
}
|
||||
|
||||
const hit = hitTestMesh(e, gridCanvas);
|
||||
pushUndo();
|
||||
if(hit !== -1) {
|
||||
@@ -657,8 +713,18 @@
|
||||
document.getElementById("navUp").addEventListener("click", () => goToChunk(coord.x, coord.y, coord.z + 1));
|
||||
document.getElementById("navDown").addEventListener("click", () => goToChunk(coord.x, coord.y, coord.z - 1));
|
||||
|
||||
document.getElementById("btnSave").addEventListener("click", () => {
|
||||
function runSave() {
|
||||
saveChunk().catch((e) => updateStatus(`Save failed: ${e.message}`, true));
|
||||
}
|
||||
|
||||
document.getElementById("btnSave").addEventListener("click", runSave);
|
||||
|
||||
document.getElementById("fillToggle").addEventListener("click", () => {
|
||||
fillMode = !fillMode;
|
||||
const btn = document.getElementById("fillToggle");
|
||||
btn.textContent = fillMode ? "Fill: On" : "Fill: Off";
|
||||
btn.classList.toggle("btn-secondary", fillMode);
|
||||
btn.classList.toggle("btn-outline-secondary", !fillMode);
|
||||
});
|
||||
|
||||
document.getElementById("btnAddMesh").addEventListener("click", () => {
|
||||
@@ -683,10 +749,17 @@
|
||||
renderPreview();
|
||||
}, { passive: false });
|
||||
|
||||
window.addEventListener("resize", () => renderPreview());
|
||||
|
||||
window.addEventListener("keydown", (e) => {
|
||||
if((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "z") {
|
||||
if(!(e.ctrlKey || e.metaKey)) return;
|
||||
const key = e.key.toLowerCase();
|
||||
if(key === "z") {
|
||||
e.preventDefault();
|
||||
undo();
|
||||
} else if(key === "s") {
|
||||
e.preventDefault();
|
||||
runSave();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user