Skip to content

Commit

Permalink
water is separate 3d geometry
Browse files Browse the repository at this point in the history
  • Loading branch information
jmdejong committed Mar 19, 2024
1 parent 8a6ed8a commit bd8489a
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 52 deletions.
55 changes: 40 additions & 15 deletions elaborate.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,19 @@ class Node {
}

isWaterBody() {
return this.isSea() || this.waterHeight > 0;
return this.isSea() || this.waterHeight > this.baseHeight+1e-8;
}

isSink() {
return this.sink;
}

height() {
return this.baseHeight + this.waterHeight;
return Math.max(this.baseHeight, this.waterHeight);
}

waterVolume() {
return Math.max(this.waterHeight - this.baseHeight, 0);
}

changeGround(ground) {
Expand Down Expand Up @@ -206,7 +210,7 @@ class World {
for (let node of this.graph.sinks()) {
fringe.put(node);
visited.add(node.id.hash());
node.waterHeight = Math.max(0, -node.baseHeight);
node.waterHeight = 0;
}
while (!fringe.isEmpty()) {
let node = fringe.take();
Expand All @@ -215,32 +219,32 @@ class World {
for (let neighbour of node.neighbours) {
if (!visited.has(neighbour.id.hash())) {
let dh = neighbour.baseHeight - node.baseHeight;
if (dh > 0 && !neighbour.waterHeight) {
if (dh > 0 && !neighbour.isWaterBody()) {
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;
if (!eroded && eroded !== 0) {
console.log("erosion error", erosion, water, baseErosion, momentumErosion, neighbour.momentum, neighbour.size);
}
neighbour.changeGround(-eroded);
this.sediment.created += eroded;
neighbour.sediment += eroded;
}
neighbour.waterHeight = 0;
neighbour.waterHeight = -1e9;
neighbour.sea = false;
neighbour.drains = [];
if (neighbour.height() < node.height()) {
neighbour.waterHeight = node.height() + 1e-7 * (2+randf(neighbour.id, this.graph.seed ^ 4890));
if (node.isSea()) {
neighbour.waterHeight = node.height() - neighbour.baseHeight + 1e-7 * (2+randf(neighbour.id, this.graph.seed ^ 4890));
// neighbour.waterHeight = node.waterHeight + 1e-7 * (2+randf(neighbour.id, this.graph.seed ^ 4890));
neighbour.sea = true;
} else {
let l = clamp(noise.GetNoise(neighbour.pos.x, neighbour.pos.y) + lakeAmount * 2 - 1, -1, 1);
if (l > 0) {
neighbour.waterHeight = l * (node.height() - neighbour.height()) * lakeDepth;
let totalHeight = node.height() + 1e-6 * randf(neighbour.id, this.graph.seed ^ 8429);
neighbour.baseHeight = totalHeight - neighbour.waterHeight;
} else {
neighbour.baseHeight = node.height() + randf(neighbour.id, this.graph.seed ^ 3627) * plainsSlope / (node.height() - neighbour.height() + 1);
}
let l = clamp(noise.GetNoise(neighbour.pos.x, neighbour.pos.y) + lakeAmount * 2 - 1, 0, 1) * lakeDepth;
neighbour.baseHeight = neighbour.baseHeight * l + neighbour.waterHeight * (1-l);
}
} else {
neighbour.waterHeight = node.waterHeight + 1e-7 * (2+randf(neighbour.id, this.graph.seed ^ 8429));
}
fringe.put(neighbour);
visited.add(neighbour.id.hash());
Expand Down Expand Up @@ -271,6 +275,9 @@ class World {
let outflow = [];
for (let drain of drains) {
let dh = nh - drain.height();
if (!(dh >= 0)) {
console.log("drain order error", dh, nh, dh);
}
node.momentum += dh;
let o = node.isWaterBody() ? 1 : dh**cohesion;
outflow.push([drain, o]);
Expand Down Expand Up @@ -298,8 +305,11 @@ class World {
continue;
}
let drains = this.graph.drains(node);
let deposited = clamp((amount +node.waterHeight * depthFactor) * node.sediment / (node.water+amount) / (node.momentum+1), 0, node.sediment);
let deposited = clamp((amount + node.waterVolume()* depthFactor) * node.sediment / (node.water+amount) / (node.momentum+1), 0, node.sediment);
node.sediment -= deposited;
if (!deposited && deposited !== 0) {
console.log("deposit error", deposited);
}
node.changeGround(deposited);
this.sediment.deposited += deposited;
for (let [drain, o] of node.outflow) {
Expand All @@ -308,6 +318,20 @@ class World {
node.sediment = 0;
}
}

amplifyWater() {
for (let node of this.graph.all()) {
if (node.isWaterBody()) {
continue;
}
let wetNeighbours = node.neighbours.filter(n => n.isWaterBody());
if (wetNeighbours.length) {
node.waterHeight = Math.min(node.height(), wetNeighbours.reduce((acc, n) => acc + n.waterHeight, 0) / wetNeighbours.length)
} else {
node.waterHeight = -1e6;
}
}
}
}


Expand Down Expand Up @@ -341,6 +365,7 @@ async function generate(settings, view) {
sorted = await view.time("flow", () => world.land(settings.plainsSlope, settings.lakeAmount, settings.lakeSize, 1, settings.baseErosion * erosionWeight, settings.momentumErosion * erosionWeight));
await view.time("drain", () => world.drain(sorted, settings.rainfall, settings.cohesion, Math.pow(settings.slowing, settings.nodeSize)));
}
await view.time("amplify water", () => world.amplifyWater());
await view.time("draw", () => view.drawWorldGraph(graph, 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}`);
let endTime = Date.now();
Expand Down
5 changes: 5 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,11 @@
boosted movement speed
<input type="number" name="boostSpeed" value="100" step="0.1" />
</label>
</label>
field of view
<input type="number" name="fov" value="75" step="0.1" />
</label>

<br />

<input type="submit" value="Render 3D" />
Expand Down
4 changes: 4 additions & 0 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class CanvasView {
} else if (n.isWaterBody()) {
return [76, 76, 255];
} else {
let v = n.height();
if (v != v) {
console.log(n);
}
return settings.colorScale.rgbBytes(n.height() / settings.colorMax);
}
});
Expand Down
79 changes: 42 additions & 37 deletions view3d.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class ThreeView {
this.scene.add(directionalLight);
const ambientLight = new THREE.AmbientLight(0x88888888);
this.scene.add(ambientLight);
this.currentGround = null;
this.terrain = null;
this.visible = false;
}

Expand All @@ -88,46 +88,44 @@ class ThreeView {
console.log("drawing graph 3d");
let settings = readSettings(document.getElementById("draw3dsettings"));
console.log(settings);
this.camera.fov = settings.fov
this.camera.updateProjectionMatrix();
this.movementSpeed = settings.movementSpeed;
this.boostSpeed = settings.boostSpeed;

let terrain = new THREE.Group()

let vertices = [];
let waterVertices = []
let colors = []
let indices = [];
if (settings.flatFaces) {
for (let triangle of graph.triangles()) {
for (let node of triangle) {
vertices.push(node.pos.x * settings.horizontalScale, node.height() * settings.heightScale, node.pos.y * settings.horizontalScale);
if (node.isSea()) {
colors.push(0, 0, 0.9);
} else if (node.isWaterBody()) {
colors.push(0.2, 0.2, 1);
} else {
colors.push(...settings.colorScale.rgbFloats(node.height() / settings.colorMax));
}
}
}
} else {
let nodeIndex = new Map();
let i = 0;
for (let node of graph.all()) {
vertices.push(node.pos.x * settings.horizontalScale, node.height() * settings.heightScale, node.pos.y * settings.horizontalScale);
if (node.isSea()) {
colors.push(0, 0, 0.9);
} else if (node.isWaterBody()) {
colors.push(0.2, 0.2, 1);
} else {
colors.push(...settings.colorScale.rgbFloats(node.height() / settings.colorMax));
}
nodeIndex.set(node.id.hash(), i++);
}
let nodes = settings.flatFaces ? [...graph.triangles()].flatMap(triangle => triangle) : graph.all();
let nodeIndex = new Map();
let i = 0;
for (let node of nodes) {
vertices.push(node.pos.x * settings.horizontalScale, node.baseHeight * settings.heightScale, node.pos.y * settings.horizontalScale);
waterVertices.push(node.pos.x * settings.horizontalScale, node.waterHeight * settings.heightScale, node.pos.y * settings.horizontalScale);
colors.push(...settings.colorScale.rgbFloats(node.baseHeight / settings.colorMax));
nodeIndex.set(node.id.hash(), i++);
}

for (let triangle of graph.triangles()) {
for (let node of triangle) {
indices.push(nodeIndex.get(node.id.hash()));
}
for (let triangle of graph.triangles()) {
for (let node of triangle) {
indices.push(nodeIndex.get(node.id.hash()));
}
}

const waterGeometry = new THREE.BufferGeometry();
waterGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(waterVertices), 3));
if (!settings.flatFaces) {
waterGeometry.setIndex(indices);
}
waterGeometry.computeBoundingBox();
waterGeometry.computeVertexNormals()
let water = new THREE.Mesh(waterGeometry, new THREE.MeshStandardMaterial({color: 0x0000ff}));
water.position.y -= 1e-6;
terrain.add(water);

const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(vertices), 3));
geometry.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colors), 3));
Expand All @@ -138,13 +136,20 @@ class ThreeView {
geometry.computeVertexNormals()

let ground = new THREE.Mesh(geometry, new THREE.MeshStandardMaterial({vertexColors: true}));
terrain.add(ground);


if (settings.centerMesh) {
ground.position.x -= graph.size.x * settings.horizontalScale / 2;
ground.position.z -= graph.size.y * settings.horizontalScale / 2;
terrain.position.x -= graph.size.x * settings.horizontalScale / 2;
terrain.position.z -= graph.size.y * settings.horizontalScale / 2;
}
this.scene.remove(this.currentGround);
this.scene.add(ground);
this.currentGround = ground;
if (this.terrain) {
this.scene.remove(this.terrain);
}
this.terrain = terrain;
this.scene.add(this.terrain);

this.renderer.setSize( window.innerWidth, window.innerHeight );

console.log("graph 3d drawn");
}
Expand Down

0 comments on commit bd8489a

Please sign in to comment.