commit 4fde55a034bce186f6a215c4417dd54c7eb6da79
Author: Leszek Koltunski <leszek@distorted.org>
Date:   Mon Aug 29 17:51:56 2016 +0100

    Beginnings of the WAVE effect.

diff --git a/src/main/java/org/distorted/library/DistortedObject.java b/src/main/java/org/distorted/library/DistortedObject.java
index 2222406..e93512b 100644
--- a/src/main/java/org/distorted/library/DistortedObject.java
+++ b/src/main/java/org/distorted/library/DistortedObject.java
@@ -790,7 +790,7 @@ public abstract class DistortedObject
 /**
  * Rotate part of the Object around the Center of the Effect by a certain angle.
  *
- * @param swirl  The degree of Swirl. Positive values swirl clockwise.
+ * @param swirl  The angle of Swirl (in degrees). Positive values swirl clockwise.
  * @param center 2-dimensional Data that, at any given time, returns the Center of the Effect.
  * @param region Region that masks the Effect.
  * @return       ID of the effect added, or -1 if we failed to add one.
@@ -804,7 +804,7 @@ public abstract class DistortedObject
 /**
  * Rotate the whole Object around the Center of the Effect by a certain angle.
  *
- * @param swirl  The degree of Swirl. Positive values swirl clockwise.
+ * @param swirl  The angle of Swirl (in degrees). Positive values swirl clockwise.
  * @param center 2-dimensional Data that, at any given time, returns the Center of the Effect.
  * @return       ID of the effect added, or -1 if we failed to add one.
  */
@@ -812,4 +812,35 @@ public abstract class DistortedObject
     {
     return mV.add(EffectNames.SWIRL, swirl, center);
     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Directional, sinusoidal wave effect.
+ *
+ * @param wave   A 3-dimensional data structure describing the wave: first member is the amplitude,
+ *               second is the angle (in degrees, as always) the direction of the wave forms with
+ *               the X-axis, and the third is the wave length.
+ * @param center 2-dimensional Data that, at any given time, returns the Center of the Effect.
+ * @return       ID of the effect added, or -1 if we failed to add one.
+ */
+  public long wave(Data3D wave, Data2D center)
+    {
+    return mV.add(EffectNames.WAVE, wave, center);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Directional, sinusoidal wave effect.
+ *
+ * @param wave   A 3-dimensional data structure describing the wave: first member is the amplitude,
+ *               second is the angle (in degrees, as always) the direction of the wave forms with
+ *               the X-axis, and the third is the wave length.
+ * @param center 2-dimensional Data that, at any given time, returns the Center of the Effect.
+ * @param region Region that masks the Effect.
+ * @return       ID of the effect added, or -1 if we failed to add one.
+ */
+  public long wave(Data3D wave, Data2D center, Data4D region)
+    {
+    return mV.add(EffectNames.WAVE, wave, center, region);
+    }
   }
diff --git a/src/main/java/org/distorted/library/EffectNames.java b/src/main/java/org/distorted/library/EffectNames.java
index 7e13f30..9d17b41 100644
--- a/src/main/java/org/distorted/library/EffectNames.java
+++ b/src/main/java/org/distorted/library/EffectNames.java
@@ -115,6 +115,14 @@ public enum EffectNames
    * Unity: swirlAngle = 0
    */
   SWIRL            ( EffectTypes.VERTEX  ,   new float[] {0.0f}           ),
+  /**
+   * Directional sinusoidal wave effect. The direction of the wave is given by the 'angle'
+   * parameter, which is the angle (in degrees) the direction forms with the X-axis.
+   * <p>
+   * Uniforms: (amplitude,angle,length,regionX,regionY,regionRX,regionRY,centerX,centerY)
+   * Unity: amplitude  = 0
+   */
+  WAVE             ( EffectTypes.VERTEX  ,   new float[] {0.0f}           ),
   // add new Vertex Effects here...
 
  /////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/EffectQueueVertex.java b/src/main/java/org/distorted/library/EffectQueueVertex.java
index e82edba..083c7eb 100644
--- a/src/main/java/org/distorted/library/EffectQueueVertex.java
+++ b/src/main/java/org/distorted/library/EffectQueueVertex.java
@@ -39,7 +39,7 @@ import org.distorted.library.type.Static4D;
 
 class EffectQueueVertex extends EffectQueue
   { 
-  private static final int NUM_UNIFORMS = 9;
+  private static final int NUM_UNIFORMS = 12;
   private static final int INDEX = EffectTypes.VERTEX.ordinal();
   private static int mNumEffectsH;
   private static int mTypeH;
@@ -90,17 +90,17 @@ class EffectQueueVertex extends EffectQueue
         else mInter[0][i] = null;
         }
 
-      if( mInter[1][i]!=null )
+      if( mInter[1][i]!=null )  // region
         {
-        mInter[1][i].interpolateMain(mUniforms, NUM_UNIFORMS*i+3, mCurrentDuration[i]);
+        mInter[1][i].interpolateMain(mUniforms, NUM_UNIFORMS*i+8, mCurrentDuration[i]);
         }
 
-      if( mInter[2][i]!=null )
+      if( mInter[2][i]!=null )  // center
         {
-        mInter[2][i].interpolateMain(mUniforms, NUM_UNIFORMS*i+7, mCurrentDuration[i]);
+        mInter[2][i].interpolateMain(mUniforms, NUM_UNIFORMS*i+6, mCurrentDuration[i]);
 
-        mUniforms[NUM_UNIFORMS*i+7] = mUniforms[NUM_UNIFORMS*i+7]-mObjHalfX;
-        mUniforms[NUM_UNIFORMS*i+8] =-mUniforms[NUM_UNIFORMS*i+8]+mObjHalfY;
+        mUniforms[NUM_UNIFORMS*i+6] = mUniforms[NUM_UNIFORMS*i+6]-mObjHalfX;
+        mUniforms[NUM_UNIFORMS*i+7] =-mUniforms[NUM_UNIFORMS*i+7]+mObjHalfY;
         }
 
       mCurrentDuration[i] += step;
@@ -113,15 +113,18 @@ class EffectQueueVertex extends EffectQueue
 
   protected void moveEffect(int index)
     {
-    mUniforms[NUM_UNIFORMS*index  ] = mUniforms[NUM_UNIFORMS*(index+1)  ];
-    mUniforms[NUM_UNIFORMS*index+1] = mUniforms[NUM_UNIFORMS*(index+1)+1];
-    mUniforms[NUM_UNIFORMS*index+2] = mUniforms[NUM_UNIFORMS*(index+1)+2];
-    mUniforms[NUM_UNIFORMS*index+3] = mUniforms[NUM_UNIFORMS*(index+1)+3];
-    mUniforms[NUM_UNIFORMS*index+4] = mUniforms[NUM_UNIFORMS*(index+1)+4];
-    mUniforms[NUM_UNIFORMS*index+5] = mUniforms[NUM_UNIFORMS*(index+1)+5];
-    mUniforms[NUM_UNIFORMS*index+6] = mUniforms[NUM_UNIFORMS*(index+1)+6];
-    mUniforms[NUM_UNIFORMS*index+7] = mUniforms[NUM_UNIFORMS*(index+1)+7];
-    mUniforms[NUM_UNIFORMS*index+8] = mUniforms[NUM_UNIFORMS*(index+1)+8];
+    mUniforms[NUM_UNIFORMS*index   ] = mUniforms[NUM_UNIFORMS*(index+1)   ];
+    mUniforms[NUM_UNIFORMS*index+ 1] = mUniforms[NUM_UNIFORMS*(index+1)+ 1];
+    mUniforms[NUM_UNIFORMS*index+ 2] = mUniforms[NUM_UNIFORMS*(index+1)+ 2];
+    mUniforms[NUM_UNIFORMS*index+ 3] = mUniforms[NUM_UNIFORMS*(index+1)+ 3];
+    mUniforms[NUM_UNIFORMS*index+ 4] = mUniforms[NUM_UNIFORMS*(index+1)+ 4];
+    mUniforms[NUM_UNIFORMS*index+ 5] = mUniforms[NUM_UNIFORMS*(index+1)+ 5];
+    mUniforms[NUM_UNIFORMS*index+ 6] = mUniforms[NUM_UNIFORMS*(index+1)+ 6];
+    mUniforms[NUM_UNIFORMS*index+ 7] = mUniforms[NUM_UNIFORMS*(index+1)+ 7];
+    mUniforms[NUM_UNIFORMS*index+ 8] = mUniforms[NUM_UNIFORMS*(index+1)+ 8];
+    mUniforms[NUM_UNIFORMS*index+ 9] = mUniforms[NUM_UNIFORMS*(index+1)+ 9];
+    mUniforms[NUM_UNIFORMS*index+10] = mUniforms[NUM_UNIFORMS*(index+1)+10];
+    mUniforms[NUM_UNIFORMS*index+11] = mUniforms[NUM_UNIFORMS*(index+1)+11];
     }
    
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -132,8 +135,8 @@ class EffectQueueVertex extends EffectQueue
       
     if( mNumEffects>0 )
       {     
-      GLES20.glUniform1iv( mTypeH    ,  mNumEffects, mName,0);
-      GLES20.glUniform3fv( mUniformsH,3*mNumEffects, mUniforms,0);
+      GLES20.glUniform1iv( mTypeH    ,  mNumEffects, mName    ,0);
+      GLES20.glUniform4fv( mUniformsH,3*mNumEffects, mUniforms,0);
       }
     }
 
@@ -148,6 +151,7 @@ class EffectQueueVertex extends EffectQueue
 // Do various post-processing on already computed effects.
 // 1) here unlike in the fragment queue, we don't have to multiply the points by ModelView matrix because that gets done in the shader.
 // 2) in case of swirl, pre-compute the sine and cosine of its rotation angle
+// 3) likewise in case of wave
   
   void postprocess()
     {
@@ -158,14 +162,20 @@ class EffectQueueVertex extends EffectQueue
       if( mName[i]==EffectNames.SWIRL.ordinal() )
         {
         d = Math.PI*mUniforms[NUM_UNIFORMS*i]/180;  
-        mUniforms[NUM_UNIFORMS*i+1] = (float)Math.sin(d);
-        mUniforms[NUM_UNIFORMS*i+2] = (float)Math.cos(d);
+        mUniforms[NUM_UNIFORMS*i+4] = (float)Math.sin(d);
+        mUniforms[NUM_UNIFORMS*i+5] = (float)Math.cos(d);
+        }
+      if( mName[i]==EffectNames.WAVE.ordinal() )
+        {
+        d = Math.PI*mUniforms[NUM_UNIFORMS*i+1]/180;
+        mUniforms[NUM_UNIFORMS*i+4] = (float)Math.sin(d);
+        mUniforms[NUM_UNIFORMS*i+5] = (float)Math.cos(d);
         }
       }
     }
   
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// distort
+// distort, wave
 
   synchronized long add(EffectNames eln, Data3D data, Data2D center, Data4D region)
     {
@@ -190,7 +200,7 @@ class EffectQueueVertex extends EffectQueue
     }
    
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// deform, distort
+// deform, distort, wave
 
   synchronized long add(EffectNames eln, Data3D data, Data2D center)
     {
@@ -276,20 +286,20 @@ class EffectQueueVertex extends EffectQueue
 
         float z = tmp.getZ();
 
-        mUniforms[NUM_UNIFORMS*mNumEffects+3] = tmp.getX();
-        mUniforms[NUM_UNIFORMS*mNumEffects+4] =-tmp.getY();   // invert y already
-        mUniforms[NUM_UNIFORMS*mNumEffects+5] = z<=0.0f ? 1000*mObjHalfX : z;
-        mUniforms[NUM_UNIFORMS*mNumEffects+6] = tmp.getW();
+        mUniforms[NUM_UNIFORMS*mNumEffects+ 8] = tmp.getX();
+        mUniforms[NUM_UNIFORMS*mNumEffects+ 9] =-tmp.getY();   // invert y already
+        mUniforms[NUM_UNIFORMS*mNumEffects+10] = z<=0.0f ? 1000*mObjHalfX : z;
+        mUniforms[NUM_UNIFORMS*mNumEffects+11] = tmp.getW();
         mInter[1][mNumEffects] = null;
         }
       else return -1;
       }
     else
       {
-      mUniforms[NUM_UNIFORMS*mNumEffects+3] = 0.0f;
-      mUniforms[NUM_UNIFORMS*mNumEffects+4] = 0.0f;
-      mUniforms[NUM_UNIFORMS*mNumEffects+5] = 1000*mObjHalfX;
-      mUniforms[NUM_UNIFORMS*mNumEffects+6] = 0.0f;
+      mUniforms[NUM_UNIFORMS*mNumEffects+ 8] = 0.0f;
+      mUniforms[NUM_UNIFORMS*mNumEffects+ 9] = 0.0f;
+      mUniforms[NUM_UNIFORMS*mNumEffects+10] = 1000*mObjHalfX;
+      mUniforms[NUM_UNIFORMS*mNumEffects+11] = 0.0f;
       mInter[1][mNumEffects] = null;
       }
 
@@ -298,8 +308,8 @@ class EffectQueueVertex extends EffectQueue
     else if( center instanceof Static2D)
       {
       mInter[2][mNumEffects] = null;
-      mUniforms[NUM_UNIFORMS*mNumEffects+7] = ((Static2D)center).getX()-mObjHalfX;
-      mUniforms[NUM_UNIFORMS*mNumEffects+8] =-((Static2D)center).getY()+mObjHalfY;
+      mUniforms[NUM_UNIFORMS*mNumEffects+6] = ((Static2D)center).getX()-mObjHalfX;
+      mUniforms[NUM_UNIFORMS*mNumEffects+7] =-((Static2D)center).getY()+mObjHalfY;
       }
 
     return addBase(eln);
diff --git a/src/main/res/raw/main_vertex_shader.glsl b/src/main/res/raw/main_vertex_shader.glsl
index f8d610e..0d17155 100644
--- a/src/main/res/raw/main_vertex_shader.glsl
+++ b/src/main/res/raw/main_vertex_shader.glsl
@@ -40,9 +40,9 @@ uniform int vNumEffects;                  // total number of vertex effects
 
 #if NUM_VERTEX>0
 uniform int vType[NUM_VERTEX];            // their types.
-uniform vec3 vUniforms[3*NUM_VERTEX];     // i-th effect is 3 consecutive vec3's: [3*i], [3*i+1], [3*i+2]. 
-                                          // The first 3 floats are the Interpolated values,
-                                          // next 4 are the Region, next 2 are the Center.
+uniform vec4 vUniforms[3*NUM_VERTEX];     // i-th effect is 3 consecutive vec4's: [3*i], [3*i+1], [3*i+2].
+                                          // The first vec4 is the Interpolated values,
+                                          // next is half cache half Center, the third -  the Region.
 #endif
 
 #if NUM_VERTEX>0
@@ -93,7 +93,7 @@ float degree_bitmap(in vec2 S, in vec2 PS)
 // where a = PS*PO/|PS| but we are really looking for d = |PX|/(|PX|+|PS|) = 1/(1+ (|PS|/|PX|) ) and
 // |PX|/|PS| = -b + sqrt(b^2 + (OX^2-PO^2)/PS^2) where b=PS*PO/|PS|^2 which can be computed with only one sqrt.
 
-float degree_region(in vec3 region, in vec2 PS)
+float degree_region(in vec4 region, in vec2 PS)
   {
   vec2 PO  = PS + region.xy;
   float D = region.z*region.z-dot(PO,PO);      // D = |OX|^2 - |PO|^2
@@ -118,7 +118,7 @@ float degree_region(in vec3 region, in vec2 PS)
 //////////////////////////////////////////////////////////////////////////////////////////////
 // return min(degree_bitmap,degree_region). Just like degree_region, currently only supports circles.
 
-float degree(in vec3 region, in vec2 S, in vec2 PS)
+float degree(in vec4 region, in vec2 S, in vec2 PS)
   {
   vec2 PO  = PS + region.xy;
   float D = region.z*region.z-dot(PO,PO);      // D = |OX|^2 - |PO|^2
@@ -205,7 +205,7 @@ void restrict(inout float v)
   
 void deform(in int effect, inout vec4 v)
   {
-  vec2 center = vUniforms[effect+2].yz;
+  vec2 center = vUniforms[effect+1].zw;
   vec2 force = vUniforms[effect].xy;    // force = vec(MM')
   vec2 vert_vec, horz_vec; 
   vec2 signXY = sign(center-v.xy);
@@ -283,9 +283,9 @@ void deform(in int effect, inout vec4 v)
         
 void distort(in int effect, inout vec4 v, inout vec4 n)
   {
-  vec2 point = vUniforms[effect+2].yz;
-  vec2 ps = point-v.xy;
-  float d = degree(vUniforms[effect+1],point,ps);
+  vec2 center = vUniforms[effect+1].zw;
+  vec2 ps = center-v.xy;
+  float d = degree(vUniforms[effect+2],center,ps);
   vec2 w = vec2(vUniforms[effect].x, -vUniforms[effect].y);
   float uz = vUniforms[effect].z;                                         // height of the bubble
   float denominator = dot(ps+(1.0-d)*w,ps);
@@ -312,10 +312,10 @@ void distort(in int effect, inout vec4 v, inout vec4 n)
  
 void sink(in int effect,inout vec4 v)
   {
-  vec2 point = vUniforms[effect+2].yz;
-  vec2 ps = point-v.xy;
+  vec2 center = vUniforms[effect+1].zw;
+  vec2 ps = center-v.xy;
   float h = vUniforms[effect].x;
-  float t = degree(vUniforms[effect+1],point,ps) * (1.0-h)/max(1.0,h);                                                                        
+  float t = degree(vUniforms[effect+2],center,ps) * (1.0-h)/max(1.0,h);
   
   v.xy += t*ps;           
   }
@@ -329,18 +329,42 @@ void sink(in int effect,inout vec4 v)
 
 void swirl(in int effect, inout vec4 v)
   {
-  vec2 S  = vUniforms[effect+2].yz;
-  vec2 PS = S-v.xy;
-  vec3 SO = vUniforms[effect+1];
+  vec2 center  = vUniforms[effect+1].zw;
+  vec2 PS = center-v.xy;
+  vec4 SO = vUniforms[effect+2];
   float d1_circle = degree_region(SO,PS);
-  float d1_bitmap = degree_bitmap(S,PS);
-  float sinA = vUniforms[effect].y;                            // sin(A) precomputed in EffectListVertex.postprocess
-  float cosA = vUniforms[effect].z;                            // cos(A) precomputed in EffectListVertex.postprocess
-  vec2 PS2 = vec2( PS.x*cosA+PS.y*sinA,-PS.x*sinA+PS.y*cosA ); // vector PS rotated by A radians clockwise around S.
-  vec3 SG = (1.0-d1_circle)*SO;                                // coordinates of the dilated circle P is going to get rotated around
-  float d2 = max(0.0,degree(SG,S,PS2));                        // make it a max(0,deg) because otherwise when S=left edge of the
+  float d1_bitmap = degree_bitmap(center,PS);
+  float sinA = vUniforms[effect+1].x;                          // sin(A) precomputed in EffectListVertex.postprocess
+  float cosA = vUniforms[effect+1].y;                          // cos(A) precomputed in EffectListVertex.postprocess
+  vec2 PS2 = vec2( PS.x*cosA+PS.y*sinA,-PS.x*sinA+PS.y*cosA ); // vector PS rotated by A radians clockwise around center.
+  vec4 SG = (1.0-d1_circle)*SO;                                // coordinates of the dilated circle P is going to get rotated around
+  float d2 = max(0.0,degree(SG,center,PS2));                   // make it a max(0,deg) because otherwise when center=left edge of the
                                                                // bitmap some points end up with d2<0 and they disappear off view.
-  v.xy += min(d1_circle,d1_bitmap)*(PS - PS2/(1.0-d2));        // if d2=1 (i.e P=S) we should have P unchanged. How to do it?
+  v.xy += min(d1_circle,d1_bitmap)*(PS - PS2/(1.0-d2));        // if d2=1 (i.e P=center) we should have P unchanged. How to do it?
+  }
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// WAVE EFFECT
+//
+// Directional sinusoidal wave effect.
+
+void wave(in int effect, inout vec4 v)
+  {
+  vec2 center = vUniforms[effect+1].zw;
+  vec2 ps = center-v.xy;
+  float deg = degree_region(vUniforms[effect+2],ps);
+  vec2 sincos = vUniforms[effect+1].xy;
+
+  float amplitude = vUniforms[effect].x;
+  float angle     = vUniforms[effect].y;
+  float length    = vUniforms[effect].z;
+  float d = dot( vec2(-ps.y,-ps.x),sincos );
+  float num = length==0.0 ? 1.0 : d / length;
+  float floornum = floor(num);
+  float therest = num-floornum;
+  float phi = mod(floornum,2.0) == 0.0 ? 1.0-therest:therest;
+
+  v.xy += (phi*amplitude*deg*sincos);
   }
 
 #endif
@@ -359,6 +383,7 @@ void main()
     else if( vType[i]==DEFORM ) deform (3*i,v);
     else if( vType[i]==SINK   ) sink   (3*i,v);
     else if( vType[i]==SWIRL  ) swirl  (3*i,v);
+    else if( vType[i]==WAVE   ) wave   (3*i,v);
     }
  
   restrict(v.z);  
