Browse Source

adding gersternerwave component

Justin Gilman 1 year ago
parent
commit
b8a997f94b
1 changed files with 271 additions and 0 deletions
  1. 271 0
      Assets/Components/GerstnerWavesComponent.re.ts

+ 271 - 0
Assets/Components/GerstnerWavesComponent.re.ts

@@ -0,0 +1,271 @@
+import * as RE from 'rogue-engine'
+import * as THREE from 'three'
+
+// https://codesandbox.io/s/prototypes-pygsc7?file=/prototypes/water/001_gerstner.html:0-10387
+// https://catlikecoding.com/unity/tutorials/flow/waves/
+
+// Old Fungi Version
+// https://sketchpunk.bitbucket.io/src/fungi_v4/002_gerstner_waves.html
+// https://bitbucket.org/sketchpunk/sketchpunk.bitbucket.io/src/c036dfb4b5a93425220b4bb482c0e96e1edc77cf/src/fungi_v4/002_gerstner_waves.html#lines-304
+
+export default class GerstnerWavesComponent extends RE.Component {
+    wave: GerstnerWave = new GerstnerWave(2, 12)
+    vertexShader    = `#version 300 es
+    in vec3 position;
+    in vec3 normal;
+    in vec2 uv;
+    
+    uniform mat4 modelMatrix;
+    uniform mat4 viewMatrix;
+    uniform mat4 projectionMatrix;
+    
+    uniform float clock;
+
+    out vec3 fragNorm;
+    out vec3 fragWPos;
+
+    /////////////////////////////////////////////////////////////////
+    // #region GERSTNER WAVE
+    const float PI_2	= 6.283185307179586;
+
+    uniform 	vec4	Waves[3];   // XY : Direction, Z: Steepness, W: Wavelength
+    uniform 	int 	WaveCnt;
+    uniform		float 	WaveSpeed;
+    uniform 	int 	WaveXZ;
+    uniform     float   GridSize;
+    uniform     float   GridRes;
+
+    // https://catlikecoding.com/unity/tutorials/flow/waves/
+    // XY : norm( Dir.xy ), Z : Steepness( 0->1 ), W : 0 to Total Grid Divisions, Max gives one wave
+    vec3 gerstnerWave( vec2 dir, float steepness, float wavelength, vec2 pnt, float time, out vec3 tangent, out vec3 binormal ){
+        float k = PI_2 / wavelength;	                // Phase Increment
+        float c = sqrt( 9.8 / k );	                    // Phase Speed. Higher the wave, the faster it moves, Gravity Constant
+        float f = k * ( dot( dir, pnt ) - c * time );   // Frequency - Specific time in Phase : PhaseInc * ( Angle - PhaseSpeed * Time )
+        float a = steepness / k;		                // Amptitude, Steep=1, app is at max where mesh will start to loop onto self.
+        
+        // cache results for multi reuse.
+        float sin_f  = sin( f );
+        float cos_f  = cos( f );
+        float acos_f = a * cos_f;
+        float scos_f = steepness * cos_f;
+        float ssin_f = steepness * sin_f;
+
+        /*====================================
+        Must Init outside function as
+        vec3 tangent = vec3( 1.0, 0.0, 0.0 );
+        vec3 binormal = vec3( 0.0, 0.0, 1.0 );
+        normal is normalize(cross(binormal, tangent)); */
+
+        tangent += vec3(
+          -dir.x * dir.x * ssin_f,
+           dir.x * scos_f,
+          -dir.x * dir.y * ssin_f
+      );
+
+      binormal += vec3(
+          -dir.x * dir.y * ssin_f,
+           dir.y * scos_f,
+          -dir.y * dir.y * ssin_f
+      );
+      
+      return vec3( 
+          dir.x * acos_f, 
+          a * sin_f,       
+          dir.y * acos_f
+      );
+  }
+  // #endregion
+
+  /////////////////////////////////////////////////////////////////
+  // #region GRID
+  const float FLOOR = 0.001;
+
+  vec2 gridCenterCoord( vec2 p ){
+      float inc = GridSize / GridRes;
+      p += vec2( GridSize * 0.5 );       // Shift so top Left is at origin
+      return floor( p / vec2( inc ) );
+  }
+
+  vec2 gridCoord( vec2 p ){
+      float inc = GridSize / GridRes;
+      return floor( p / vec2( inc ) );
+      // return p / vec2( inc );
+  }
+  // #endregion
+  
+  /////////////////////////////////////////////////////////////////
+
+  void main(){
+      vec3 pos = position;
+      fragNorm = normal;
+
+      if( pos.y > FLOOR ){
+          vec2    coord       = gridCoord( pos.xz );
+          vec3    offset      = vec3( 0.0 );
+          vec3    tangent     = vec3( 1.0, 0.0, 0.0 ); // Normal is Up, So tangent is Right
+          vec3    binormal    = vec3( 0.0, 0.0, 1.0 ); // ... and Binormal is Forward
+          float   time        = clock * WaveSpeed;    
+
+          if( WaveCnt > 0 ) offset += gerstnerWave( normalize(Waves[0].xy), Waves[0].z, Waves[0].w, coord, time, tangent, binormal );
+          if( WaveCnt > 1 ) offset += gerstnerWave( normalize(Waves[1].xy), Waves[1].z, Waves[1].w, coord, time, tangent, binormal );
+          if( WaveCnt > 2 ) offset += gerstnerWave( normalize(Waves[2].xy), Waves[2].z, Waves[2].w, coord, time, tangent, binormal );
+          
+          if( WaveXZ == 1 ){
+              // Only keep height change
+              offset.x = 0.0;
+              offset.z = 0.0;
+          }else{
+              // keep Wave in cube bounds
+              if( coord.x < 1.0 || coord.x >= GridRes ) offset.x = 0.0;
+              if( coord.y < 1.0 || coord.y >= GridRes ) offset.z = 0.0;
+          }
+
+          pos += offset;
+
+          // Only Apply Wave Normals on top face, use normal to find it
+          if( normal.x < 0.001 && normal.z < 0.001 && normal.y > 0.9 ){
+              fragNorm = normalize( cross( binormal, tangent ) );
+          }
+      }
+
+      vec4 wPos   = modelMatrix * vec4( pos, 1.0 );
+      fragWPos    = wPos.xyz;
+      gl_Position = projectionMatrix * viewMatrix * wPos;
+  }`
+
+  fragmentShader  = `#version 300 es
+  precision mediump float;
+
+  in vec3 fragNorm;
+  in vec3 fragWPos;
+
+  uniform vec3 cameraPosition;
+
+  out vec4 outColor;
+
+  ////////////////////////////////////////////////////////////////////////
+
+
+  ////////////////////////////////////////////////////////////////////////
+  const vec3 lightWPos = vec3( 10.0, 10.0, 10.0 );
+
+  void main(){
+      // vec3 N = normalize( cross( dFdx(fragWPos), dFdy(fragWPos) ) );
+      vec3 N = normalize( fragNorm );
+      
+      // float ns  = 6.0; // Normal is to light, scale it up to improve renderering
+      // vec3 N    = normalize( vec3( fragNorm.x * ns, fragNorm.y, fragNorm.z * ns ) );
+
+      vec3 L    = normalize( lightWPos - fragWPos );
+      float NdL = clamp( dot( L, N ), 0.0, 1.0 );
+
+      outColor  = vec4( vec3( NdL ), 1.0 );
+
+      // if( NdL <= 0.0 ) outColor.rgb = vec3( 1.0, 0.0, 0.0 );
+  }`
+
+  elapsed = 0
+
+  awake() {
+
+  }
+
+  start() {
+
+    this.elapsed = 0;
+    let objectThree = (this.object3d as any)
+    let geometry: THREE.BoxGeometry = objectThree.geometry;
+
+    let gridSize = geometry.parameters.width//2
+    let gridRes = geometry.parameters.width * 6//12
+    objectThree.material = new THREE.RawShaderMaterial(
+      { depthTest: true,
+        transparent: false,
+        uniforms: {
+          clock       : { value : 0 },
+          Waves       : { value : new Float32Array( [ 0.5,0.5,0.09,10,  0,1,0.05,5,  0.3,0.7,0.01,2, ]) },
+          WaveCnt     : { value : 3 },
+          WaveSpeed   : { value : 1.0 },
+          WaveXZ      : { value : 1 },
+          GridSize    : { value : gridSize },
+          GridRes     : { value : gridRes },
+        },
+        vertexShader: this.vertexShader,
+        fragmentShader: this.fragmentShader,
+      }
+    )
+
+    this.wave = new GerstnerWave(gridSize, gridRes)
+
+  }
+
+  update() {
+    this.elapsed += RE.Runtime.deltaTime;
+    (this.object3d as any).material.uniforms.clock.value = this.elapsed
+  }
+}
+
+RE.registerComponent(GerstnerWavesComponent);
+
+// Compute height of the position
+class GerstnerWave {
+  minH      = 1;
+  gridSize  = 2;
+  gridRes   = 12;
+  waveSpeed = 1.0;
+
+  constructor(gridSize, gridRes) {
+    this.gridSize = gridSize//2
+    this.gridRes = gridRes//12
+  }
+
+  waves = [
+      { dir:[ 0.5,0.5 ], steep:0.09, wavelen:10 },
+      { dir:[ 0,1 ], steep:0.05, wavelen:5 },
+      { dir:[ 0.3,0.7 ], steep:0.01, wavelen:2 },
+  ];
+
+  static dot(a: THREE.Vector2, b: THREE.Vector2) {
+    return a.x * b.x + a.y * b.y;
+  }
+
+  calcWaveH( w, coord, time ){
+      const dir = new THREE.Vector2( w.dir ).normalize();
+      const k = 6.283185307179586 / w.wavelen;	                // Phase Increment
+      const c = Math.sqrt( 9.8 / k );	                            // Phase Speed. Higher the wave, the faster it moves, Gravity Constant
+      const f = k * ( GerstnerWave.dot( dir, coord ) - c * time );        // Frequency - Specific time in Phase : PhaseInc * ( Angle - PhaseSpeed * Time )
+      const a = w.steep / k;		                                // Amptitude, Steep=1, app is at max where mesh will start to loop onto self.
+      
+      // cache results for multi reuse.
+      const sin_f  = Math.sin( f );
+      // const cos_f  = Math.cos( f );
+      // const acos_f = a * cos_f;
+
+      // Gerstner Position
+      // return [ 
+      //     dir.x * acos_f, 
+      //     a * sin_f,       
+      //     dir.y * acos_f
+      // ];
+
+      // Just the Y value
+      return a * sin_f;
+  }
+
+  getHit( p, clock=0 ){
+      const coord = this.gridCoord( p );
+      const time  = clock * this.waveSpeed;
+
+      let h = 0;
+      for( const w of this.waves ){
+          h += this.calcWaveH( w, coord, time );
+      }
+
+      return [ p[0], this.minH + h, p[2] ];
+  }
+
+  gridCoord( p ){
+      const inc = this.gridSize / this.gridRes;
+      return {x: p[0] / inc , y: p[2] / inc };
+  }
+}