Jelajahi Sumber

More cleanups.

Simon 4 tahun lalu
induk
melakukan
ed3a5e46f7

+ 13 - 3
src/controls.js

@@ -45,7 +45,7 @@ export const controls = (function() {
       this._standing = true;
       this._velocity = new THREE.Vector3(0, 0, 0);
       this._decceleration = new THREE.Vector3(-10, -10, -10);
-      this._acceleration = new THREE.Vector3(50000, 50000, 50000);
+      this._acceleration = new THREE.Vector3(5000, 5000, 5000);
 
       this._SetupPointerLock();
 
@@ -53,6 +53,12 @@ export const controls = (function() {
           params.camera, document.body);
       params.scene.add(this._controls.getObject());
 
+      const controlObject = this._controls.getObject();
+      this._position = new THREE.Vector3();
+      this._rotation = new THREE.Quaternion();
+      this._position.copy(controlObject.position);
+      this._rotation.copy(controlObject.quaternion);
+
       document.addEventListener('keydown', (e) => this._onKeyDown(e), false);
       document.addEventListener('keyup', (e) => this._onKeyUp(e), false);
 
@@ -61,7 +67,7 @@ export const controls = (function() {
 
     _InitGUI() {
       this._params.guiParams.camera = {
-        acceleration_x: 50000,
+        acceleration_x: 5000,
       };
 
       const rollup = this._params.gui.addFolder('Camera.FPS');
@@ -254,7 +260,11 @@ export const controls = (function() {
       controlObject.position.add(sideways);
       controlObject.position.add(updown);
 
-      oldPosition.copy(controlObject.position);
+      // this._position.lerp(controlObject.position, 0.15);
+      this._rotation.slerp(controlObject.quaternion, 0.15);
+
+      // controlObject.position.copy(this._position);
+      controlObject.quaternion.copy(this._rotation);
     }
   };
 

+ 0 - 27
src/main.js

@@ -4,11 +4,9 @@ import {controls} from './controls.js';
 import {game} from './game.js';
 import {terrain} from './terrain.js';
 
-
 let _APP = null;
 
 
-
 class ProceduralTerrain_Demo extends game.Game {
   constructor() {
     super();
@@ -33,22 +31,6 @@ class ProceduralTerrain_Demo extends game.Game {
       game: this
     });
 
-    // this._entities['_controls'] = new controls.OrbitControls({
-    //   camera: this._graphics.Camera,
-    //   scene: this._graphics.Scene,
-    //   domElement: this._graphics._threejs.domElement,
-    //   gui: this._gui,
-    //   guiParams: this._guiParams,
-    // });
-
-    // this._entities['_controls'] = new controls.ShipControls({
-    //   camera: this._graphics.Camera,
-    //   scene: this._graphics.Scene,
-    //   domElement: this._graphics._threejs.domElement,
-    //   gui: this._gui,
-    //   guiParams: this._guiParams,
-    // });
-
     this._entities['_controls'] = new controls.FPSControls({
         camera: this._graphics.Camera,
         scene: this._graphics.Scene,
@@ -57,15 +39,6 @@ class ProceduralTerrain_Demo extends game.Game {
         guiParams: this._guiParams,
     });
 
-    // this._focusMesh = new THREE.Mesh(
-    //   new THREE.SphereGeometry(25, 32, 32),
-    //   new THREE.MeshBasicMaterial({
-    //       color: 0xFFFFFF
-    //   }));
-    // this._focusMesh.castShadow = true;
-    // this._focusMesh.receiveShadow = true;
-    //this._graphics.Scene.add(this._focusMesh);
-
     this._totalTime = 0;
 
     this._LoadBackground();

+ 4 - 1
src/terrain-builder-threaded-worker.js

@@ -22,7 +22,10 @@ class _TerrainBuilderThreadedWorker {
     this._params.biomeGenerator = new noise.Noise(params.biomesParams);
     this._params.colourNoise = new noise.Noise(params.colourNoiseParams);
     this._params.colourGenerator = new texture_splatter.TextureSplatter(
-        {biomeGenerator: this._params.biomeGenerator, colourNoise: this._params.colourNoise});
+        {
+          biomeGenerator: this._params.biomeGenerator,
+          colourNoise: this._params.colourNoise
+        });
   }
 
   _GenerateHeight(v) {

+ 8 - 7
src/terrain-builder-threaded.js

@@ -8,7 +8,7 @@ export const terrain_builder_threaded = (function() {
 
   let _IDs = 0;
 
-  class PWorker {
+  class WorkerThread {
     constructor(s) {
       this._worker = new Worker(s, {type: 'module'});
       this._worker.onmessage = (e) => {
@@ -28,15 +28,15 @@ export const terrain_builder_threaded = (function() {
       return this._id;
     }
 
-    sendAsync(s, resolve) {
+    postMessage(s, resolve) {
       this._resolve = resolve;
       this._worker.postMessage(s);
     }
   }
 
-  class PWorkerPool {
+  class WorkerThreadPool {
     constructor(sz, entry) {
-      this._workers = [...Array(sz)].map(_ => new PWorker(entry));
+      this._workers = [...Array(sz)].map(_ => new WorkerThread(entry));
       this._free = [...this._workers];
       this._busy = {};
       this._queue = [];
@@ -62,7 +62,7 @@ export const terrain_builder_threaded = (function() {
 
         const [workItem, workResolve] = this._queue.shift();
 
-        w.sendAsync(workItem, (v) => {
+        w.postMessage(workItem, (v) => {
           delete this._busy[w.id];
           this._free.push(w);
           workResolve(v);
@@ -77,14 +77,15 @@ export const terrain_builder_threaded = (function() {
       this._pool = {};
       this._old = [];
 
-      this._workerPool = new PWorkerPool(_NUM_WORKERS, 'src/terrain-builder-threaded-worker.js');
+      this._workerPool = new WorkerThreadPool(
+          _NUM_WORKERS, 'src/terrain-builder-threaded-worker.js');
   
       this._params = params;
     }
 
     _OnResult(chunk, msg) {
       if (msg.subject == 'build_chunk_result') {
-        chunk.RebuildMeshFromData(msg.data)
+        chunk.RebuildMeshFromData(msg.data);
         chunk.Show();
       }
     }

+ 100 - 0
src/terrain-builder.js

@@ -0,0 +1,100 @@
+import {terrain_chunk} from './terrain-chunk.js';
+
+
+export const terrain_builder = (function() {
+
+  class _TerrainChunkRebuilder {
+    constructor(params) {
+      this._pool = {};
+      this._params = params;
+      this._Reset();
+    }
+
+    AllocateChunk(params) {
+      const w = params.width;
+
+      if (!(w in this._pool)) {
+        this._pool[w] = [];
+      }
+
+      let c = null;
+      if (this._pool[w].length > 0) {
+        c = this._pool[w].pop();
+        c._params = params;
+      } else {
+        c = new terrain_chunk.TerrainChunk(params);
+      }
+
+      c.Hide();
+
+      this._queued.push(c);
+
+      return c;    
+    }
+
+    RetireChunks(chunks) {
+      this._old.push(...chunks);
+    }
+
+    _RecycleChunks(chunks) {
+      for (let c of chunks) {
+        if (!(c.chunk._params.width in this._pool)) {
+          this._pool[c.chunk._params.width] = [];
+        }
+
+        c.chunk.Destroy();
+      }
+    }
+
+    _Reset() {
+      this._active = null;
+      this._queued = [];
+      this._old = [];
+      this._new = [];
+    }
+
+    get Busy() {
+      return this._active || this._queued.length > 0;
+    }
+
+    Rebuild(chunks) {
+      if (this.Busy) {
+        return;
+      }
+      for (let k in chunks) {
+        this._queued.push(chunks[k].chunk);
+      }
+    }
+
+    Update() {
+      if (this._active) {
+        const r = this._active.next();
+        if (r.done) {
+          this._active = null;
+        }
+      } else {
+        const b = this._queued.pop();
+        if (b) {
+          this._active = b._Rebuild();
+          this._new.push(b);
+        }
+      }
+
+      if (this._active) {
+        return;
+      }
+
+      if (!this._queued.length) {
+        this._RecycleChunks(this._old);
+        for (let b of this._new) {
+          b.Show();
+        }
+        this._Reset();
+      }
+    }
+  }
+
+  return {
+    TerrainChunkRebuilder: _TerrainChunkRebuilder
+  }
+})();

+ 272 - 0
src/terrain-chunk.js

@@ -44,7 +44,279 @@ export const terrain_chunk = (function() {
           'weights2', new THREE.Float32BufferAttribute(data.weights2, 4));
       this._geometry.setAttribute(
           'uv', new THREE.Float32BufferAttribute(data.uvs, 2));
+    }
+
+    _GenerateHeight(v) {
+      return this._params.heightGenerators[0].Get(v.x, v.y, v.z)[0];
+    }
+
+    *_Rebuild() {
+      const _D = new THREE.Vector3();
+      const _D1 = new THREE.Vector3();
+      const _D2 = new THREE.Vector3();
+      const _P = new THREE.Vector3();
+      const _H = new THREE.Vector3();
+      const _W = new THREE.Vector3();
+      const _C = new THREE.Vector3();
+      const _S = new THREE.Vector3();
+
+      const _N = new THREE.Vector3();
+      const _N1 = new THREE.Vector3();
+      const _N2 = new THREE.Vector3();
+      const _N3 = new THREE.Vector3();
+
+      const positions = [];
+      const colors = [];
+      const normals = [];
+      const tangents = [];
+      const uvs = [];
+      const weights1 = [];
+      const weights2 = [];
+      const indices = [];
+      const wsPositions = [];
+
+      const localToWorld = this._params.group.matrix;
+      const resolution = this._params.resolution;
+      const radius = this._params.radius;
+      const offset = this._params.offset;
+      const width = this._params.width;
+      const half = width / 2;
+
+      for (let x = 0; x < resolution + 1; x++) {
+        const xp = width * x / resolution;
+        for (let y = 0; y < resolution + 1; y++) {
+          const yp = width * y / resolution;
+
+          // Compute position
+          _P.set(xp - half, yp - half, radius);
+          _P.add(offset);
+          _P.normalize();
+          _D.copy(_P);
+          _P.multiplyScalar(radius);
+          _P.z -= radius;
+
+          // Compute a world space position to sample noise
+          _W.copy(_P);
+          _W.applyMatrix4(localToWorld);
+
+          const height = this._GenerateHeight(_W);
+
+          // Purturb height along z-vector
+          _H.copy(_D);
+          _H.multiplyScalar(height);
+          _P.add(_H);
+
+          positions.push(_P.x, _P.y, _P.z);
+
+          _S.set(_W.x, _W.y, height);
+
+          const color = this._params.colourGenerator.GetColour(_S);
+          colors.push(color.r, color.g, color.b);
+          normals.push(_D.x, _D.y, _D.z);
+          tangents.push(1, 0, 0, 1);
+          wsPositions.push(_W.x, _W.y, height);
+          // TODO GUI
+          uvs.push(_P.x / 200.0, _P.y / 200.0);
+        }
+      }
+      yield;
+
+      for (let i = 0; i < resolution; i++) {
+        for (let j = 0; j < resolution; j++) {
+          indices.push(
+              i * (resolution + 1) + j,
+              (i + 1) * (resolution + 1) + j + 1,
+              i * (resolution + 1) + j + 1);
+          indices.push(
+              (i + 1) * (resolution + 1) + j,
+              (i + 1) * (resolution + 1) + j + 1,
+              i * (resolution + 1) + j);
+        }
+      }
+      yield;
+
+      const up = [...normals];
+
+      for (let i = 0, n = indices.length; i < n; i+= 3) {
+        const i1 = indices[i] * 3;
+        const i2 = indices[i+1] * 3;
+        const i3 = indices[i+2] * 3;
+
+        _N1.fromArray(positions, i1);
+        _N2.fromArray(positions, i2);
+        _N3.fromArray(positions, i3);
+
+        _D1.subVectors(_N3, _N2);
+        _D2.subVectors(_N1, _N2);
+        _D1.cross(_D2);
+
+        normals[i1] += _D1.x;
+        normals[i2] += _D1.x;
+        normals[i3] += _D1.x;
+
+        normals[i1+1] += _D1.y;
+        normals[i2+1] += _D1.y;
+        normals[i3+1] += _D1.y;
+
+        normals[i1+2] += _D1.z;
+        normals[i2+2] += _D1.z;
+        normals[i3+2] += _D1.z;
+      }
+      yield;
+
+      for (let i = 0, n = normals.length; i < n; i+=3) {
+        _N.fromArray(normals, i);
+        _N.normalize();
+        normals[i] = _N.x;
+        normals[i+1] = _N.y;
+        normals[i+2] = _N.z;
+      }
+      yield;
+
+      let count = 0;
+      for (let i = 0, n = indices.length; i < n; i+=3) {
+        const splats = [];
+        const i1 = indices[i] * 3;
+        const i2 = indices[i+1] * 3;
+        const i3 = indices[i+2] * 3;
+        const indexes = [i1, i2, i3];
+        for (let j = 0; j < 3; j++) {
+          const j1 = indexes[j];
+          _P.fromArray(wsPositions, j1);
+          _N.fromArray(normals, j1);
+          _D.fromArray(up, j1);
+          const s = this._params.colourGenerator.GetSplat(_P, _N, _D);
+          splats.push(s);
+        }
+
+        const splatStrengths = {};
+        for (let k in splats[0]) {
+          splatStrengths[k] = {key: k, strength: 0.0};
+        }
+        for (let curSplat of splats) {
+          for (let k in curSplat) {
+            splatStrengths[k].strength += curSplat[k].strength;
+          }
+        }
 
+        let typeValues = Object.values(splatStrengths);
+        typeValues.sort((a, b) => {
+          if (a.strength < b.strength) {
+            return 1;
+          }
+          if (a.strength > b.strength) {
+            return -1;
+          }
+          return 0;
+        });
+
+        const w1 = indices[i] * 4;
+        const w2 = indices[i+1] * 4;
+        const w3 = indices[i+2] * 4;
+
+        for (let s = 0; s < 3; s++) {
+          let total = (
+              splats[s][typeValues[0].key].strength +
+              splats[s][typeValues[1].key].strength +
+              splats[s][typeValues[2].key].strength +
+              splats[s][typeValues[3].key].strength);
+          const normalization = 1.0 / total;
+
+          splats[s][typeValues[0].key].strength *= normalization;
+          splats[s][typeValues[1].key].strength *= normalization;
+          splats[s][typeValues[2].key].strength *= normalization;
+          splats[s][typeValues[3].key].strength *= normalization;
+        }
+ 
+        weights1.push(splats[0][typeValues[3].key].index);
+        weights1.push(splats[0][typeValues[2].key].index);
+        weights1.push(splats[0][typeValues[1].key].index);
+        weights1.push(splats[0][typeValues[0].key].index);
+
+        weights1.push(splats[1][typeValues[3].key].index);
+        weights1.push(splats[1][typeValues[2].key].index);
+        weights1.push(splats[1][typeValues[1].key].index);
+        weights1.push(splats[1][typeValues[0].key].index);
+
+        weights1.push(splats[2][typeValues[3].key].index);
+        weights1.push(splats[2][typeValues[2].key].index);
+        weights1.push(splats[2][typeValues[1].key].index);
+        weights1.push(splats[2][typeValues[0].key].index);
+
+        weights2.push(splats[0][typeValues[3].key].strength);
+        weights2.push(splats[0][typeValues[2].key].strength);
+        weights2.push(splats[0][typeValues[1].key].strength);
+        weights2.push(splats[0][typeValues[0].key].strength);
+
+        weights2.push(splats[1][typeValues[3].key].strength);
+        weights2.push(splats[1][typeValues[2].key].strength);
+        weights2.push(splats[1][typeValues[1].key].strength);
+        weights2.push(splats[1][typeValues[0].key].strength);
+
+        weights2.push(splats[2][typeValues[3].key].strength);
+        weights2.push(splats[2][typeValues[2].key].strength);
+        weights2.push(splats[2][typeValues[1].key].strength);
+        weights2.push(splats[2][typeValues[0].key].strength);
+
+        count++;
+        if ((count % 10000) == 0) {
+          yield;
+        }
+      }
+      yield;
+
+      function _Unindex(src, stride) {
+        const dst = [];
+        for (let i = 0, n = indices.length; i < n; i+= 3) {
+          const i1 = indices[i] * stride;
+          const i2 = indices[i+1] * stride;
+          const i3 = indices[i+2] * stride;
+
+          for (let j = 0; j < stride; j++) {
+            dst.push(src[i1 + j]);
+          }
+          for (let j = 0; j < stride; j++) {
+            dst.push(src[i2 + j]);
+          }
+          for (let j = 0; j < stride; j++) {
+            dst.push(src[i3 + j]);
+          }
+        }
+        return dst;
+      }
+
+      const uiPositions = _Unindex(positions, 3);
+      yield;
+
+      const uiColours = _Unindex(colors, 3);
+      yield;
+
+      const uiNormals = _Unindex(normals, 3);
+      yield;
+
+      const uiTangents = _Unindex(tangents, 4);
+      yield;
+
+      const uiUVs = _Unindex(uvs, 2);
+      yield;
+
+      const uiWeights1 = weights1;
+      const uiWeights2 = weights2;
+
+      this._geometry.setAttribute(
+        'position', new THREE.Float32BufferAttribute(uiPositions, 3));
+      this._geometry.setAttribute(
+          'color', new THREE.Float32BufferAttribute(uiColours, 3));
+      this._geometry.setAttribute(
+          'normal', new THREE.Float32BufferAttribute(uiNormals, 3));
+      this._geometry.setAttribute(
+          'tangent', new THREE.Float32BufferAttribute(uiTangents, 4));
+      this._geometry.setAttribute(
+          'weights1', new THREE.Float32BufferAttribute(uiWeights1, 4));
+      this._geometry.setAttribute(
+          'weights2', new THREE.Float32BufferAttribute(uiWeights2, 4));
+      this._geometry.setAttribute(
+          'uv', new THREE.Float32BufferAttribute(uiUVs, 2));
     }
   }
 

+ 3 - 88
src/terrain.js

@@ -7,6 +7,7 @@ import {quadtree} from './quadtree.js';
 import {spline} from './spline.js';
 import {terrain_chunk} from './terrain-chunk.js';
 import {terrain_shader} from './terrain-shader.js';
+import {terrain_builder} from './terrain-builder.js';
 import {terrain_builder_threaded} from './terrain-builder-threaded.js';
 import {texture_splatter} from './texture-splatter.js';
 import {textures} from './textures.js';
@@ -19,94 +20,7 @@ export const terrain = (function() {
   const _MIN_CELL_RESOLUTION = 64;
   const _PLANET_RADIUS = 4000;  
   
-
-  class TerrainChunkRebuilder {
-    constructor(params) {
-      this._pool = {};
-      this._params = params;
-      this._Reset();
-    }
-
-    AllocateChunk(params) {
-      const w = params.width;
-
-      if (!(w in this._pool)) {
-        this._pool[w] = [];
-      }
-
-      let c = null;
-      if (this._pool[w].length > 0) {
-        c = this._pool[w].pop();
-        c._params = params;
-      } else {
-        c = new terrain_chunk.TerrainChunk(params);
-      }
-
-      c.Hide();
-
-      this._queued.push(c);
-
-      return c;    
-    }
-
-    _RecycleChunks(chunks) {
-      for (let c of chunks) {
-        if (!(c.chunk._params.width in this._pool)) {
-          this._pool[c.chunk._params.width] = [];
-        }
-
-        c.chunk.Destroy();
-      }
-    }
-
-    _Reset() {
-      this._active = null;
-      this._queued = [];
-      this._old = [];
-      this._new = [];
-    }
-
-    get Busy() {
-      return this._active || this._queued.length > 0;
-    }
-
-    Rebuild(chunks) {
-      if (this.Busy) {
-        return;
-      }
-      for (let k in chunks) {
-        this._queued.push(chunks[k].chunk);
-      }
-    }
-
-    Update() {
-      if (this._active) {
-        const r = this._active.next();
-        if (r.done) {
-          this._active = null;
-        }
-      } else {
-        const b = this._queued.pop();
-        if (b) {
-          this._active = b._Rebuild();
-          this._new.push(b);
-        }
-      }
-
-      if (this._active) {
-        return;
-      }
-
-      if (!this._queued.length) {
-        this._RecycleChunks(this._old);
-        for (let b of this._new) {
-          b.Show();
-        }
-        this._Reset();
-      }
-    }
-  }
-
+  
   class TerrainChunkManager {
     constructor(params) {
       this._Init(params);
@@ -176,6 +90,7 @@ export const terrain = (function() {
       });
 
       this._builder = new terrain_builder_threaded.TerrainChunkRebuilder_Threaded();
+      // this._builder = new terrain_builder.TerrainChunkRebuilder();
 
       this._InitNoise(params);
       this._InitBiomes(params);

+ 1 - 1
src/textures.js

@@ -58,7 +58,7 @@ export const textures = (function() {
           diffuse.format = THREE.RGBAFormat;
           diffuse.type = THREE.UnsignedByteType;
           diffuse.minFilter = THREE.LinearMipMapLinearFilter;
-          diffuse.magFilter = THREE.NearestFilter;
+          diffuse.magFilter = THREE.LinearFilter;
           diffuse.wrapS = THREE.RepeatWrapping;
           diffuse.wrapT = THREE.RepeatWrapping;
           diffuse.generateMipmaps = true;