diff --git a/elaborate.js b/elaborate.js index 7e14f7b..37e5767 100644 --- a/elaborate.js +++ b/elaborate.js @@ -24,7 +24,7 @@ class Node { } isWaterBody() { - return this.isSea() || this.waterHeight > 0; + return this.isSea() || this.waterHeight > this.baseHeight+1e-8; } isSink() { @@ -32,7 +32,11 @@ class Node { } height() { - return this.baseHeight + this.waterHeight; + return Math.max(this.baseHeight, this.waterHeight); + } + + waterVolume() { + return Math.max(this.waterHeight - this.baseHeight, 0); } changeGround(ground) { @@ -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(); @@ -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()); @@ -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]); @@ -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) { @@ -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; + } + } + } } @@ -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(); diff --git a/index.html b/index.html index 9d84e0a..56e3fb3 100644 --- a/index.html +++ b/index.html @@ -269,6 +269,11 @@ boosted movement speed + + field of view + + +
diff --git a/main.js b/main.js index f71395c..dcccc0d 100644 --- a/main.js +++ b/main.js @@ -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); } }); diff --git a/view3d.js b/view3d.js index 6031bf3..691e60e 100644 --- a/view3d.js +++ b/view3d.js @@ -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; } @@ -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)); @@ -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"); }