heightmap - fix issues

This commit is contained in:
Azgaar 2022-05-24 23:27:06 +03:00
parent 27a045b709
commit ff31e23a27
5 changed files with 60 additions and 44 deletions

View file

@ -1332,7 +1332,7 @@
</select> </select>
</td> </td>
<td> <td>
<button id="templateSelect" data-tip="Select heightmap template or pre-made heightmap">Select</button> <button id="templateSelectButton" data-tip="Select heightmap template or pre-made heightmap">Select</button>
</td> </td>
</tr> </tr>

View file

@ -136,7 +136,10 @@ function insertEditorHtml() {
const templatesHtml = templates const templatesHtml = templates
.map(({id, name}) => { .map(({id, name}) => {
Math.random = aleaPRNG(seed); Math.random = aleaPRNG(seed);
HeightmapGenerator.resetHeights();
const heights = HeightmapGenerator.fromTemplate(id); const heights = HeightmapGenerator.fromTemplate(id);
HeightmapGenerator.cleanup();
const dataUrl = drawHeights(heights); const dataUrl = drawHeights(heights);
return /* html */ `<article data-id="${id}" data-seed="${seed}"> return /* html */ `<article data-id="${id}" data-seed="${seed}">
@ -224,7 +227,9 @@ function regeneratePreview(article, id) {
article.dataset.seed = seed; article.dataset.seed = seed;
Math.random = aleaPRNG(seed); Math.random = aleaPRNG(seed);
HeightmapGenerator.resetHeights();
const heights = HeightmapGenerator.fromTemplate(id); const heights = HeightmapGenerator.fromTemplate(id);
HeightmapGenerator.cleanup();
const dataUrl = drawHeights(heights); const dataUrl = drawHeights(heights);
article.querySelector("img").src = dataUrl; article.querySelector("img").src = dataUrl;
} }

View file

@ -1,12 +1,14 @@
"use strict"; "use strict";
window.HeightmapGenerator = (function () { window.HeightmapGenerator = (function () {
let cells, p, heights; let heights = null;
const setHeights = h => (heights = h);
const resetHeights = () => (heights = new Uint8Array(grid.points.length));
const getHeights = () => heights;
const cleanup = () => (heights = null);
const generate = async function () { const generate = async function () {
cells = grid.cells; resetHeights();
p = grid.points;
heights = new Uint8Array(grid.points.length);
const input = document.getElementById("templateInput"); const input = document.getElementById("templateInput");
const selectedId = input.selectedIndex >= 0 ? input.selectedIndex : 0; const selectedId = input.selectedIndex >= 0 ? input.selectedIndex : 0;
@ -33,8 +35,8 @@ window.HeightmapGenerator = (function () {
canvas.remove(); canvas.remove();
img.remove(); img.remove();
cells.h = heights; grid.cells.h = heights;
heights = null; cleanup();
TIME && console.timeEnd("defineHeightmap"); TIME && console.timeEnd("defineHeightmap");
resolve(); resolve();
}; };
@ -55,8 +57,8 @@ window.HeightmapGenerator = (function () {
addStep(...elements); addStep(...elements);
} }
cells.h = heights; grid.cells.h = heights;
heights = null; cleanup();
TIME && console.timeEnd("generateHeightmap"); TIME && console.timeEnd("generateHeightmap");
}; };
@ -66,8 +68,6 @@ window.HeightmapGenerator = (function () {
if (!steps.length) throw new Error(`Heightmap template: no steps. Template: ${template}. Steps: ${steps}`); if (!steps.length) throw new Error(`Heightmap template: no steps. Template: ${template}. Steps: ${steps}`);
heights = new Uint8Array(grid.points.length);
for (const step of steps) { for (const step of steps) {
const elements = step.trim().split(" "); const elements = step.trim().split(" ");
if (elements.length < 2) throw new Error(`Heightmap template: steps < 2. Template: ${template}. Step: ${elements}`); if (elements.length < 2) throw new Error(`Heightmap template: steps < 2. Template: ${template}. Step: ${elements}`);
@ -77,20 +77,21 @@ window.HeightmapGenerator = (function () {
return heights; return heights;
}; };
function addStep(a1, a2, a3, a4, a5) { function addStep(tool, a2, a3, a4, a5) {
if (a1 === "Hill") return addHill(a2, a3, a4, a5); if (tool === "Hill") return addHill(a2, a3, a4, a5);
if (a1 === "Pit") return addPit(a2, a3, a4, a5); if (tool === "Pit") return addPit(a2, a3, a4, a5);
if (a1 === "Range") return addRange(a2, a3, a4, a5); if (tool === "Range") return addRange(a2, a3, a4, a5);
if (a1 === "Trough") return addTrough(a2, a3, a4, a5); if (tool === "Trough") return addTrough(a2, a3, a4, a5);
if (a1 === "Strait") return addStrait(a2, a3); if (tool === "Strait") return addStrait(a2, a3);
if (a1 === "Mask") return mask(a2); if (tool === "Mask") return mask(a2);
if (a1 === "Add") return modify(a3, +a2, 1); if (tool === "Invert") return invert(a2, a3);
if (a1 === "Multiply") return modify(a3, 0, +a2); if (tool === "Add") return modify(a3, +a2, 1);
if (a1 === "Smooth") return smooth(a2); if (tool === "Multiply") return modify(a3, 0, +a2);
if (tool === "Smooth") return smooth(a2);
} }
function getBlobPower() { function getBlobPower() {
const cells = +pointsInput.dataset.cells; const cells = +byId("pointsInput").dataset.cells;
if (cells === 1000) return 0.93; if (cells === 1000) return 0.93;
if (cells === 2000) return 0.95; if (cells === 2000) return 0.95;
if (cells === 5000) return 0.96; if (cells === 5000) return 0.96;
@ -107,7 +108,7 @@ window.HeightmapGenerator = (function () {
} }
function getLinePower() { function getLinePower() {
const cells = +pointsInput.dataset.cells; const cells = +byId("pointsInput").dataset.cells;
if (cells === 1000) return 0.74; if (cells === 1000) return 0.74;
if (cells === 2000) return 0.75; if (cells === 2000) return 0.75;
if (cells === 5000) return 0.78; if (cells === 5000) return 0.78;
@ -149,7 +150,7 @@ window.HeightmapGenerator = (function () {
while (queue.length) { while (queue.length) {
const q = queue.shift(); const q = queue.shift();
for (const c of cells.c[q]) { for (const c of grid.cells.c[q]) {
if (change[c]) continue; if (change[c]) continue;
change[c] = change[q] ** power * (Math.random() * 0.2 + 0.9); change[c] = change[q] ** power * (Math.random() * 0.2 + 0.9);
if (change[c] > 1) queue.push(c); if (change[c] > 1) queue.push(c);
@ -186,7 +187,7 @@ window.HeightmapGenerator = (function () {
h = h ** getBlobPower() * (Math.random() * 0.2 + 0.9); h = h ** getBlobPower() * (Math.random() * 0.2 + 0.9);
if (h < 1) return; if (h < 1) return;
cells.c[q].forEach(function (c, i) { grid.cells.c[q].forEach(function (c, i) {
if (used[c]) return; if (used[c]) return;
heights[c] = lim(heights[c] - h * (Math.random() * 0.2 + 0.9)); heights[c] = lim(heights[c] - h * (Math.random() * 0.2 + 0.9));
used[c] = 1; used[c] = 1;
@ -228,11 +229,12 @@ window.HeightmapGenerator = (function () {
// get main ridge // get main ridge
function getRange(cur, end) { function getRange(cur, end) {
const range = [cur]; const range = [cur];
const p = grid.points;
used[cur] = 1; used[cur] = 1;
while (cur !== end) { while (cur !== end) {
let min = Infinity; let min = Infinity;
cells.c[cur].forEach(function (e) { grid.cells.c[cur].forEach(function (e) {
if (used[e]) return; if (used[e]) return;
let diff = (p[end][0] - p[e][0]) ** 2 + (p[end][1] - p[e][1]) ** 2; let diff = (p[end][0] - p[e][0]) ** 2 + (p[end][1] - p[e][1]) ** 2;
if (Math.random() > 0.85) diff = diff / 2; if (Math.random() > 0.85) diff = diff / 2;
@ -261,7 +263,7 @@ window.HeightmapGenerator = (function () {
h = h ** power - 1; h = h ** power - 1;
if (h < 2) break; if (h < 2) break;
frontier.forEach(f => { frontier.forEach(f => {
cells.c[f].forEach(i => { grid.cells.c[f].forEach(i => {
if (!used[i]) { if (!used[i]) {
queue.push(i); queue.push(i);
used[i] = 1; used[i] = 1;
@ -274,7 +276,7 @@ window.HeightmapGenerator = (function () {
range.forEach((cur, d) => { range.forEach((cur, d) => {
if (d % 6 !== 0) return; if (d % 6 !== 0) return;
for (const l of d3.range(i)) { for (const l of d3.range(i)) {
const min = cells.c[cur][d3.scan(cells.c[cur], (a, b) => heights[a] - heights[b])]; // downhill cell const min = grid.cells.c[cur][d3.scan(grid.cells.c[cur], (a, b) => heights[a] - heights[b])]; // downhill cell
heights[min] = (heights[cur] * 2 + heights[min]) / 3; heights[min] = (heights[cur] * 2 + heights[min]) / 3;
cur = min; cur = min;
} }
@ -322,11 +324,12 @@ window.HeightmapGenerator = (function () {
// get main ridge // get main ridge
function getRange(cur, end) { function getRange(cur, end) {
const range = [cur]; const range = [cur];
const p = grid.points;
used[cur] = 1; used[cur] = 1;
while (cur !== end) { while (cur !== end) {
let min = Infinity; let min = Infinity;
cells.c[cur].forEach(function (e) { grid.cells.c[cur].forEach(function (e) {
if (used[e]) return; if (used[e]) return;
let diff = (p[end][0] - p[e][0]) ** 2 + (p[end][1] - p[e][1]) ** 2; let diff = (p[end][0] - p[e][0]) ** 2 + (p[end][1] - p[e][1]) ** 2;
if (Math.random() > 0.8) diff = diff / 2; if (Math.random() > 0.8) diff = diff / 2;
@ -355,7 +358,7 @@ window.HeightmapGenerator = (function () {
h = h ** power - 1; h = h ** power - 1;
if (h < 2) break; if (h < 2) break;
frontier.forEach(f => { frontier.forEach(f => {
cells.c[f].forEach(i => { grid.cells.c[f].forEach(i => {
if (!used[i]) { if (!used[i]) {
queue.push(i); queue.push(i);
used[i] = 1; used[i] = 1;
@ -368,7 +371,7 @@ window.HeightmapGenerator = (function () {
range.forEach((cur, d) => { range.forEach((cur, d) => {
if (d % 6 !== 0) return; if (d % 6 !== 0) return;
for (const l of d3.range(i)) { for (const l of d3.range(i)) {
const min = cells.c[cur][d3.scan(cells.c[cur], (a, b) => heights[a] - heights[b])]; // downhill cell const min = grid.cells.c[cur][d3.scan(grid.cells.c[cur], (a, b) => heights[a] - heights[b])]; // downhill cell
//debug.append("circle").attr("cx", p[min][0]).attr("cy", p[min][1]).attr("r", 1); //debug.append("circle").attr("cx", p[min][0]).attr("cy", p[min][1]).attr("r", 1);
heights[min] = (heights[cur] * 2 + heights[min]) / 3; heights[min] = (heights[cur] * 2 + heights[min]) / 3;
cur = min; cur = min;
@ -394,10 +397,11 @@ window.HeightmapGenerator = (function () {
function getRange(cur, end) { function getRange(cur, end) {
const range = []; const range = [];
const p = grid.points;
while (cur !== end) { while (cur !== end) {
let min = Infinity; let min = Infinity;
cells.c[cur].forEach(function (e) { grid.cells.c[cur].forEach(function (e) {
let diff = (p[end][0] - p[e][0]) ** 2 + (p[end][1] - p[e][1]) ** 2; let diff = (p[end][0] - p[e][0]) ** 2 + (p[end][1] - p[e][1]) ** 2;
if (Math.random() > 0.8) diff = diff / 2; if (Math.random() > 0.8) diff = diff / 2;
if (diff < min) { if (diff < min) {
@ -416,7 +420,7 @@ window.HeightmapGenerator = (function () {
while (width > 0) { while (width > 0) {
const exp = 0.9 - step * width; const exp = 0.9 - step * width;
range.forEach(function (r) { range.forEach(function (r) {
cells.c[r].forEach(function (e) { grid.cells.c[r].forEach(function (e) {
if (used[e]) return; if (used[e]) return;
used[e] = 1; used[e] = 1;
query.push(e); query.push(e);
@ -448,7 +452,7 @@ window.HeightmapGenerator = (function () {
const smooth = (fr = 2, add = 0) => { const smooth = (fr = 2, add = 0) => {
heights = heights.map((h, i) => { heights = heights.map((h, i) => {
const a = [h]; const a = [h];
cells.c[i].forEach(c => a.push(heights[c])); grid.cells.c[i].forEach(c => a.push(heights[c]));
if (fr === 1) return d3.mean(a) + add; if (fr === 1) return d3.mean(a) + add;
return lim((h * (fr - 1) + d3.mean(a) + add) / fr); return lim((h * (fr - 1) + d3.mean(a) + add) / fr);
}); });
@ -458,7 +462,7 @@ window.HeightmapGenerator = (function () {
const fr = power ? Math.abs(power) : 1; const fr = power ? Math.abs(power) : 1;
heights = heights.map((h, i) => { heights = heights.map((h, i) => {
const [x, y] = p[i]; const [x, y] = grid.points[i];
const nx = (2 * x) / graphWidth - 1; // [-1, 1], 0 is center const nx = (2 * x) / graphWidth - 1; // [-1, 1], 0 is center
const ny = (2 * y) / graphHeight - 1; // [-1, 1], 0 is center const ny = (2 * y) / graphHeight - 1; // [-1, 1], 0 is center
let distance = (1 - nx ** 2) * (1 - ny ** 2); // 1 is center, 0 is edge let distance = (1 - nx ** 2) * (1 - ny ** 2); // 1 is center, 0 is edge
@ -500,12 +504,12 @@ window.HeightmapGenerator = (function () {
} }
function assignColorsToHeight(imageData) { function assignColorsToHeight(imageData) {
for (let i = 0; i < cells.i.length; i++) { for (let i = 0; i < grid.cells.i.length; i++) {
const lightness = imageData[i * 4] / 255; const lightness = imageData[i * 4] / 255;
const powered = lightness < 0.2 ? lightness : 0.2 + (lightness - 0.2) ** 0.8; const powered = lightness < 0.2 ? lightness : 0.2 + (lightness - 0.2) ** 0.8;
heights[i] = minmax(Math.floor(powered * 100), 0, 100); heights[i] = minmax(Math.floor(powered * 100), 0, 100);
} }
} }
return {generate, fromTemplate, addHill, addRange, addTrough, addStrait, addPit, smooth, modify, mask, invert}; return {setHeights, resetHeights, getHeights, cleanup, generate, fromTemplate, addHill, addRange, addTrough, addStrait, addPit, smooth, modify, mask, invert};
})(); })();

View file

@ -652,17 +652,24 @@ function editHeightmap() {
if (Number.isNaN(operand)) return tip("Operand should be a number", false, "error"); if (Number.isNaN(operand)) return tip("Operand should be a number", false, "error");
if ((operator === "add" || operator === "subtract") && !Number.isInteger(operand)) return tip("Operand should be an integer", false, "error"); if ((operator === "add" || operator === "subtract") && !Number.isInteger(operand)) return tip("Operand should be an integer", false, "error");
HeightmapGenerator.setHeights(grid.cells.h);
if (operator === "multiply") HeightmapGenerator.modify(range, 0, operand, 0); if (operator === "multiply") HeightmapGenerator.modify(range, 0, operand, 0);
else if (operator === "divide") HeightmapGenerator.modify(range, 0, 1 / operand, 0); else if (operator === "divide") HeightmapGenerator.modify(range, 0, 1 / operand, 0);
else if (operator === "add") HeightmapGenerator.modify(range, operand, 1, 0); else if (operator === "add") HeightmapGenerator.modify(range, operand, 1, 0);
else if (operator === "subtract") HeightmapGenerator.modify(range, -1 * operand, 1, 0); else if (operator === "subtract") HeightmapGenerator.modify(range, -1 * operand, 1, 0);
else if (operator === "exponent") HeightmapGenerator.modify(range, 0, 1, operand); else if (operator === "exponent") HeightmapGenerator.modify(range, 0, 1, operand);
grid.cells.h = HeightmapGenerator.getHeights();
HeightmapGenerator.cleanup();
updateHeightmap(); updateHeightmap();
} }
function smoothAllHeights() { function smoothAllHeights() {
HeightmapGenerator.setHeights(grid.cells.h);
HeightmapGenerator.smooth(4, 1.5); HeightmapGenerator.smooth(4, 1.5);
grid.cells.h = HeightmapGenerator.getHeights();
HeightmapGenerator.cleanup();
updateHeightmap(); updateHeightmap();
} }
@ -879,10 +886,7 @@ function editHeightmap() {
const steps = body.querySelectorAll("div").length; const steps = body.querySelectorAll("div").length;
const changed = +body.getAttribute("data-changed"); const changed = +body.getAttribute("data-changed");
const template = e.target.value; const template = e.target.value;
if (!steps || !changed) { if (!steps || !changed) return changeTemplate(template);
changeTemplate(template);
return;
}
alertMessage.innerHTML = "Are you sure you want to select a different template? All changes will be lost."; alertMessage.innerHTML = "Are you sure you want to select a different template? All changes will be lost.";
$("#alert").dialog({ $("#alert").dialog({
@ -921,10 +925,10 @@ function editHeightmap() {
const steps = byId("templateBody").querySelectorAll("#templateBody > div"); const steps = byId("templateBody").querySelectorAll("#templateBody > div");
if (!steps.length) return; if (!steps.length) return;
grid.cells.h = new Uint8Array(grid.cells.i.length); // clean all heights
const seed = byId("templateSeed").value; const seed = byId("templateSeed").value;
if (seed) Math.random = aleaPRNG(seed); if (seed) Math.random = aleaPRNG(seed);
HeightmapGenerator.resetHeights();
restartHistory(); restartHistory();
for (const step of steps) { for (const step of steps) {
@ -948,9 +952,12 @@ function editHeightmap() {
else if (type === "Multiply") HeightmapGenerator.modify(dist, 0, +count); else if (type === "Multiply") HeightmapGenerator.modify(dist, 0, +count);
else if (type === "Smooth") HeightmapGenerator.smooth(+count); else if (type === "Smooth") HeightmapGenerator.smooth(+count);
grid.cells.h = HeightmapGenerator.getHeights();
updateHistory("noStat"); // update history on every step updateHistory("noStat"); // update history on every step
} }
grid.cells.h = HeightmapGenerator.getHeights();
HeightmapGenerator.cleanup();
updateStatistics(); updateStatistics();
mockHeightmap(); mockHeightmap();
if (byId("preview")) drawHeightmapPreview(); // update heightmap preview if opened if (byId("preview")) drawHeightmapPreview(); // update heightmap preview if opened

View file

@ -142,7 +142,7 @@ optionsContent.addEventListener("click", function (event) {
else if (id === "optionsMapHistory") showSeedHistoryDialog(); else if (id === "optionsMapHistory") showSeedHistoryDialog();
else if (id === "optionsCopySeed") copyMapURL(); else if (id === "optionsCopySeed") copyMapURL();
else if (id === "optionsEraRegenerate") regenerateEra(); else if (id === "optionsEraRegenerate") regenerateEra();
else if (id === "templateSelect") openTemplateSelectionDialog(); else if (id === "templateSelectButton") openTemplateSelectionDialog();
else if (id === "zoomExtentDefault") restoreDefaultZoomExtent(); else if (id === "zoomExtentDefault") restoreDefaultZoomExtent();
else if (id === "translateExtent") toggleTranslateExtent(event.target); else if (id === "translateExtent") toggleTranslateExtent(event.target);
else if (id === "speakerTest") testSpeaker(); else if (id === "speakerTest") testSpeaker();