more editor stuff
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"mesh": "meshes/chunks/chunk_2_0_0_0.dmf",
|
||||
"color": [
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255
|
||||
],
|
||||
"texture": "tiles.png"
|
||||
}
|
||||
+778
-778
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -52,7 +52,10 @@
|
||||
<span id="zLevelLabel">0</span>
|
||||
</div>
|
||||
<div class="d-flex gap-3">
|
||||
<div id="tilePalette" class="tile-palette"></div>
|
||||
<div class="d-flex flex-column gap-2">
|
||||
<button type="button" id="fillToggle" class="btn btn-sm btn-outline-secondary">Fill: Off</button>
|
||||
<div id="tilePalette" class="tile-palette"></div>
|
||||
</div>
|
||||
<canvas id="gridCanvas" class="border tile-grid" width="512" height="512"></canvas>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "util/memory.h"
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
#include "display/text/text.h"
|
||||
#include "display/screen/screen.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
uisettings_t UI_SETTINGS;
|
||||
@@ -75,15 +76,20 @@ errorret_t uiSettingsInit(void) {
|
||||
errorret_t uiSettingsDraw(void) {
|
||||
if(!uiMenuIsActive(&UI_SETTINGS.menu)) errorOk();
|
||||
|
||||
uint8_t rows = UI_SETTINGS_ITEM_COUNT;
|
||||
const float_t width = 300.0f;
|
||||
const float_t height = 300.0f;
|
||||
const float_t x = (float_t)SCREEN.scanX +
|
||||
((float_t)SCREEN.scanWidth - width) * 0.5f;
|
||||
const float_t y = (float_t)SCREEN.scanY +
|
||||
((float_t)SCREEN.scanHeight - height) * 0.5f;
|
||||
|
||||
errorChain(uiFrameDraw(0.0f, 0.0f, 300.0f, 300));
|
||||
errorChain(uiFrameDraw(x, y, width, height));
|
||||
errorChain(uiMenuDraw(
|
||||
&UI_SETTINGS.menu,
|
||||
UI_FRAME_BORDER_WIDTH,
|
||||
UI_FRAME_BORDER_HEIGHT,
|
||||
300.0f - (UI_FRAME_BORDER_WIDTH * 2),
|
||||
300.0f - (UI_FRAME_BORDER_HEIGHT * 2)
|
||||
x + UI_FRAME_BORDER_WIDTH,
|
||||
y + UI_FRAME_BORDER_HEIGHT,
|
||||
width - (UI_FRAME_BORDER_WIDTH * 2),
|
||||
height - (UI_FRAME_BORDER_HEIGHT * 2)
|
||||
));
|
||||
|
||||
errorChain(spriteBatchFlush());
|
||||
|
||||
Reference in New Issue
Block a user