not good but not bad

This commit is contained in:
33333-33333 2026-05-26 15:32:27 +09:00
commit 4f0df3f6c5
11 changed files with 1284 additions and 622 deletions

View file

@ -1,11 +1,10 @@
import { INF, MAP_H, MAP_W, SIZE, MinHeap, clamp, createMapFields, fbm, hash2, indexOf, inside, lerp, pickEntities, rand, smoothstep, valueNoise, xyOf } from "./mapUtils.js";
import {
extractMaskBorder,
extractRegionBorderSegments,
generateRegionalPrefectures,
makePrefectureMask,
neighbors8,
} from "./mapGeneratorHelpers.js";
import { buildNaturalCompartments } from "./adminRegions.js";
const ASPECT = MAP_W / MAP_H;
const SQRT2 = Math.SQRT2;
@ -128,10 +127,10 @@ function ridgeContribution(px, py, ridge, seed) {
const along = Math.abs(u / Math.max(0.001, half));
if (along >= 1.22) return 0;
const taper = smoothstep(1 - clamp((along - 0.68) / 0.54));
const wobble = (valueNoise((u + ridge.phase) * 720, (py + ridge.phase) * 720, seed + ridge.seedOffset, 14) - 0.5) * ridge.width * ridge.wobble;
const wobble = (valueNoise((u + ridge.phase) * 720, (v + ridge.phase * 0.37) * 980, seed + ridge.seedOffset, 14) - 0.5) * ridge.width * ridge.wobble;
const cross = Math.abs(v + wobble);
const core = Math.exp(-Math.pow(cross / Math.max(0.0008, ridge.width), 2.0));
const serration = 0.82 + 0.36 * valueNoise((px + ridge.phase) * 900, (py - ridge.phase) * 900, seed + ridge.seedOffset + 71, 7.5);
const serration = 0.82 + 0.36 * valueNoise((u + ridge.phase) * 900, (v - ridge.phase * 0.41) * 1200, seed + ridge.seedOffset + 71, 7.5);
return ridge.height * core * taper * serration;
}
@ -169,13 +168,13 @@ const TERRAIN_TYPES = [
mountainOffsetRange: [0.47, 0.53],
baseHeightRange: [0.74, 1.10],
primaryLengthRange: [0.76, 0.96],
primaryWidthRange: [0.17, 0.30],
primaryWidthRange: [0.13, 0.22],
systemCountRange: [12, 16],
beltCountRange: [3, 4],
angleSpread: 0.14,
crossSpread: 0.54,
crossSpread: 0.38,
lengthScale: 1.22,
widthScale: 1.16,
widthScale: 0.92,
heightScale: 1.24,
coastStrength: 0.90,
plainBiasRange: [0.16, 0.34],
@ -445,7 +444,7 @@ function buildMountainSystems(template, seed) {
y: clamp(0.50 + Math.sin(centralAngle) * centralAlong + Math.sin(centralAngle + Math.PI / 2) * centralCross, 0.12, 0.88),
angle: centralAngle,
length: (0.78 + rand(seed, 3333) * 0.18) * lengthScale,
width: (0.17 + rand(seed, 3334) * 0.11) * widthScale,
width: (0.12 + rand(seed, 3334) * 0.07) * widthScale,
height: template.mountainBaseHeight * heightScale * (0.58 + rand(seed, 3335) * 0.18),
scratchCount: Math.round(20 + rand(seed, 3336) * 10),
massifness: clamp((template.mountainMassifness ?? 0.16) * 0.55),
@ -462,7 +461,7 @@ function buildMountainSystems(template, seed) {
y: clamp(0.50 + Math.sin(centralAngle) * (centralAlong + side * 0.18) + Math.sin(centralAngle + Math.PI / 2) * (centralCross + side * 0.028), 0.10, 0.90),
angle: centralAngle + (rand(seed, 3341) - 0.5) * 0.08,
length: (0.48 + rand(seed, 3342) * 0.20) * lengthScale,
width: (0.11 + rand(seed, 3343) * 0.08) * widthScale,
width: (0.075 + rand(seed, 3343) * 0.055) * widthScale,
height: template.mountainBaseHeight * heightScale * (0.38 + rand(seed, 3344) * 0.16),
scratchCount: Math.round(12 + rand(seed, 3345) * 8),
massifness: clamp((template.mountainMassifness ?? 0.16) * 0.65),
@ -970,9 +969,24 @@ export function generateTerrainAndRivers(seed) {
branchRidgeField[i] = clamp(branchRidgeField[i] + r * 5.0);
arcSpineField[i] = clamp(Math.max(arcSpineField[i], r * 4.6));
}
const macro = (fbm(x * terrainTemplate.macroNoiseScale * 48, y * terrainTemplate.macroNoiseScale * 48, seed + 500) - 0.5) * 2;
let macro;
let scratch;
if (terrainTemplate.terrainType === "tohoku_spine") {
const dx = (px - 0.5) * ASPECT;
const dy = py - 0.5;
const { u, v } = rotate(dx, dy, terrainTemplate.mountainAngle);
const warp = (valueNoise(x * 0.50, y * 0.50, seed + 504, 28) - 0.5) * 18;
macro = (fbm(u * 520 + warp, v * 1850 - warp * 0.35, seed + 500) - 0.5) * 2;
scratch = (fbm(u * 920 + warp * 0.8, v * 2300 + warp * 0.25, seed + 502) - 0.5) * 2;
const passBreak = clamp((valueNoise(u * 760 + 17, v * 1800 - 11, seed + 505, 18) - 0.62) * 2.6);
const lateralBranch = clamp((valueNoise(v * 1700 - 3, u * 540 + 5, seed + 506, 16) - 0.54) * 2.1);
e += lateralBranch * mountainMaskMax * 0.022;
e -= passBreak * mountainMaskMax * 0.052;
} else {
macro = (fbm(x * terrainTemplate.macroNoiseScale * 48, y * terrainTemplate.macroNoiseScale * 48, seed + 500) - 0.5) * 2;
scratch = (fbm(x * 2.2, y * 2.2, seed + 502) - 0.5) * 2;
}
const global = (valueNoise(x * 0.23, y * 0.23, seed + 501, 38) - 0.5) * 2;
const scratch = (fbm(x * 2.2, y * 2.2, seed + 502) - 0.5) * 2;
e += macro * terrainTemplate.macroNoiseStrength * (0.38 + mountainMaskMax * 0.80);
e += global * 0.020;
e += scratch * terrainTemplate.scratchNoiseStrength * mountainMaskMax;
@ -997,11 +1011,16 @@ export function generateTerrainAndRivers(seed) {
deriveFields(seed, terrainTemplate, fields, seaLevel);
const prefectureMask = makePrefectureMask(seed, sea, elevation, slope, river);
const regional = generateRegionalPrefectures(seed, sea, elevation, slope, river, ridgeField, flowAccum, prefectureMask);
const prefectureRegionId = regional.regionId;
const adminPrefectureRegionId = regional.displayRegionId || regional.regionId;
const regionalDebug = regional.debug;
const regionalPrefectureBorders = extractRegionBorderSegments(adminPrefectureRegionId, sea);
const landMask = new Uint8Array(SIZE);
for (let i = 0; i < SIZE; i++) landMask[i] = sea[i] ? 0 : 1;
const zeroDensity = new Float32Array(SIZE);
const zeroLanduse = new Int8Array(SIZE);
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)) / 30), 80, 520) }
);
const sharedNaturalBarrierScore = natural.naturalBarrierScore || naturalBarrierScore;
const prefectureBorder = extractMaskBorder(prefectureMask, sea);
let landCount = 0;
@ -1065,17 +1084,16 @@ export function generateTerrainAndRivers(seed) {
depositionalLowland,
alluvialFanField,
deltaField,
naturalBarrierScore,
naturalBarrierScore: sharedNaturalBarrierScore,
portSuitability,
crossingSuitability,
passSuitability,
prefectureMask,
landMask,
prefectureBorder,
prefectureRegionId,
adminPrefectureRegionId,
regionalDebug,
naturalCompartmentId: natural.compartmentId,
naturalCompartments: natural.compartments,
terrainDebug,
regionalPrefectureBorders,
riverPaths,
mainRivers,
tributaryRivers,