Skip to content

Commit

Permalink
combined 'land' and 'erode' steps
Browse files Browse the repository at this point in the history
  • Loading branch information
jmdejong committed Oct 15, 2023
1 parent 0341e23 commit b828103
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 39 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

Procedural map generation experiment.

Time complexity: `O(n*log(n)*i)` where `n` is the number of nodes (approximately `(size / nodeSize) ^2`) and i is the number of erosion iterations.
Time complexity: `O(n*log(n)*i)` where `n` is the number of nodes (approximately `(size / nodeSize) ^2`) and `i` is the number of erosion iterations.

# Screenshots

Expand Down
8 changes: 2 additions & 6 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
</label>
<label>
detail amplitude
<input type="number" name="detailAmplitude" value="0.05" step="0.01"/>
<input type="number" name="detailAmplitude" value="0.02" step="0.01"/>
</label>
<label>
detail factor over iterations
Expand All @@ -125,7 +125,7 @@
</label>
<label>
momentum shrink
<input type="number" name="slowing" value="0.5" step="0.01"/>
<input type="number" name="slowing" value="0.9" step="0.001"/>
</label>
<br />

Expand Down Expand Up @@ -160,10 +160,6 @@
iterations
<input type="number" name="iterations" value="2" min="0" />
</label>
<label>
skip final depose step
<input type="checkbox" name="skipFinalDepose" />
</label>
<br />

<label>
Expand Down
66 changes: 34 additions & 32 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ class Node {
this.size = size;
this.baseHeight = 0;
this.sink = false;
this.reset();
this.sea = false;
this.waterHeight = 0;
this.drains = [];
this.water = 0;
this.outflow = [];
this.momentum = 0;
this.sediment = 0;
}

isSea() {
Expand All @@ -31,17 +37,6 @@ class Node {
changeGround(ground) {
this.baseHeight += ground;
}

reset() {
this.water = 0;
this.waterHeight = 0;
this.sediment = 0;
this.drain = null;
this.drains = [];
this.outflow = [];
this.sea = false;
this.momentum = 0;
}
}


Expand Down Expand Up @@ -119,12 +114,6 @@ class NodeGraph {
return this.nodes.values();
}

reset() {
for (let node of this.all()) {
node.reset();
}
}

nearest(pos) {
let vy = pos.y / this.ns / TRIHEIGHT;
let vx = pos.x / this.ns - vy / 2;
Expand Down Expand Up @@ -255,7 +244,7 @@ class World {
}
}

land(plainsSlope, lakeAmount, lakeSize, lakeDepth) {
land(plainsSlope, lakeAmount, lakeSize, lakeDepth, baseErosion, momentumErosion) {
let noise = new FastNoiseLite(hash(this.graph.seed^23790));
noise.SetNoiseType(FastNoiseLite.NoiseType.OpenSimplex2);
noise.SetFractalType(FastNoiseLite.FractalType.FBm);
Expand All @@ -276,6 +265,19 @@ class World {
processed.add(node.id.hash());
for (let neighbour of node.neighbours) {
if (!visited.has(neighbour.id.hash())) {
let dh = neighbour.baseHeight - node.baseHeight;
if (dh > 0 && !neighbour.waterHeight) {
let water = node.isSink() ? 1 : node.water;
let erosion = Math.sqrt(water) * (baseErosion + momentumErosion*neighbour.momentum) / neighbour.size;
let newHeight = clamp(node.baseHeight + dh / (1+erosion), node.baseHeight, neighbour.baseHeight);
let eroded = neighbour.baseHeight - newHeight;
neighbour.changeGround(-eroded);
this.sediment.created += eroded;
neighbour.sediment += eroded;
}
neighbour.waterHeight = 0;
neighbour.sea = false;
neighbour.drains = [];
if (neighbour.height() < node.height()) {
if (node.isSea()) {
neighbour.waterHeight = node.height() - neighbour.baseHeight + 1e-7 * (2+randf(neighbour.id, this.graph.seed ^ 4890));
Expand All @@ -291,7 +293,6 @@ class World {
}
}
}
neighbour.drain = node.id;
fringe.put(neighbour);
visited.add(neighbour.id.hash());
}
Expand All @@ -304,6 +305,11 @@ class World {
}

drain(nodes, rainfall, cohesion, slowing) {
for (let node of nodes) {
node.water = 0
node.outflow = [];
node.momentum = 0;
}
for (let i=nodes.length; i--;) {
let node = nodes[i];
if (node.isSink()) {
Expand Down Expand Up @@ -359,18 +365,18 @@ class World {
let node = nodes[i];
if (node.isSink()) {
this.sediment.lost += node.sediment;
node.sediment = 0;
continue;
}
let drains = this.graph.drains(node);
let deposited = clamp(amount * node.sediment / (node.water+amount) / (node.momentum+1) * (node.waterHeight + depthFactor), 0, node.sediment);
let deposited = clamp((amount +node.waterHeight * depthFactor) * node.sediment / (node.water+amount) / (node.momentum+1), 0, node.sediment);
node.sediment -= deposited;
node.changeGround(deposited);

this.sediment.deposited += deposited;

for (let [drain, o] of node.outflow) {
drain.sediment += node.sediment * o;
}
node.sediment = 0;
}
}
}
Expand All @@ -391,8 +397,8 @@ async function generate(settings) {
let world = new World(graph);
await time("heighten", () => world.heighten(settings.seed^61882, settings.amplitude, settings.featureSize, settings.baseHeight, settings.warpSize, settings.warpEffect));
await time("cut edge", () => world.cutEdge(settings.edgeHeight, size * 0.005 * settings.edgePercentage, settings.edgeMode == "add", settings.edgeShape == "parabole"));
let sorted = await time("flow", () => world.land(settings.plainsSlope, settings.lakeAmount, settings.lakeSize, settings.lakeDepth));
await time("drain", () => world.drain(sorted, settings.rainfall, settings.cohesion, settings.slowing));
let sorted = await time("flow", () => world.land(settings.plainsSlope, settings.lakeAmount, settings.lakeSize, settings.lakeDepth, 0, 0));
await time("drain", () => world.drain(sorted, settings.rainfall, settings.cohesion, Math.pow(settings.slowing, settings.nodeSize)));
if (settings.drawPartial) {
await time("draw partial", () => graph.draw("partial", settings));
} else {
Expand All @@ -404,16 +410,12 @@ async function generate(settings) {
}
let detailFactor = 1;
for (let i=0; i<settings.iterations; ++i) {
await time("depose", () => world.depose(sorted, settings.deposition, settings.depositionDepthFactor));
await time("detail", () => world.heighten(settings.seed^(9009*i), settings.detailAmplitude*detailFactor, settings.detailSize*detailFactor, 0, 1, 0));
detailFactor *= settings.detailStep;
let erosionWeight = Math.pow(settings.erosionStep, i) * erosionScale;
await time("erode", () => world.erode(sorted, settings.baseErosion * erosionWeight, settings.momentumErosion * erosionWeight));
if (!(settings.skipFinalDepose &&i === settings.iterations - 1)) {
await time("depose", () => world.depose(sorted, settings.deposition, settings.depositionDepthFactor));
}
graph.reset();
sorted = await time("flow", () => world.land(settings.plainsSlope, settings.lakeAmount, settings.lakeSize, 1));
await time("drain", () => world.drain(sorted, settings.rainfall, settings.cohesion, settings.slowing));
sorted = await time("flow", () => world.land(settings.plainsSlope, settings.lakeAmount, settings.lakeSize, 1, settings.baseErosion * erosionWeight, settings.momentumErosion * erosionWeight));
await time("drain", () => world.drain(sorted, settings.rainfall, settings.cohesion, Math.pow(settings.slowing, settings.nodeSize)));
}
await time("draw", () => graph.draw(null, settings));
console.log(`sediment created: ${world.sediment.created / 1e6}, sediment deposited: ${world.sediment.deposited/1e6}, sediment lost: ${world.sediment.lost/1e6}, balance: ${(world.sediment.created - world.sediment.deposited - world.sediment.lost)/1e6}`);
Expand Down

0 comments on commit b828103

Please sign in to comment.