chinko
This commit is contained in:
parent
0359bb2445
commit
b17be0e0d2
21 changed files with 8034 additions and 2737 deletions
|
|
@ -213,7 +213,7 @@ const TERRAIN_TYPES = [
|
|||
coastStyle: "inland_sea",
|
||||
mountainMode: "mixed",
|
||||
massifnessRange: [0.34, 0.62],
|
||||
seaRatioRange: [0.20, 0.33],
|
||||
seaRatioRange: [0.28, 0.43],
|
||||
twoSidedChance: 0.92,
|
||||
mountainOffsetRange: [0.22, 0.34],
|
||||
baseHeightRange: [0.46, 0.78],
|
||||
|
|
@ -226,7 +226,7 @@ const TERRAIN_TYPES = [
|
|||
lengthScale: 1.00,
|
||||
widthScale: 1.18,
|
||||
heightScale: 0.82,
|
||||
coastStrength: 1.10,
|
||||
coastStrength: 1.34,
|
||||
plainBiasRange: [0.26, 0.50],
|
||||
riverRichnessRange: [0.58, 0.96],
|
||||
bigRiverChanceRange: [0.18, 0.42],
|
||||
|
|
@ -580,10 +580,12 @@ function computeCoastLower(px, py, template, seed) {
|
|||
let pressure = 0;
|
||||
|
||||
if (template.coastStyle === "inland_sea") {
|
||||
// 瀬戸内型だけは中央を横切る浅い内海を許す。出現率は地形タイプ側で管理する。
|
||||
const sideA = smoothstep((-axis + 0.25 + wave + bay) / 0.26);
|
||||
const sideB = smoothstep((axis + 0.23 - wave + bay * 0.7) / 0.27);
|
||||
const channel = smoothstep((0.060 - Math.abs(cross + wave * 0.65 + islandNoise)) / 0.090) * 0.82;
|
||||
// 瀬戸内型は旧来の大きな内海+両岸海岸線に戻す。
|
||||
// 海面比率はテンプレート側で高めに保ち、微細な島ノイズではなく
|
||||
// 連続した水道形状で海を増やす。
|
||||
const sideA = smoothstep((-axis + 0.28 + wave + bay) / 0.28);
|
||||
const sideB = smoothstep((axis + 0.26 - wave + bay * 0.7) / 0.29);
|
||||
const channel = smoothstep((0.082 - Math.abs(cross + wave * 0.68 + islandNoise * 0.55)) / 0.112) * 0.98;
|
||||
pressure = Math.max(sideA, sideB, channel);
|
||||
} else if (template.coastStyle === "parallel_spine") {
|
||||
// 東北型: 左右端または上下端に海を置く。海岸線は脊梁山脈とおおよそ平行。
|
||||
|
|
@ -738,6 +740,81 @@ function computeFlowAccumulation(sea, flowTo, filled, flowAccum) {
|
|||
return area;
|
||||
}
|
||||
|
||||
function buildWatershedId(sea, flowTo, flowAccum) {
|
||||
const outletKey = new Int32Array(SIZE);
|
||||
outletKey.fill(-1);
|
||||
const ids = new Int32Array(SIZE);
|
||||
ids.fill(-1);
|
||||
const outletToId = new Map();
|
||||
const quant = 12;
|
||||
|
||||
function keyForOutlet(i) {
|
||||
if (i < 0) return -1;
|
||||
const x = i % MAP_W;
|
||||
const y = Math.floor(i / MAP_W);
|
||||
const qx = Math.floor(x / quant);
|
||||
const qy = Math.floor(y / quant);
|
||||
return qy * 1000 + qx;
|
||||
}
|
||||
|
||||
function resolve(start) {
|
||||
if (start < 0 || sea[start]) return -1;
|
||||
if (outletKey[start] >= 0) return outletKey[start];
|
||||
const chain = [];
|
||||
const seen = new Set();
|
||||
let i = start;
|
||||
let key = -1;
|
||||
for (let guard = 0; guard < SIZE && i >= 0; guard++) {
|
||||
if (sea[i]) { key = keyForOutlet(i); break; }
|
||||
if (outletKey[i] >= 0) { key = outletKey[i]; break; }
|
||||
if (seen.has(i)) { key = keyForOutlet(i); break; }
|
||||
seen.add(i);
|
||||
chain.push(i);
|
||||
const to = flowTo[i];
|
||||
if (to < 0 || to === i) { key = keyForOutlet(i); break; }
|
||||
// Major channels should be a watershed's spine, not a sequence of tiny
|
||||
// drainage labels. Continue to the coast/outlet even after hitting them.
|
||||
i = to;
|
||||
}
|
||||
if (key < 0 && chain.length) key = keyForOutlet(chain[chain.length - 1]);
|
||||
for (const ci of chain) outletKey[ci] = key;
|
||||
return key;
|
||||
}
|
||||
|
||||
for (let i = 0; i < SIZE; i++) {
|
||||
if (sea[i]) continue;
|
||||
const key = resolve(i);
|
||||
if (key < 0) continue;
|
||||
if (!outletToId.has(key)) outletToId.set(key, outletToId.size);
|
||||
ids[i] = outletToId.get(key);
|
||||
}
|
||||
|
||||
// Very small coastal outlet labels create noisy slivers. Merge them into the
|
||||
// strongest neighbouring watershed so natural compartments remain basin-scale.
|
||||
const counts = new Int32Array(outletToId.size || 1);
|
||||
for (let i = 0; i < SIZE; i++) if (ids[i] >= 0) counts[ids[i]]++;
|
||||
const minArea = 18;
|
||||
for (let pass = 0; pass < 2; pass++) {
|
||||
for (let y = 0; y < MAP_H; y++) {
|
||||
for (let x = 0; x < MAP_W; x++) {
|
||||
const i = indexOf(x, y);
|
||||
const id = ids[i];
|
||||
if (id < 0 || counts[id] >= minArea) continue;
|
||||
const choices = new Map();
|
||||
for (const [nx, ny] of [[x + 1, y], [x - 1, y], [x, y + 1], [x, y - 1]]) {
|
||||
if (!inside(nx, ny)) continue;
|
||||
const nid = ids[indexOf(nx, ny)];
|
||||
if (nid >= 0 && nid !== id) choices.set(nid, (choices.get(nid) || 0) + 1 + (flowAccum[indexOf(nx, ny)] || 0));
|
||||
}
|
||||
let best = -1, bestScore = -1;
|
||||
for (const [nid, score] of choices) if (score > bestScore) { bestScore = score; best = nid; }
|
||||
if (best >= 0) { counts[id]--; counts[best]++; ids[i] = best; }
|
||||
}
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
function traceFlowPath(start, sea, flowTo, maxSteps = 900) {
|
||||
const path = [];
|
||||
const seen = new Set();
|
||||
|
|
@ -1129,6 +1206,9 @@ export function generateTerrainAndRivers(seed) {
|
|||
const lowHillNoise = clamp((fbm(x * 0.95 + 17, y * 0.95 - 23, seed + 571) - 0.38) * 2.9);
|
||||
const lowHillMask = clamp(0.34 + mountainMaskMax * 0.72 + lowHillNoise * 0.50 - coastPressure * 0.12);
|
||||
e += lowHillMask * 0.145;
|
||||
// Do not add the previous fine speckle uplift here: it created too many
|
||||
// tiny islets. Sea amount is controlled by seaRatio/coast pressure.
|
||||
e -= clamp((coastPressure - 0.42) * 1.35) * 0.026;
|
||||
const high = Math.max(0, e - 0.62);
|
||||
e -= high * 0.42;
|
||||
}
|
||||
|
|
@ -1150,6 +1230,7 @@ export function generateTerrainAndRivers(seed) {
|
|||
const filled = new Float32Array(SIZE);
|
||||
priorityFloodFlow(elevation, sea, flowTo, filled);
|
||||
computeFlowAccumulation(sea, flowTo, filled, flowAccum);
|
||||
const watershedId = buildWatershedId(sea, flowTo, flowAccum);
|
||||
const { riverPaths, mainRivers, tributaryRivers, smallStreams } = buildRiverNetwork(seed, terrainTemplate, sea, lake, elevation, slope, flowTo, flowAccum, river, erosionField);
|
||||
enforceLandGradient(elevation, sea, seaLevel);
|
||||
deriveFields(seed, terrainTemplate, fields, seaLevel);
|
||||
|
|
@ -1162,7 +1243,7 @@ export function generateTerrainAndRivers(seed) {
|
|||
const natural = buildNaturalCompartments(
|
||||
landMask, sea, elevation, slope, river, ridgeField, valleyField, basinField, coastalLowland, flowAccum,
|
||||
null, plain, agriculture, zeroDensity, zeroLanduse,
|
||||
{ seed: seed + 17003, targetCompartmentCount: clamp(Math.round((SIZE - sea.reduce((sum, value) => sum + value, 0)) / 45), 70, 360) }
|
||||
{ seed: seed + 17003, watershedId, targetCompartmentCount: clamp(Math.round((SIZE - sea.reduce((sum, value) => sum + value, 0)) / 45), 70, 360) }
|
||||
);
|
||||
const sharedNaturalBarrierScore = natural.naturalBarrierScore || naturalBarrierScore;
|
||||
const prefectureBorder = extractMaskBorder(prefectureMask, sea);
|
||||
|
|
@ -1221,6 +1302,7 @@ export function generateTerrainAndRivers(seed) {
|
|||
basinField,
|
||||
coastalLowland,
|
||||
flowAccum,
|
||||
watershedId,
|
||||
erosionField,
|
||||
depositionField,
|
||||
arcSpineField,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue