mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 09:41:24 +01:00
refactor: Biomes.define - partial
This commit is contained in:
parent
bb78d5168b
commit
21234dc414
7 changed files with 72 additions and 59 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
import {clipPoly} from "utils/lineUtils";
|
import {clipPoly} from "utils/lineUtils";
|
||||||
|
import {TIME} from "config/logging";
|
||||||
|
|
||||||
export function drawBiomes() {
|
export function drawBiomes() {
|
||||||
TIME && console.time("drawBiomes");
|
TIME && console.time("drawBiomes");
|
||||||
|
|
@ -6,6 +7,7 @@ export function drawBiomes() {
|
||||||
|
|
||||||
const {cells, vertices} = pack;
|
const {cells, vertices} = pack;
|
||||||
const n = cells.i.length;
|
const n = cells.i.length;
|
||||||
|
|
||||||
const used = new Uint8Array(cells.i.length);
|
const used = new Uint8Array(cells.i.length);
|
||||||
const paths = new Array(biomesData.i.length).fill("");
|
const paths = new Array(biomesData.i.length).fill("");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import {pick} from "utils/functionUtils";
|
import {pick} from "utils/functionUtils";
|
||||||
|
|
||||||
export function drawRivers(pack: IPack) {
|
export function drawRivers() {
|
||||||
rivers.selectAll("*").remove();
|
rivers.selectAll("*").remove();
|
||||||
|
|
||||||
const {addMeandering, getRiverPath} = window.Rivers;
|
const {addMeandering, getRiverPath} = window.Rivers;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import * as d3 from "d3";
|
import * as d3 from "d3";
|
||||||
|
|
||||||
import {TIME} from "config/logging";
|
import {TIME} from "config/logging";
|
||||||
import {isLand} from "utils/graphUtils";
|
|
||||||
import {rn} from "utils/numberUtils";
|
import {rn} from "utils/numberUtils";
|
||||||
|
import {MIN_LAND_HEIGHT} from "config/generation";
|
||||||
|
|
||||||
window.Biomes = (function () {
|
window.Biomes = (function () {
|
||||||
const getDefault = () => {
|
const getDefault = () => {
|
||||||
|
|
@ -78,40 +78,40 @@ window.Biomes = (function () {
|
||||||
return {i: d3.range(0, name.length), name, color, biomesMartix, habitability, iconsDensity, icons, cost};
|
return {i: d3.range(0, name.length), name, color, biomesMartix, habitability, iconsDensity, icons, cost};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// assign biome id for each cell
|
||||||
|
function define({temp, prec, flux, riverIds, heights, neighbors, gridReference}) {
|
||||||
|
TIME && console.time("defineBiomes");
|
||||||
|
|
||||||
|
const biome = new Uint8Array(heights.length); // biomes array
|
||||||
|
|
||||||
|
for (let cellId = 0; cellId < heights.length; cellId++) {
|
||||||
|
const temperature = temp[gridReference[cellId]];
|
||||||
|
const height = heights[cellId];
|
||||||
|
const moisture = height < MIN_LAND_HEIGHT ? 0 : calculateMoisture(cellId);
|
||||||
|
biome[cellId] = getId(moisture, temperature, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateMoisture(cellId) {
|
||||||
|
let moist = prec[gridReference[cellId]];
|
||||||
|
if (riverIds[cellId]) moist += Math.max(flux[cellId] / 20, 2);
|
||||||
|
|
||||||
|
const moistAround = neighbors[cellId]
|
||||||
|
.filter(neibCellId => heights[neibCellId] >= MIN_LAND_HEIGHT)
|
||||||
|
.map(c => prec[gridReference[c]])
|
||||||
|
.concat([moist]);
|
||||||
|
return rn(4 + d3.mean(moistAround));
|
||||||
|
}
|
||||||
|
|
||||||
|
TIME && console.timeEnd("defineBiomes");
|
||||||
|
return biome;
|
||||||
|
}
|
||||||
|
|
||||||
function isWetLand(moisture, temperature, height) {
|
function isWetLand(moisture, temperature, height) {
|
||||||
if (moisture > 40 && temperature > -2 && height < 25) return true; //near coast
|
if (moisture > 40 && temperature > -2 && height < 25) return true; //near coast
|
||||||
if (moisture > 24 && temperature > -2 && height > 24 && height < 60) return true; //off coast
|
if (moisture > 24 && temperature > -2 && height > 24 && height < 60) return true; //off coast
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// assign biome id for each cell
|
|
||||||
function define(pack, grid) {
|
|
||||||
TIME && console.time("defineBiomes");
|
|
||||||
const {cells} = pack;
|
|
||||||
const {temp, prec} = grid.cells;
|
|
||||||
cells.biome = new Uint8Array(cells.i.length); // biomes array
|
|
||||||
|
|
||||||
for (const i of cells.i) {
|
|
||||||
const temperature = temp[cells.g[i]];
|
|
||||||
const height = cells.h[i];
|
|
||||||
const moisture = height < 20 ? 0 : calculateMoisture(i);
|
|
||||||
cells.biome[i] = getId(moisture, temperature, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateMoisture(i) {
|
|
||||||
let moist = prec[cells.g[i]];
|
|
||||||
if (cells.r[i]) moist += Math.max(cells.fl[i] / 20, 2);
|
|
||||||
|
|
||||||
const n = cells.c[i]
|
|
||||||
.filter(isLand)
|
|
||||||
.map(c => prec[cells.g[c]])
|
|
||||||
.concat([moist]);
|
|
||||||
return rn(4 + d3.mean(n));
|
|
||||||
}
|
|
||||||
|
|
||||||
TIME && console.timeEnd("defineBiomes");
|
|
||||||
}
|
|
||||||
|
|
||||||
// assign biome id to a cell
|
// assign biome id to a cell
|
||||||
function getId(moisture, temperature, height) {
|
function getId(moisture, temperature, height) {
|
||||||
if (height < 20) return 0; // marine biome: all water cells
|
if (height < 20) return 0; // marine biome: all water cells
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,8 @@ async function generate(options?: IGenerationOptions) {
|
||||||
// renderLayer("cells");
|
// renderLayer("cells");
|
||||||
renderLayer("features");
|
renderLayer("features");
|
||||||
renderLayer("heightmap");
|
renderLayer("heightmap");
|
||||||
renderLayer("rivers", pack);
|
renderLayer("rivers");
|
||||||
|
// renderLayer("biomes");
|
||||||
|
|
||||||
WARN && console.warn(`TOTAL: ${rn((performance.now() - timeStart) / 1000, 2)}s`);
|
WARN && console.warn(`TOTAL: ${rn((performance.now() - timeStart) / 1000, 2)}s`);
|
||||||
// showStatistics();
|
// showStatistics();
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,10 @@ import {rn} from "utils/numberUtils";
|
||||||
import {generateRivers} from "./rivers";
|
import {generateRivers} from "./rivers";
|
||||||
|
|
||||||
const {LAND_COAST, WATER_COAST, DEEPER_WATER} = DISTANCE_FIELD;
|
const {LAND_COAST, WATER_COAST, DEEPER_WATER} = DISTANCE_FIELD;
|
||||||
// const {Lakes, OceanLayers, Biomes, Cultures, BurgsAndStates, Religions, Military, Markers, Names} = window;
|
const {Biomes} = window;
|
||||||
|
|
||||||
export function createPack(grid: IGrid): IPack {
|
export function createPack(grid: IGrid): IPack {
|
||||||
|
const {temp, prec} = grid.cells;
|
||||||
const {vertices, cells} = repackGrid(grid);
|
const {vertices, cells} = repackGrid(grid);
|
||||||
|
|
||||||
const {features, featureIds, distanceField, haven, harbor} = markupPackFeatures(
|
const {features, featureIds, distanceField, haven, harbor} = markupPackFeatures(
|
||||||
|
|
@ -29,18 +30,26 @@ export function createPack(grid: IGrid): IPack {
|
||||||
const {
|
const {
|
||||||
heights,
|
heights,
|
||||||
flux,
|
flux,
|
||||||
r,
|
riverIds,
|
||||||
conf,
|
conf,
|
||||||
rivers: rawRivers,
|
rivers: rawRivers,
|
||||||
mergedFeatures
|
mergedFeatures
|
||||||
} = generateRivers(
|
} = generateRivers(
|
||||||
{...pick(cells, "i", "c", "b", "g", "h", "p"), f: featureIds, t: distanceField, haven},
|
{...pick(cells, "i", "c", "b", "g", "h", "p"), f: featureIds, t: distanceField, haven},
|
||||||
features,
|
features,
|
||||||
grid.cells.prec,
|
prec,
|
||||||
grid.cells.temp
|
temp
|
||||||
);
|
);
|
||||||
|
|
||||||
// Biomes.define(newPack, grid);
|
const biome: IPack["cells"]["biome"] = Biomes.define({
|
||||||
|
temp,
|
||||||
|
prec,
|
||||||
|
flux,
|
||||||
|
riverIds,
|
||||||
|
heights,
|
||||||
|
neighbors: cells.c,
|
||||||
|
gridReference: cells.g
|
||||||
|
});
|
||||||
|
|
||||||
// const rankCellsData = pick(newPack.cells, "i", "f", "fl", "conf", "r", "h", "area", "biome", "haven", "harbor");
|
// const rankCellsData = pick(newPack.cells, "i", "f", "fl", "conf", "r", "h", "area", "biome", "haven", "harbor");
|
||||||
// rankCells(newPack.features!, rankCellsData);
|
// rankCells(newPack.features!, rankCellsData);
|
||||||
|
|
@ -79,8 +88,9 @@ export function createPack(grid: IGrid): IPack {
|
||||||
haven,
|
haven,
|
||||||
harbor,
|
harbor,
|
||||||
fl: flux,
|
fl: flux,
|
||||||
r,
|
r: riverIds,
|
||||||
conf
|
conf,
|
||||||
|
biome
|
||||||
},
|
},
|
||||||
features: mergedFeatures,
|
features: mergedFeatures,
|
||||||
rivers: rawRivers
|
rivers: rawRivers
|
||||||
|
|
|
||||||
|
|
@ -40,19 +40,19 @@ export function generateRivers(
|
||||||
const cellsNumberModifier = (points / 10000) ** 0.25;
|
const cellsNumberModifier = (points / 10000) ** 0.25;
|
||||||
|
|
||||||
const {flux, lakeData} = drainWater();
|
const {flux, lakeData} = drainWater();
|
||||||
const {r, conf, rivers} = defineRivers();
|
const {riverIds, conf, rivers} = defineRivers();
|
||||||
const heights = downcutRivers(currentCellHeights);
|
const heights = downcutRivers(currentCellHeights);
|
||||||
|
|
||||||
const mergedFeatures = mergeLakeData(features, lakeData, rivers);
|
const mergedFeatures = mergeLakeData(features, lakeData, rivers);
|
||||||
|
|
||||||
TIME && console.timeEnd("generateRivers");
|
TIME && console.timeEnd("generateRivers");
|
||||||
|
|
||||||
return {heights, flux, r, conf, rivers, mergedFeatures};
|
return {heights, flux, riverIds, conf, rivers, mergedFeatures};
|
||||||
|
|
||||||
function drainWater() {
|
function drainWater() {
|
||||||
const MIN_FLUX_TO_FORM_RIVER = 30;
|
const MIN_FLUX_TO_FORM_RIVER = 30;
|
||||||
|
|
||||||
const riverIds = new Uint16Array(cellsNumber);
|
const tempRiverIds = new Uint16Array(cellsNumber);
|
||||||
const confluence = new Uint8Array(cellsNumber);
|
const confluence = new Uint8Array(cellsNumber);
|
||||||
const flux = new Uint16Array(cellsNumber);
|
const flux = new Uint16Array(cellsNumber);
|
||||||
|
|
||||||
|
|
@ -82,20 +82,20 @@ export function generateRivers(
|
||||||
flux[lakeCell] += Math.max(lake.flux - lake.evaporation, 0); // not evaporated lake water drains to outlet
|
flux[lakeCell] += Math.max(lake.flux - lake.evaporation, 0); // not evaporated lake water drains to outlet
|
||||||
|
|
||||||
// allow to chain lakes to keep river identity
|
// allow to chain lakes to keep river identity
|
||||||
if (riverIds[lakeCell] !== lake.river) {
|
if (tempRiverIds[lakeCell] !== lake.river) {
|
||||||
const sameRiver = cells.c[lakeCell].some(c => riverIds[c] === lake.river);
|
const sameRiver = cells.c[lakeCell].some(c => tempRiverIds[c] === lake.river);
|
||||||
|
|
||||||
if (lake.river && sameRiver) {
|
if (lake.river && sameRiver) {
|
||||||
riverIds[lakeCell] = lake.river;
|
tempRiverIds[lakeCell] = lake.river;
|
||||||
addCellToRiver(lakeCell, lake.river);
|
addCellToRiver(lakeCell, lake.river);
|
||||||
} else {
|
} else {
|
||||||
riverIds[lakeCell] = nextRiverId;
|
tempRiverIds[lakeCell] = nextRiverId;
|
||||||
addCellToRiver(lakeCell, nextRiverId);
|
addCellToRiver(lakeCell, nextRiverId);
|
||||||
nextRiverId++;
|
nextRiverId++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lake.outlet = riverIds[lakeCell];
|
lake.outlet = tempRiverIds[lakeCell];
|
||||||
flowDown(cellId, flux[lakeCell], lake.outlet);
|
flowDown(cellId, flux[lakeCell], lake.outlet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,7 +111,7 @@ export function generateRivers(
|
||||||
}
|
}
|
||||||
|
|
||||||
// near-border cell: pour water out of the screen
|
// near-border cell: pour water out of the screen
|
||||||
if (cells.b[cellId] && riverIds[cellId]) return addCellToRiver(-1, riverIds[cellId]);
|
if (cells.b[cellId] && tempRiverIds[cellId]) return addCellToRiver(-1, tempRiverIds[cellId]);
|
||||||
|
|
||||||
// downhill cell (make sure it's not in the source lake)
|
// downhill cell (make sure it's not in the source lake)
|
||||||
let min = null;
|
let min = null;
|
||||||
|
|
@ -136,20 +136,20 @@ export function generateRivers(
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a new river
|
// create a new river
|
||||||
if (!riverIds[cellId]) {
|
if (!tempRiverIds[cellId]) {
|
||||||
riverIds[cellId] = nextRiverId;
|
tempRiverIds[cellId] = nextRiverId;
|
||||||
addCellToRiver(cellId, nextRiverId);
|
addCellToRiver(cellId, nextRiverId);
|
||||||
nextRiverId++;
|
nextRiverId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
flowDown(min, flux[cellId], riverIds[cellId]);
|
flowDown(min, flux[cellId], tempRiverIds[cellId]);
|
||||||
});
|
});
|
||||||
|
|
||||||
return {flux, lakeData};
|
return {flux, lakeData};
|
||||||
|
|
||||||
function flowDown(toCell: number, fromFlux: number, riverId: number) {
|
function flowDown(toCell: number, fromFlux: number, riverId: number) {
|
||||||
const toFlux = flux[toCell] - confluence[toCell];
|
const toFlux = flux[toCell] - confluence[toCell];
|
||||||
const toRiver = riverIds[toCell];
|
const toRiver = tempRiverIds[toCell];
|
||||||
|
|
||||||
if (toRiver) {
|
if (toRiver) {
|
||||||
// downhill cell already has river assigned
|
// downhill cell already has river assigned
|
||||||
|
|
@ -159,7 +159,7 @@ export function generateRivers(
|
||||||
// min river is a tributary of current river
|
// min river is a tributary of current river
|
||||||
riverParents[toRiver] = riverId;
|
riverParents[toRiver] = riverId;
|
||||||
}
|
}
|
||||||
riverIds[toCell] = riverId; // re-assign river if downhill part has less flux
|
tempRiverIds[toCell] = riverId; // re-assign river if downhill part has less flux
|
||||||
} else {
|
} else {
|
||||||
confluence[toCell] += fromFlux; // mark confluence
|
confluence[toCell] += fromFlux; // mark confluence
|
||||||
if (currentCellHeights[toCell] >= MIN_LAND_HEIGHT) {
|
if (currentCellHeights[toCell] >= MIN_LAND_HEIGHT) {
|
||||||
|
|
@ -167,7 +167,7 @@ export function generateRivers(
|
||||||
riverParents[riverId] = toRiver;
|
riverParents[riverId] = toRiver;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else riverIds[toCell] = riverId; // assign the river to the downhill cell
|
} else tempRiverIds[toCell] = riverId; // assign the river to the downhill cell
|
||||||
|
|
||||||
if (currentCellHeights[toCell] < MIN_LAND_HEIGHT) {
|
if (currentCellHeights[toCell] < MIN_LAND_HEIGHT) {
|
||||||
// pour water to the water body
|
// pour water to the water body
|
||||||
|
|
@ -197,7 +197,7 @@ export function generateRivers(
|
||||||
}
|
}
|
||||||
|
|
||||||
function defineRivers() {
|
function defineRivers() {
|
||||||
const r = new Uint16Array(cellsNumber);
|
const riverIds = new Uint16Array(cellsNumber);
|
||||||
const conf = new Uint16Array(cellsNumber);
|
const conf = new Uint16Array(cellsNumber);
|
||||||
const rivers: Omit<IRiver, "name" | "basin" | "type">[] = [];
|
const rivers: Omit<IRiver, "name" | "basin" | "type">[] = [];
|
||||||
|
|
||||||
|
|
@ -213,8 +213,8 @@ export function generateRivers(
|
||||||
if (cell < 0 || cells.h[cell] < MIN_LAND_HEIGHT) continue;
|
if (cell < 0 || cells.h[cell] < MIN_LAND_HEIGHT) continue;
|
||||||
|
|
||||||
// mark confluences and assign river to cells
|
// mark confluences and assign river to cells
|
||||||
if (r[cell]) conf[cell] = 1;
|
if (riverIds[cell]) conf[cell] = 1;
|
||||||
else r[cell] = riverId;
|
else riverIds[cell] = riverId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const source = riverCells[0];
|
const source = riverCells[0];
|
||||||
|
|
@ -246,13 +246,13 @@ export function generateRivers(
|
||||||
if (!conf[i]) continue;
|
if (!conf[i]) continue;
|
||||||
|
|
||||||
const sortedInflux = cells.c[i]
|
const sortedInflux = cells.c[i]
|
||||||
.filter(c => r[c] && currentCellHeights[c] > currentCellHeights[i])
|
.filter(c => riverIds[c] && currentCellHeights[c] > currentCellHeights[i])
|
||||||
.map(c => flux[c])
|
.map(c => flux[c])
|
||||||
.sort((a, b) => b - a);
|
.sort((a, b) => b - a);
|
||||||
conf[i] = sortedInflux.reduce((acc, flux, index) => (index ? acc + flux : acc), 0);
|
conf[i] = sortedInflux.reduce((acc, flux, index) => (index ? acc + flux : acc), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {r, conf, rivers};
|
return {riverIds, conf, rivers};
|
||||||
}
|
}
|
||||||
|
|
||||||
function downcutRivers(heights: Float32Array) {
|
function downcutRivers(heights: Float32Array) {
|
||||||
|
|
|
||||||
2
src/types/pack/pack.d.ts
vendored
2
src/types/pack/pack.d.ts
vendored
|
|
@ -20,7 +20,7 @@ interface IPackCells {
|
||||||
fl: Uint16Array; // flux volume, defined by drainWater() in river-generator.ts
|
fl: Uint16Array; // flux volume, defined by drainWater() in river-generator.ts
|
||||||
r: Uint16Array; // river id, defined by defineRivers() in river-generator.ts
|
r: Uint16Array; // river id, defined by defineRivers() in river-generator.ts
|
||||||
conf: Uint16Array; // conluence, defined by defineRivers() in river-generator.ts
|
conf: Uint16Array; // conluence, defined by defineRivers() in river-generator.ts
|
||||||
biome: UintArray;
|
biome: Uint8Array;
|
||||||
area: UintArray;
|
area: UintArray;
|
||||||
state: UintArray;
|
state: UintArray;
|
||||||
culture: UintArray;
|
culture: UintArray;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue