terrain-map
ARCHIVED - repo for blog post http://www.vogt.world/writing/procedural-terrain-generation/
git clone https://git.vogt.world/terrain-map.git
Log | Files | README.md
← Commit log
commit
message
Erosion working in a manner of speaking
author
Ben Vogt <[email protected]>
date
2016-05-14 02:24:19
stats
2 file(s) changed, 174 insertions(+), 14 deletions(-)
files
index.html
main.js
  1diff --git a/index.html b/index.html
  2index 5aaf051..5093422 100644
  3--- a/index.html
  4+++ b/index.html
  5@@ -24,10 +24,10 @@
  6       </div>
  7       <div class="set-of" id="container-1"></div>
  8       <hr>
  9+    </div>
 10+    <div class="container">
 11       <div class="row">
 12-        <h2>
 13-              Combining Diamond-Square maps
 14-            </h2>
 15+        <h2>Combining Diamond-Square maps</h2>
 16         <p>
 17           Combining a DS, DS smoothed (10), and DS smoothed (20) maps. This is done by generating <code>standard</code>, then <code>standard-two</code>, a completely different map. Then using each of these to generate a smoothed version of themselves. Then
 18           we can combine standard with <code>standard-10</code>, using <code>standard-two-20</code> as the map controlling which parts of the map are used for the result. For example the higher the value of a given point on <code>standard-two-20</code>, the
 19@@ -41,11 +41,8 @@
 20         </p>
 21       </div>
 22       <div class="set-of" id="container-2"></div>
 23+      <hr>
 24     </div>
 25-    <div class="hidden">
 26-      <canvas id="tmp" width="256" height="256"></canvas>
 27-    </div>
 28-    <hr>
 29     <div class="container">
 30       <div class="row">
 31         <h2>Greedy Raindrop Erosion Algorithm</h2>
 32@@ -55,14 +52,17 @@
 33         </p>
 34       </div>
 35       <div class="set-of" id="container-3"></div>
 36+      <hr>
 37     </div>
 38     <div class="container">
 39-      <hr>
 40       <div class="row">
 41+        <h2>Soil Erosion Algorithm</h2>
 42         <p>
 43-          That's all!
 44+          <button class="run button-primary" id="erosion">RUN</button>
 45         </p>
 46       </div>
 47+      <div class="set-of" id="container-4"></div>
 48     </div>
 49+    <canvas class="hidden" id="tmp" width="256" height="256"></canvas>
 50   </body>
 51 </html>
 52diff --git a/main.js b/main.js
 53index 36de5bf..5e68442 100644
 54--- a/main.js
 55+++ b/main.js
 56@@ -1,4 +1,39 @@
 57 $(document).ready(function() {
 58+
 59+  function indexOfMax(arr) {
 60+    if (arr.length === 0) {
 61+      return -1;
 62+    }
 63+
 64+    var max = arr[0];
 65+    var maxIndex = 0;
 66+
 67+    for (var ind = 1; ind < arr.length; ind++) {
 68+      if (arr[ind] > max) {
 69+        maxIndex = ind;
 70+        max = arr[ind];
 71+      }
 72+    }
 73+    return maxIndex;
 74+  }
 75+
 76+  function indexOfMin(arr) {
 77+    if (arr.length === 0) {
 78+      return -1;
 79+    }
 80+
 81+    var min = arr[0];
 82+    var minIndex = 0;
 83+
 84+    for (var ind = 1; ind < arr.length; ind++) {
 85+      if (arr[ind] < min) {
 86+        minIndex = ind;
 87+        min = arr[ind];
 88+      }
 89+    }
 90+    return minIndex;
 91+  }
 92+
 93   function LandMap(options) {
 94     var level = 8;
 95     this.containerId = options.containerId;
 96@@ -225,24 +260,122 @@ $(document).ready(function() {
 97         }
 98       }
 99     }
100+  };
101 
102-    function indexOfMax(arr) {
103-      if (arr.length === 0) {
104-        return -1;
105+  LandMap.prototype.erosion = function(options) {
106+    var Kq = options.Kq;
107+    var Kw = options.evaporationSpeed;
108+    var Kr = options.erosionSpeed;
109+    var Kd = options.depositionSpeed;
110+    var Kg = options.gravity * 2;
111+    var iterations = options.iterations;
112+    var drops = options.drops;
113+    var one = options.one;
114+    var two = options.two;
115+    var ds = 0;
116+
117+    var HeightMap = new Array(this.size * this.size);
118+    for (var y = 0; y < this.size; y++) {
119+      for (var x = 0; x < this.size; x++) {
120+        HeightMap[(x + this.size * y)] = this.get(one, x, y);
121       }
122+    }
123+
124+    var HMAP_SIZE = this.size;
125+
126+    function HMAP_INDEX(x, y) {
127+      var val = (x + HMAP_SIZE * y)
128+      return val;
129+    }
130+
131+    function HMAP_VALUE(x, y) {
132+      return HeightMap[(x + HMAP_SIZE * y)];
133+    }
134 
135-      var max = arr[0];
136-      var maxIndex = 0;
137 
138-      for (var ind = 1; ind < arr.length; ind++) {
139-        if (arr[ind] > max) {
140-          maxIndex = ind;
141-          max = arr[ind];
142+    var MAX_PATH_LEN = this.size * 4;
143+
144+    function DEPOSIT_AT(X, Y) {
145+      var c = 0.0;
146+      var v = 1.05;
147+      var g = 1.4;
148+      var mv = 10.0;
149+
150+      // For the number of iterations
151+      for (var iter = 0; iter < iterations; iter++) {
152+        var v = Math.min(v, mv); // limiting velocity
153+        var val = HMAP_VALUE(X, Y);
154+        var nv = [
155+          HMAP_VALUE(X, Y - 1), //NORTH
156+          HMAP_VALUE(X, Y + 1), //SOUTH
157+          HMAP_VALUE(X + 1, Y), //EAST
158+          HMAP_VALUE(X - 1, Y) //WEST
159+        ];
160+
161+        var minInd = indexOfMin(nv);
162+        // if the lowest neighbor is NOT greater than the current value
163+        if (nv[minInd] < val) {
164+          //deposit or erode
165+          var vtc = Kd * v * Math.abs(nv[minInd]); // value to steal is depositionSpeed * velocity * abs(slope);
166+          // if carrying amount is greater than Kq
167+          if (c > Kq) {
168+            //DEPOSIT
169+            c -= vtc;
170+            HeightMap[HMAP_INDEX(X, Y)] += vtc;
171+          } else {
172+            //ERODE
173+            // if carrying + value to steal > carrying cap
174+            if (c + vtc > Kq) {
175+              var delta = c + vtc - Kq;
176+              c += delta;
177+              HeightMap[HMAP_INDEX(X, Y)] -= delta;
178+            } else {
179+              c += vtc;
180+              HeightMap[HMAP_INDEX(X, Y)] -= vtc;
181+            }
182+          }
183+
184+          // move to next value
185+          if (minInd == 0) {
186+            //NORTH
187+            Y -= 1
188+          }
189+          if (minInd == 1) {
190+            //SOUTH
191+            Y += 1
192+          }
193+          if (minInd == 2) {
194+            //EAST
195+            X += 1
196+          }
197+          if (minInd == 3) {
198+            //WEST
199+            X -= 1
200+          }
201+
202+          // limiting to edge of map
203+          if (X > this.size - 1) {
204+            X = this.size;
205+          }
206+          if (Y > this.size - 1) {
207+            Y = this.size;
208+          }
209+          if (Y < 0) {
210+            Y = 0;
211+          }
212+          if (X < 0) {
213+            X = 0;
214+          }
215         }
216       }
217-      return maxIndex;
218     }
219-  };
220+
221+    for (var drop = 0; drop < drops; drop++) {
222+      DEPOSIT_AT(Math.floor(Math.random() * this.size), Math.floor(Math.random() * this.size));
223+      this.maps[two] = HeightMap;
224+    }
225+  }
226+
227 
228   LandMap.prototype.draw = function() {
229     var html = '<div class="row">';
230@@ -336,4 +469,24 @@ $(document).ready(function() {
231     terrain.draw();
232   });
233 
234+
235+  $("#erosion").click(function() {
236+    var terrain = new LandMap({
237+      containerId: "container-4"
238+    });
239+    terrain.generate(0.75, "standard");
240+    terrain.erosion({
241+      Kq: 1.5,
242+      evaporationSpeed: 0.001,
243+      erosionSpeed: 1.01,
244+      depositionSpeed: 0.03,
245+      gravity: 10,
246+      iterations: 10,
247+      drops: 1000000,
248+      one: "standard",
249+      two: "erosion"
250+    });
251+    terrain.draw();
252+  });
253+
254 })