Project

General

Profile

« Previous | Next » 

Revision 7cd24173

Added by Leszek Koltunski over 7 years ago

Move all the knowledge about Vertex and Fragment effects to their respective classes.

View differences:

src/main/java/org/distorted/library/effect/FragmentEffect.java
26 26
public abstract class FragmentEffect extends Effect
27 27
  {
28 28
  public static final int NUM_UNIFORMS = 8; // 4-per effect interpolated values, 4 dimensional Region.
29
  private static String mGLSL = "";
30
  private static int mNumEnabled = 0;
29 31

  
30 32
///////////////////////////////////////////////////////////////////////////////////////////////////
31 33

  
......
33 35
    {
34 36
    super(name);
35 37
    }
38

  
39
///////////////////////////////////////////////////////////////////////////////////////////////////
40

  
41
  static void addEffect(EffectName not_smooth, EffectName yes_smooth, String code)
42
    {
43
    mNumEnabled ++;
44

  
45
    mGLSL +=
46

  
47
         "if( fName[i]=="+not_smooth.ordinal()+")\n"
48
        +  "{\n"
49
        +  "degree = sign(degree); \n"
50
        +   code +"\n"
51
        +  "}\n"
52
        +"else\n"
53
        +"if( fName[i]=="+yes_smooth.ordinal()+")\n"
54
        +  "{\n"
55
        +   code +"\n"
56
        +  "}\n"
57
        +"else\n";
58
    }
59

  
60
///////////////////////////////////////////////////////////////////////////////////////////////////
61

  
62
  public static String getGLSL()
63
    {
64
    return mGLSL + "{}";
65
    }
66

  
67
///////////////////////////////////////////////////////////////////////////////////////////////////
68

  
69
  public static int getNumEnabled()
70
    {
71
    return mNumEnabled;
72
    }
73

  
74
///////////////////////////////////////////////////////////////////////////////////////////////////
75

  
76
  public static void onDestroy()
77
    {
78
    mNumEnabled = 0;
79
    mGLSL = "";
80
    }
36 81
  }
src/main/java/org/distorted/library/effect/FragmentEffectAlpha.java
67 67
    mRegion.get(uniforms, index+4, currentDuration, step);
68 68
    return mAlpha.get(uniforms,index, currentDuration, step);
69 69
    }
70

  
71
///////////////////////////////////////////////////////////////////////////////////////////////////
72

  
73
  public static void enable()
74
    {
75
    addEffect( EffectName.ALPHA,EffectName.SMOOTH_ALPHA,
76
               "color.a *= (degree*(fUniforms[effect].x-1.0)+1.0);" );
77
    }
70 78
  }
src/main/java/org/distorted/library/effect/FragmentEffectBrightness.java
65 65
    mRegion.get(uniforms,index+4,currentDuration,step);
66 66
    return mBrightness.get(uniforms,index,currentDuration,step);
67 67
    }
68

  
69
///////////////////////////////////////////////////////////////////////////////////////////////////
70

  
71
  public static void enable()
72
    {
73
    addEffect( EffectName.BRIGHTNESS,EffectName.SMOOTH_BRIGHTNESS,
74
               "color.rgb = mix(vec3(0.0,0.0,0.0), color.rgb, degree*(fUniforms[effect].x-1.0)+1.0 );" );
75
    }
68 76
  }
src/main/java/org/distorted/library/effect/FragmentEffectChroma.java
76 76
    mColor.get(uniforms,index+1,currentDuration,step);
77 77
    return mBlend.get(uniforms,index,currentDuration,step);
78 78
    }
79

  
80
///////////////////////////////////////////////////////////////////////////////////////////////////
81

  
82
  public static void enable()
83
    {
84
    addEffect( EffectName.CHROMA,EffectName.SMOOTH_CHROMA,
85
               "color.rgb = mix(color.rgb, fUniforms[effect].yzw, degree*fUniforms[effect].x);" );
86
    }
79 87
  }
src/main/java/org/distorted/library/effect/FragmentEffectContrast.java
65 65
    mRegion.get(uniforms,index+4,currentDuration,step);
66 66
    return mContrast.get(uniforms,index,currentDuration,step);
67 67
    }
68

  
69
///////////////////////////////////////////////////////////////////////////////////////////////////
70

  
71
  public static void enable()
72
    {
73
    addEffect( EffectName.CONTRAST,EffectName.SMOOTH_CONTRAST,
74
               "color.rgb = mix(vec3(0.5,0.5,0.5), color.rgb, degree*(fUniforms[effect].x-1.0)+1.0 );" );
75
    }
68 76
  }
src/main/java/org/distorted/library/effect/FragmentEffectSaturation.java
67 67
    mRegion.get(uniforms,index+4,currentDuration,step);
68 68
    return mSaturation.get(uniforms,index,currentDuration,step);
69 69
    }
70

  
71
///////////////////////////////////////////////////////////////////////////////////////////////////
72

  
73
  public static void enable()
74
    {
75
    addEffect( EffectName.SATURATION,EffectName.SMOOTH_SATURATION,
76
               "float luminance = dot(vec3( 0.2125, 0.7154, 0.0721 ),color.rgb);\n" +
77
               "color.rgb = mix(vec3(luminance,luminance,luminance), color.rgb, degree*(fUniforms[effect].x-1.0)+1.0 ); " );
78
    }
70 79
  }
src/main/java/org/distorted/library/effect/PostprocessEffectBlur.java
86 86
    mBlurRadius = blurRadius;
87 87
    }
88 88

  
89
///////////////////////////////////////////////////////////////////////////////////////////////////
90

  
91
  public static void enable()
92
    {
93

  
94
    }
95

  
89 96
///////////////////////////////////////////////////////////////////////////////////////////////////
90 97

  
91 98
  public boolean compute(float[] uniforms, int index, long currentDuration, long step )
src/main/java/org/distorted/library/effect/PostprocessEffectGlow.java
30 30
  private Data1D mGlowRadius;
31 31
  private Data4D mColor;
32 32

  
33
///////////////////////////////////////////////////////////////////////////////////////////////////
34

  
35
  public static void enable()
36
    {
37

  
38
    }
39

  
33 40
///////////////////////////////////////////////////////////////////////////////////////////////////
34 41
/**
35 42
 * Make the object glow with a specific color and a halo of specific radius.
src/main/java/org/distorted/library/effect/VertexEffect.java
25 25
public abstract class VertexEffect extends Effect
26 26
  {
27 27
  public static final int NUM_UNIFORMS = 12; // 5 per-effect interpolated values, 3-dimensional center, 4-dimensional Region
28
  private static String mGLSL = "";
29
  private static int mNumEnabled = 0;
28 30

  
29 31
///////////////////////////////////////////////////////////////////////////////////////////////////
30 32

  
......
32 34
    {
33 35
    super(name);
34 36
    }
37

  
38
///////////////////////////////////////////////////////////////////////////////////////////////////
39

  
40
  static void addEffect(EffectName name, String code)
41
    {
42
    mNumEnabled ++;
43

  
44
    mGLSL +=
45

  
46
        "if( vName[i]=="+name.ordinal()+")\n" +
47
          "{\n" +
48
           code +"\n" +
49
          "}\n" +
50
        "else\n";
51
    }
52

  
53
///////////////////////////////////////////////////////////////////////////////////////////////////
54

  
55
  public static String getGLSL()
56
    {
57
    return mGLSL + "{}";
58
    }
59

  
60
///////////////////////////////////////////////////////////////////////////////////////////////////
61

  
62
  public static int getNumEnabled()
63
    {
64
    return mNumEnabled;
65
    }
66

  
67
///////////////////////////////////////////////////////////////////////////////////////////////////
68

  
69
  public static void onDestroy()
70
    {
71
    mNumEnabled = 0;
72
    mGLSL = "";
73
    }
35 74
  }
src/main/java/org/distorted/library/effect/VertexEffectDeform.java
75 75

  
76 76
    return ret;
77 77
    }
78

  
79
///////////////////////////////////////////////////////////////////////////////////////////////////
80
// Deform the whole shape of the Object by force V. Algorithm is as follows:
81
//
82
// Suppose we apply force (Vx,Vy) at point (Cx,Cy) (i.e. the center of the effect). Then, first of all,
83
// divide the rectangle into 4 smaller rectangles along the 1 horizontal + 1 vertical lines that pass
84
// through (Cx,Cy). Now suppose we have already understood the following case:
85
//
86
// A vertical (0,Vy) force applied to a rectangle (WxH) in size, at center which is the top-left corner
87
// of the rectangle.  (*)
88
//
89
// If we understand (*), then we understand everything, because in order to compute the movement of the
90
// whole rectangle we can apply (*) 8 times: for each one of the 4 sub-rectangles, apply (*) twice,
91
// once for the vertical component of the force vector, the second time for the horizontal one.
92
//
93
// Let's then compute (*):
94
// 1) the top-left point will move by exactly (0,Vy)
95
// 2) we arbitrarily decide that the top-right point will move by (|Vy|/(|Vy|+A*W))*Vy, where A is some
96
//    arbitrary constant (const float A below). The F(V,W) = (|Vy|/(|Vy|+A*W)) comes from the following:
97
//    a) we want F(V,0) = 1
98
//    b) we want lim V->inf (F) = 1
99
//    c) we actually want F() to only depend on W/V, which we have here.
100
// 3) then the top edge of the rectangle will move along the line Vy*G(x), where G(x) = (1 - (A*W/(|Vy|+A*W))*(x/W)^2)
101
// 4) Now we decide that the left edge of the rectangle will move along Vy*H(y), where H(y) = (1 - |y|/(|Vy|+C*|y|))
102
//    where C is again an arbitrary constant. Again, H(y) comes from the requirement that no matter how
103
//    strong we push the left edge of the rectangle up or down, it can never 'go over itself', but its
104
//    length will approach 0 if squeezed very hard.
105
// 5) The last point we need to compute is the left-right motion of the top-right corner (i.e. if we push
106
//    the top-left corner up very hard, we want to have the top-right corner not only move up, but also to
107
//    the left at least a little bit).
108
//    We arbitrarily decide that, in addition to moving up-down by Vy*F(V,W), the corner will also move
109
//    left-right by I(V,W) = B*W*F(V,W), where B is again an arbitrary constant.
110
// 6) combining 3), 4) and 5) together, we arrive at a movement of an arbitrary point (x,y) away from the
111
//    top-left corner:
112
//    X(x,y) = -B*x * (|Vy|/(|Vy|+A*W)) * (1-(y/H)^2)                               (**)
113
//    Y(x,y) = Vy * (1 - |y|/(|Vy|+C*|y|)) * (1 - (A*W/(|Vy|+A*W))*(x/W)^2)         (**)
114
//
115
// We notice that formulas (**) have been construed so that it is possible to continously mirror them
116
// left-right and up-down (i.e. apply not only to the 'bottom-right' rectangle of the 4 subrectangles
117
// but to all 4 of them!).
118
//
119
// Constants:
120
// a) A : valid values: (0,infinity). 'Bendiness' if the surface - the higher A is, the more the surface
121
//        bends. A<=0 destroys the system.
122
// b) B : valid values: <-1,1>. The amount side edges get 'sucked' inwards when we pull the middle of the
123
//        top edge up. B=0 --> not at all, B=1: a looot. B=-0.5: the edges will actually be pushed outwards
124
//        quite a bit. One can also set it to <-1 or >1, but it will look a bit ridiculous.
125
// c) C : valid values: <1,infinity). The derivative of the H(y) function at 0, i.e. the rate of 'squeeze'
126
//        surface gets along the force line. C=1: our point gets pulled very closely to points above it
127
//        even when we apply only small vertical force to it. The higher C is, the more 'uniform' movement
128
//        along the force line is.
129
//        0<=C<1 looks completely ridiculous and C<0 destroys the system.
130

  
131
  public static void enable()
132
    {
133
    addEffect( EffectName.DEFORM,
134

  
135
        "const vec2 ONE = vec2(1.0,1.0);  \n"
136
      + "const float A = 0.5; \n"
137
      + "const float B = 0.2; \n"
138
      + "const float C = 5.0; \n"
139

  
140
      + "vec2 center = vUniforms[effect+1].yz; \n"
141
      + "vec2 ps     = center-v.xy; \n"
142
      + "vec2 aPS    = abs(ps); \n"
143
      + "vec2 maxps  = u_objD.xy + abs(center); \n"
144
      + "float d     = degree_region(vUniforms[effect+2],ps); \n"
145
      + "vec3 force  = vUniforms[effect].xyz * d; \n"
146
      + "vec2 aForce = abs(force.xy); \n"
147
      + "float denom = dot(ps+(1.0-d)*force.xy,ps); \n"
148
      + "float one_over_denom = 1.0/(denom-0.001*(sign(denom)-1.0)); \n"
149
      + "vec2 Aw = A*maxps; \n"
150
      + "vec2 quot = ps / maxps; \n"
151
      + "quot = quot*quot; \n"                          // ( (x/W)^2 , (y/H)^2 ) where x,y are distances from V to center
152

  
153
      + "float denomV = 1.0 / (aForce.y + Aw.x); \n"
154
      + "float denomH = 1.0 / (aForce.x + Aw.y); \n"
155

  
156
      + "vec2 vertCorr= ONE - aPS / ( aForce+C*aPS + (ONE-sign(aForce)) ); \n" // avoid division by 0 when force and PS both are 0
157

  
158
      + "float mvXvert = -B * ps.x * aForce.y * (1.0-quot.y) * denomV; \n"     // impact the vertical   component of the force vector has on horizontal movement
159
      + "float mvYhorz = -B * ps.y * aForce.x * (1.0-quot.x) * denomH; \n"     // impact the horizontal component of the force vector has on vertical   movement
160
      + "float mvYvert =  force.y * (1.0-quot.x*Aw.x*denomV) * vertCorr.y; \n" // impact the vertical   component of the force vector has on vertical   movement
161
      + "float mvXhorz = -force.x * (1.0-quot.y*Aw.y*denomH) * vertCorr.x; \n" // impact the horizontal component of the force vector has on horizontal movement
162

  
163
      + "v.x -= (mvXvert+mvXhorz); \n"
164
      + "v.y -= (mvYvert+mvYhorz); \n"
165

  
166
      + "v.z += force.z*d*d*(3.0*d*d -8.0*d +6.0); \n"                         // thick bubble
167
      + "float b = -(12.0*force.z*d*(1.0-d)*(1.0-d)*(1.0-d))*one_over_denom; \n"
168

  
169
      + "n.xy += n.z*b*ps;"
170
      );
171
    }
78 172
  }
src/main/java/org/distorted/library/effect/VertexEffectDistort.java
74 74

  
75 75
    return ret;
76 76
    }
77

  
78
///////////////////////////////////////////////////////////////////////////////////////////////////
79
// Point (Px,Py) gets moved by vector (Wx,Wy,Wz) where Wx/Wy = Vx/Vy i.e. Wx=aVx and Wy=aVy where
80
// a=Py/Sy (N --> when (Px,Py) is above (Sx,Sy)) or a=Px/Sx (W) or a=(w-Px)/(w-Sx) (E) or a=(h-Py)/(h-Sy) (S)
81
// It remains to be computed which of the N,W,E or S case we have: answer: a = min[ Px/Sx , Py/Sy , (w-Px)/(w-Sx) , (h-Py)/(h-Sy) ]
82
// Computations above are valid for screen (0,0)x(w,h) but here we have (-w/2,-h/2)x(w/2,h/2)
83
//
84
// the vertical part
85
// Let |(v.x,v.y),(ux,uy)| = |PS|, ux-v.x=dx,uy-v.y=dy, f(x) (0<=x<=|SX|) be the shape of the side of the bubble.
86
// H(v.x,v.y) = |PS|>|SX| ? 0 : f(|PX|)
87
// N(v.x,v.y) = |PS|>|SX| ? (0,0,1) : ( -(dx/|PS|)sin(beta), -(dy/|PS|)sin(beta), cos(beta) ) where tan(beta) is f'(|PX|)
88
// ( i.e. normalize( dx, dy, -|PS|/f'(|PX|))
89
//
90
// Now we also have to take into account the effect horizontal move by V=(u_dVx[i],u_dVy[i]) will have on the normal vector.
91
// Solution:
92
// 1. Decompose the V into two subcomponents, one parallel to SX and another perpendicular.
93
// 2. Convince yourself (draw!) that the perpendicular component has no effect on normals.
94
// 3. The parallel component changes the length of |SX| by the factor of a=(|SX|-|Vpar|)/|SX| (where the length
95
//    can be negative depending on the direction)
96
// 4. that in turn leaves the x and y parts of the normal unchanged and multiplies the z component by a!
97
//
98
// |Vpar| = (u_dVx[i]*dx - u_dVy[i]*dy) / sqrt(ps_sq) = (Vx*dx-Vy*dy)/ sqrt(ps_sq)  (-Vy because y is inverted)
99
// a =  (|SX| - |Vpar|)/|SX| = 1 - |Vpar|/((sqrt(ps_sq)/(1-d)) = 1 - (1-d)*|Vpar|/sqrt(ps_sq) = 1-(1-d)*(Vx*dx-Vy*dy)/ps_sq
100
//
101
// Side of the bubble
102
//
103
// choose from one of the three bubble shapes: the cone, the thin bubble and the thick bubble
104
// Case 1:
105
// f(t) = t, i.e. f(x) = uz * x/|SX|   (a cone)
106
// -|PS|/f'(|PX|) = -|PS|*|SX|/uz but since ps_sq=|PS|^2 and d=|PX|/|SX| then |PS|*|SX| = ps_sq/(1-d)
107
// so finally -|PS|/f'(|PX|) = -ps_sq/(uz*(1-d))
108
//
109
// Case 2:
110
// f(t) = 3t^2 - 2t^3 --> f(0)=0, f'(0)=0, f'(1)=0, f(1)=1 (the bell curve)
111
// here we have t = x/|SX| which makes f'(|PX|) = 6*uz*|PS|*|PX|/|SX|^3.
112
// so -|PS|/f'(|PX|) = (-|SX|^3)/(6uz|PX|) =  (-|SX|^2) / (6*uz*d) but
113
// d = |PX|/|SX| and ps_sq = |PS|^2 so |SX|^2 = ps_sq/(1-d)^2
114
// so finally -|PS|/f'(|PX|) = -ps_sq/ (6uz*d*(1-d)^2)
115
//
116
// Case 3:
117
// f(t) = 3t^4-8t^3+6t^2 would be better as this satisfies f(0)=0, f'(0)=0, f'(1)=0, f(1)=1,
118
// f(0.5)=0.7 and f'(t)= t(t-1)^2 >=0 for t>=0 so this produces a fuller, thicker bubble!
119
// then -|PS|/f'(|PX|) = (-|PS|*|SX)) / (12uz*d*(d-1)^2) but |PS|*|SX| = ps_sq/(1-d) (see above!)
120
// so finally -|PS|/f'(|PX|) = -ps_sq/ (12uz*d*(1-d)^3)
121
//
122
// Now, new requirement: we have to be able to add up normal vectors, i.e. distort already distorted surfaces.
123
// If a surface is given by z = f(x,y), then the normal vector at (x0,y0) is given by (-df/dx (x0,y0), -df/dy (x0,y0), 1 ).
124
// so if we have two surfaces defined by f1(x,y) and f2(x,y) with their normals expressed as (f1x,f1y,1) and (f2x,f2y,1)
125
// then the normal to g = f1+f2 is simply given by (f1x+f2x,f1y+f2y,1), i.e. if the third components are equal, then we
126
// can simply add up the first and second components.
127
//
128
// Thus we actually want to compute N(v.x,v.y) = a*(-(dx/|PS|)*f'(|PX|), -(dy/|PS|)*f'(|PX|), 1) and keep adding
129
// the first two components. (a is the horizontal part)
130

  
131
  public static void enable()
132
    {
133
    addEffect(EffectName.DISTORT,
134

  
135
        "vec2 center = vUniforms[effect+1].yz; \n"
136
      + "vec2 ps = center-v.xy; \n"
137
      + "vec3 force = vUniforms[effect].xyz; \n"
138
      + "float d = degree(vUniforms[effect+2],center,ps); \n"
139
      + "float denom = dot(ps+(1.0-d)*force.xy,ps); \n"
140
      + "float one_over_denom = 1.0/(denom-0.001*(sign(denom)-1.0)); \n"          // = denom==0 ? 1000:1/denom;
141

  
142
       //v.z += force.z*d;                                                        // cone
143
       //b = -(force.z*(1.0-d))*one_over_denom;                                   //
144

  
145
       //v.z += force.z*d*d*(3.0-2.0*d);                                          // thin bubble
146
       //b = -(6.0*force.z*d*(1.0-d)*(1.0-d))*one_over_denom;                     //
147

  
148
      + "v.z += force.z*d*d*(3.0*d*d -8.0*d +6.0); \n"                            // thick bubble
149
      + "float b = -(12.0*force.z*d*(1.0-d)*(1.0-d)*(1.0-d))*one_over_denom; \n"  //
150

  
151
      + "v.xy += d*force.xy; \n"
152
      + "n.xy += n.z*b*ps;"
153
      );
154
    }
77 155
  }
78 156

  
79 157

  
src/main/java/org/distorted/library/effect/VertexEffectPinch.java
78 78

  
79 79
    return ret;
80 80
    }
81

  
82
///////////////////////////////////////////////////////////////////////////////////////////////////
83
// Pull P=(v.x,v.y) towards the line that
84
// a) passes through the center of the effect
85
// b) forms angle defined in the 2nd interpolated value with the X-axis
86
// with P' = P + (1-h)*dist(line to P)
87
// when h>1 we are pushing points away from S: P' = P + (1/h-1)*dist(line to P)
88

  
89
  public static void enable()
90
    {
91
    addEffect(EffectName.PINCH,
92

  
93
        "vec2 center = vUniforms[effect+1].yz; \n"
94
      + "vec2 ps = center-v.xy; \n"
95
      + "float h = vUniforms[effect].x; \n"
96
      + "float t = degree(vUniforms[effect+2],center,ps) * (1.0-h)/max(1.0,h); \n"
97
      + "float angle = vUniforms[effect].y; \n"
98
      + "vec2 dir = vec2(sin(angle),-cos(angle)); \n"
99
      + "v.xy += t*dot(ps,dir)*dir;"
100
      );
101
    }
81 102
  }
src/main/java/org/distorted/library/effect/VertexEffectSink.java
77 77

  
78 78
    return ret;
79 79
    }
80

  
81
///////////////////////////////////////////////////////////////////////////////////////////////////
82
// Pull P=(v.x,v.y) towards center of the effect with P' = P + (1-h)*dist(S-P)
83
// when h>1 we are pushing points away from S: P' = P + (1/h-1)*dist(S-P)
84

  
85
  public static void enable()
86
    {
87
    addEffect(EffectName.SINK,
88

  
89
        "vec2 center = vUniforms[effect+1].yz; \n"
90
      + "vec2 ps = center-v.xy; \n"
91
      + "float h = vUniforms[effect].x; \n"
92
      + "float t = degree(vUniforms[effect+2],center,ps) * (1.0-h)/max(1.0,h); \n"
93

  
94
      + "v.xy += t*ps;"
95
      );
96
    }
80 97
  }
src/main/java/org/distorted/library/effect/VertexEffectSwirl.java
76 76

  
77 77
    return ret;
78 78
    }
79

  
80
///////////////////////////////////////////////////////////////////////////////////////////////////
81
// Let d be the degree of the current vertex V with respect to center of the effect S and Region vRegion.
82
// This effect rotates the current vertex V by vInterpolated.x radians clockwise around the circle dilated
83
// by (1-d) around the center of the effect S.
84

  
85
  public static void enable()
86
    {
87
    addEffect(EffectName.SWIRL,
88

  
89
        "vec2 center  = vUniforms[effect+1].yz; \n"
90
      + "vec2 PS = center-v.xy; \n"
91
      + "vec4 SO = vUniforms[effect+2]; \n"
92
      + "float d1_circle = degree_region(SO,PS); \n"
93
      + "float d1_bitmap = degree_bitmap(center,PS); \n"
94

  
95
      + "float alpha = vUniforms[effect].x; \n"
96
      + "float sinA = sin(alpha); \n"
97
      + "float cosA = cos(alpha); \n"
98

  
99
      + "vec2 PS2 = vec2( PS.x*cosA+PS.y*sinA,-PS.x*sinA+PS.y*cosA ); \n" // vector PS rotated by A radians clockwise around center.
100
      + "vec4 SG = (1.0-d1_circle)*SO; \n"                                // coordinates of the dilated circle P is going to get rotated around
101
      + "float d2 = max(0.0,degree(SG,center,PS2)); \n"                   // make it a max(0,deg) because otherwise when center=left edge of the
102
                                                                          // bitmap some points end up with d2<0 and they disappear off view.
103
      + "v.xy += min(d1_circle,d1_bitmap)*(PS - PS2/(1.0-d2)); \n"        // if d2=1 (i.e P=center) we should have P unchanged. How to do it?
104
      );
105
    }
79 106
  }
80 107

  
src/main/java/org/distorted/library/effect/VertexEffectWave.java
99 99

  
100 100
    return ret;
101 101
    }
102

  
103
///////////////////////////////////////////////////////////////////////////////////////////////////
104
// Directional sinusoidal wave effect.
105
//
106
// This is an effect from a (hopefully!) generic family of effects of the form (vec3 V: |V|=1 , f(x,y) )  (*)
107
// i.e. effects defined by a unit vector and an arbitrary function. Those effects are defined to move each
108
// point (x,y,0) of the XY plane to the point (x,y,0) + V*f(x,y).
109
//
110
// In this case V is defined by angles A and B (sines and cosines of which are precomputed in
111
// EffectQueueVertex and passed in the uniforms).
112
// Let's move V to start at the origin O, let point C be the endpoint of V, and let C' be C's projection
113
// to the XY plane. Then A is defined to be the angle C0C' and angle B is the angle C'O(axisY).
114
//
115
// Also, in this case f(x,y) = amplitude*sin(x/length), with those 2 parameters passed in uniforms.
116
//
117
//////////////////////////////////////////////////////////////////////////////////////////////
118
// How to compute any generic effect of type (*)
119
//////////////////////////////////////////////////////////////////////////////////////////////
120
//
121
// By definition, the vertices move by f(x,y)*V.
122
//
123
// Normals are much more complicated.
124
// Let angle X be the angle (0,Vy,Vz)(0,Vy,0)(Vx,Vy,Vz).
125
// Let angle Y be the angle (Vx,0,Vz)(Vx,0,0)(Vx,Vy,Vz).
126
//
127
// Then it can be shown that the resulting surface, at point to which point (x0,y0,0) got moved to,
128
// has 2 tangent vectors given by
129
//
130
// SX = (1.0+cosX*fx , cosY*sinX*fx , |sinY|*sinX*fx);  (**)
131
// SY = (cosX*sinY*fy , 1.0+cosY*fy , |sinX|*sinY*fy);  (***)
132
//
133
// and then obviously the normal N is given by N= SX x SY .
134
//
135
// We still need to remember the note from the distort function about adding up normals:
136
// we first need to 'normalize' the normals to make their third components equal, and then we
137
// simply add up the first and the second component while leaving the third unchanged.
138
//
139
// How to see facts (**) and (***) ? Briefly:
140
// a) compute the 2D analogon and conclude that in this case the tangent SX is given by
141
//    SX = ( cosA*f'(x) +1, sinA*f'(x) )    (where A is the angle vector V makes with X axis )
142
// b) cut the resulting surface with plane P which
143
//    - includes vector V
144
//    - crosses plane XY along line parallel to X axis
145
// c) apply the 2D analogon and notice that the tangent vector to the curve that is the common part of P
146
//    and our surface (I am talking about the tangent vector which belongs to P) is given by
147
//    (1+cosX*fx,0,sinX*fx) rotated by angle (90-|Y|) (where angles X,Y are defined above) along vector (1,0,0).
148
//
149
//    Matrix of rotation:
150
//
151
//    |sinY|  cosY
152
//    -cosY  |sinY|
153
//
154
// d) compute the above and see that this is equal precisely to SX from (**).
155
// e) repeat points b,c,d in direction Y and come up with (***).
156
//
157
//////////////////////////////////////////////////////////////////////////////////////////////
158
// Note: we should avoid passing certain combinations of parameters to this function. One such known
159
// combination is ( A: small but positive, B: any, amplitude >= length ).
160
// In this case, certain 'unlucky' points have their normals almost horizontal (they got moved by (almost!)
161
// amplitude, and other point length (i.e. <=amplitude) away got moved by 0, so the slope in this point is
162
// very steep). Visual effect is: vast majority of surface pretty much unchanged, but random 'unlucky'
163
// points very dark)
164
//
165
// Generally speaking I'd keep to amplitude < length, as the opposite case has some other problems as well.
166

  
167
  public static void enable()
168
    {
169
    addEffect(EffectName.WAVE,
170

  
171
        "vec2 center     = vUniforms[effect+1].yz; \n"
172
      + "float amplitude = vUniforms[effect  ].x; \n"
173
      + "float length    = vUniforms[effect  ].y; \n"
174

  
175
      + "vec2 ps = center - v.xy; \n"
176
      + "float deg = amplitude*degree_region(vUniforms[effect+2],ps); \n"
177

  
178
      + "if( deg != 0.0 && length != 0.0 ) \n"
179
      +   "{ \n"
180
      +   "float phase = vUniforms[effect  ].z; \n"
181
      +   "float alpha = vUniforms[effect  ].w; \n"
182
      +   "float beta  = vUniforms[effect+1].x; \n"
183

  
184
      +   "float sinA = sin(alpha); \n"
185
      +   "float cosA = cos(alpha); \n"
186
      +   "float sinB = sin(beta); \n"
187
      +   "float cosB = cos(beta); \n"
188

  
189
      +   "float angle= 1.578*(ps.x*cosB-ps.y*sinB) / length + phase; \n"
190
      +   "vec3 dir= vec3(sinB*cosA,cosB*cosA,sinA); \n"
191
      +   "v += sin(angle)*deg*dir; \n"
192

  
193
      +   "if( n.z != 0.0 ) \n"
194
      +     "{ \n"
195
      +     "float sqrtX = sqrt(dir.y*dir.y + dir.z*dir.z); \n"
196
      +     "float sqrtY = sqrt(dir.x*dir.x + dir.z*dir.z); \n"
197

  
198
      +     "float sinX = ( sqrtY==0.0 ? 0.0 : dir.z / sqrtY); \n"
199
      +     "float cosX = ( sqrtY==0.0 ? 1.0 : dir.x / sqrtY); \n"
200
      +     "float sinY = ( sqrtX==0.0 ? 0.0 : dir.z / sqrtX); \n"
201
      +     "float cosY = ( sqrtX==0.0 ? 1.0 : dir.y / sqrtX); \n"
202

  
203
      +     "float abs_z = dir.z <0.0 ? -(sinX*sinY) : (sinX*sinY); \n"
204
      +     "float tmp = 1.578*cos(angle)*deg/length; \n"
205

  
206
      +     "float fx =-cosB*tmp; \n"
207
      +     "float fy = sinB*tmp; \n"
208

  
209
      +     "vec3 sx = vec3 (1.0+cosX*fx,cosY*sinX*fx,abs_z*fx); \n"
210
      +     "vec3 sy = vec3 (cosX*sinY*fy,1.0+cosY*fy,abs_z*fy); \n"
211

  
212
      +     "vec3 normal = cross(sx,sy); \n"
213

  
214
      +     "if( normal.z<=0.0 ) \n"                   // Why this bizarre shit rather than the straightforward
215
      +       "{ \n"                                   //
216
      +       "normal.x= 0.0; \n"                      // if( normal.z>0.0 )
217
      +       "normal.y= 0.0; \n"                      //   {
218
      +       "normal.z= 1.0; \n"                      //   n.x = (n.x*normal.z + n.z*normal.x);
219
      +       "} \n"                                   //   n.y = (n.y*normal.z + n.z*normal.y);
220
                                                       //   n.z = (n.z*normal.z);
221
                                                       //   }
222
      +     "n.x = (n.x*normal.z + n.z*normal.x); \n"  //
223
      +     "n.y = (n.y*normal.z + n.z*normal.y); \n"  // ? Because if we do the above, my shitty Nexus4 crashes
224
      +     "n.z = (n.z*normal.z); \n"                 // during shader compilation!
225
      +     "} \n"
226
      +   "}"
227
      );
228
    }
102 229
  }
src/main/java/org/distorted/library/main/Distorted.java
25 25
import android.content.res.Resources;
26 26

  
27 27
import org.distorted.library.effect.Effect;
28
import org.distorted.library.effect.FragmentEffect;
28 29
import org.distorted.library.effect.PostprocessEffectBlur;
30
import org.distorted.library.effect.VertexEffect;
29 31
import org.distorted.library.program.*;
30 32

  
31 33
///////////////////////////////////////////////////////////////////////////////////////////////////
......
152 154
    DistortedMaster.onDestroy();
153 155
    EffectQueue.onDestroy();
154 156
    Effect.onDestroy();
157
    VertexEffect.onDestroy();
158
    FragmentEffect.onDestroy();
155 159
    EffectMessageSender.stopSending();
156 160

  
157 161
    mInitialized = false;
src/main/java/org/distorted/library/main/DistortedEffects.java
26 26
import org.distorted.library.effect.Effect;
27 27
import org.distorted.library.effect.EffectName;
28 28
import org.distorted.library.effect.EffectType;
29
import org.distorted.library.effect.FragmentEffect;
30
import org.distorted.library.effect.VertexEffect;
29 31
import org.distorted.library.message.EffectListener;
30 32
import org.distorted.library.program.DistortedProgram;
31 33
import org.distorted.library.program.FragmentCompilationException;
......
54 56
  /// MAIN PROGRAM ///
55 57
  private static DistortedProgram mMainProgram;
56 58
  private static int mMainTextureH;
57
  private static boolean[] mEffectEnabled = new boolean[EffectName.LENGTH];
58

  
59
  static
60
    {
61
    int len = EffectName.LENGTH;
62
    for(int i=0; i<len; i++)
63
      {
64
      mEffectEnabled[i] = false;
65
      }
66
    }
67 59

  
68 60
  /// BLIT PROGRAM ///
69 61
  private static DistortedProgram mBlitProgram;
......
122 114
    final InputStream mainVertStream = resources.openRawResource(R.raw.main_vertex_shader);
123 115
    final InputStream mainFragStream = resources.openRawResource(R.raw.main_fragment_shader);
124 116

  
125
    String mainVertHeader= Distorted.GLSL_VERSION;
126
    String mainFragHeader= Distorted.GLSL_VERSION;
127

  
128
    EffectName name;
129
    EffectType type;
130
    boolean foundF = false;
131
    boolean foundV = false;
132

  
133
    for(int i=0; i<mEffectEnabled.length; i++)
134
      {
135
      if( mEffectEnabled[i] )
136
        {
137
        name = EffectName.getName(i);
138
        type = name.getType();
139

  
140
        if( type == EffectType.VERTEX )
141
          {
142
          mainVertHeader += ("#define "+name.name()+" "+name.ordinal()+"\n");
143
          foundV = true;
144
          }
145
        else if( type == EffectType.FRAGMENT )
146
          {
147
          mainFragHeader += ("#define "+name.name()+" "+name.ordinal()+"\n");
148
          foundF = true;
149
          }
150
        }
151
      }
117
    int numF = FragmentEffect.getNumEnabled();
118
    int numV = VertexEffect.getNumEnabled();
152 119

  
153
    mainVertHeader += ("#define NUM_VERTEX "   + ( foundV ? getMax(EffectType.VERTEX  ) : 0 ) + "\n");
154
    mainFragHeader += ("#define NUM_FRAGMENT " + ( foundF ? getMax(EffectType.FRAGMENT) : 0 ) + "\n");
120
    String mainVertHeader= Distorted.GLSL_VERSION + ("#define NUM_VERTEX "   + ( numV>0 ? getMax(EffectType.VERTEX  ) : 0 ) + "\n");
121
    String mainFragHeader= Distorted.GLSL_VERSION + ("#define NUM_FRAGMENT " + ( numF>0 ? getMax(EffectType.FRAGMENT) : 0 ) + "\n");
122
    String enabledEffectV= VertexEffect.getGLSL();
123
    String enabledEffectF= FragmentEffect.getGLSL();
155 124

  
156 125
    //android.util.Log.e("Effects", "vertHeader= "+mainVertHeader);
157 126
    //android.util.Log.e("Effects", "fragHeader= "+mainFragHeader);
127
    //android.util.Log.e("Effects", "enabledV= "+enabledEffectV);
128
    //android.util.Log.e("Effects", "enabledF= "+enabledEffectF);
158 129

  
159 130
    String[] feedback = { "v_Position", "v_endPosition" };
160 131

  
161
    mMainProgram = new DistortedProgram(mainVertStream,mainFragStream, mainVertHeader, mainFragHeader, Distorted.GLSL, feedback);
132
    mMainProgram = new DistortedProgram( mainVertStream, mainFragStream, mainVertHeader, mainFragHeader,
133
                                         enabledEffectV, enabledEffectF, Distorted.GLSL, feedback);
162 134

  
163 135
    int mainProgramH = mMainProgram.getProgramHandle();
164 136
    EffectQueueFragment.getUniforms(mainProgramH);
......
449 421
  static void onDestroy()
450 422
    {
451 423
    mNextID = 0;
452

  
453
    for(int i=0; i<EffectName.LENGTH; i++)
454
      {
455
      mEffectEnabled[i] = false;
456
      }
457 424
    }
458 425

  
459 426
///////////////////////////////////////////////////////////////////////////////////////////////////
......
622 589
      }
623 590
    }
624 591

  
625
///////////////////////////////////////////////////////////////////////////////////////////////////
626
/**
627
 * Enables a given Effect.
628
 * <p>
629
 * By default, all effects are disabled. One has to explicitly enable each effect one intends to use.
630
 * This needs to be called BEFORE shaders get compiled, i.e. before the call to Distorted.onCreate().
631
 * The point: by enabling only the effects we need, we can optimize the shaders.
632
 *
633
 * @param name one of the constants defined in {@link EffectName}
634
 */
635
  public static void enableEffect(EffectName name)
636
    {
637
    mEffectEnabled[name.ordinal()] = true;
638
    }
639

  
640 592
///////////////////////////////////////////////////////////////////////////////////////////////////
641 593
/**
642 594
 * Returns the maximum number of effects of a given type.
src/main/java/org/distorted/library/program/DistortedProgram.java
251 251
    return shaderHandle;
252 252
    }
253 253

  
254
///////////////////////////////////////////////////////////////////////////////////////////////////
255

  
256
  private static String insertEnabledEffects(String code, final String effects)
257
    {
258
    final String marker = "// ENABLED EFFECTS WILL BE INSERTED HERE";
259
    int length = marker.length();
260

  
261
    int place = code.indexOf(marker);
262

  
263
    if( place>=0 )
264
      {
265
      String begin = code.substring(0,place-1);
266
      String end   = code.substring(place+length);
267
/*
268
int len = begin.length();
269

  
270
android.util.Log.d("Program", begin.substring(len-40));
271
android.util.Log.d("Program", effects);
272
android.util.Log.d("Program", end.substring(0,40));
273
*/
274
      return begin + effects + end;
275
      }
276
    else
277
      {
278
      android.util.Log.e("Program", "Error: marker string not found in SHADER!");
279
      }
280

  
281
    return null;
282
    }
283

  
254 284
///////////////////////////////////////////////////////////////////////////////////////////////////
255 285
// feedback: List of 'out' variables (OpenGL ES >= 3.0 only!) that will be transferred back to CPU
256 286
// using Transform Feedback.
......
281 311
      }
282 312
    }
283 313

  
314
///////////////////////////////////////////////////////////////////////////////////////////////////
315
// feedback: List of 'out' variables (OpenGL ES >= 3.0 only!) that will be transferred back to CPU
316
// using Transform Feedback.
317

  
318
  public DistortedProgram(final InputStream vertex, final InputStream fragment, final String vertexHeader, final String fragmentHeader,
319
                          final String enabledVertex, final String enabledFragment, int glslVersion, final String[] feedback )
320
  throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
321
    {
322
    mAttributeStr = (glslVersion == 100 ? "attribute " : "in ");
323
    mAttributeLen = mAttributeStr.length();
324

  
325
    mNumAttributes = 0;
326

  
327
    String vertexShader   = readTextFileFromRawResource(vertex  , true );
328
    String fragmentShader = readTextFileFromRawResource(fragment, false);
329

  
330
    vertexShader   = insertEnabledEffects(vertexShader  ,enabledVertex  );
331
    fragmentShader = insertEnabledEffects(fragmentShader,enabledFragment);
332

  
333
    sanitizeMaxValues();
334

  
335
    final int vertexShaderHandle   = compileShader(GLES30.GL_VERTEX_SHADER  , vertexHeader   + vertexShader  );
336
    final int fragmentShaderHandle = compileShader(GLES30.GL_FRAGMENT_SHADER, fragmentHeader + fragmentShader);
337

  
338
    mProgramHandle = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, mAttributeName, glslVersion>= 300 ? feedback:null );
339

  
340
    mAttribute = new int[mNumAttributes];
341

  
342
    for(int i=0; i<mNumAttributes; i++)
343
      {
344
      mAttribute[i] = GLES30.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
345
      }
346
    }
347

  
284 348
///////////////////////////////////////////////////////////////////////////////////////////////////
285 349
// PUBLIC API
286 350
///////////////////////////////////////////////////////////////////////////////////////////////////
src/main/res/raw/main_fragment_shader.glsl
41 41
uniform int fName[NUM_FRAGMENT];        // their namess.
42 42
uniform vec4 fUniforms[2*NUM_FRAGMENT]; // i-th effect is 2 consecutive vec4's: [2*i], [2*i+1]. First vec4 is the Interpolated values,
43 43
                                        // next describes the Region, i.e. area over which the effect is active.
44

  
45
#if defined(SATURATION) || defined(SMOOTH_SATURATION)
46
const vec3 LUMI = vec3( 0.2125, 0.7154, 0.0721 );                                        
47
#endif
48

  
49
//////////////////////////////////////////////////////////////////////////////////////////////
50
// CHROMA EFFECT
51

  
52
#if defined(CHROMA) || defined(SMOOTH_CHROMA)
53
void chroma(float degree, int effect, inout vec4 color)
54
  {
55
  color.rgb = mix(color.rgb, fUniforms[effect].yzw, degree*fUniforms[effect].x);
56
  }
57
#endif
58

  
59
//////////////////////////////////////////////////////////////////////////////////////////////
60
// ALPHA EFFECT (change transparency level)
61

  
62
#if defined(ALPHA) || defined(SMOOTH_ALPHA)
63
void alpha(float degree, int effect, inout vec4 color)
64
  {
65
  color.a *= (degree*(fUniforms[effect].x-1.0)+1.0); 
66
  }
67
#endif
68

  
69
//////////////////////////////////////////////////////////////////////////////////////////////
70
// BRIGHTNESS EFFECT
71

  
72
#if defined(BRIGHTNESS) || defined(SMOOTH_BRIGHTNESS)
73
void brightness(float degree, int effect, inout vec4 color)
74
  {
75
  color.rgb = mix(vec3(0.0,0.0,0.0), color.rgb, degree*(fUniforms[effect].x-1.0)+1.0 ); 
76
  }
77
#endif
78

  
79
//////////////////////////////////////////////////////////////////////////////////////////////
80
// CONTRAST EFFECT
81

  
82
#if defined(CONTRAST) || defined(SMOOTH_CONTRAST)
83
void contrast(float degree, int effect, inout vec4 color)
84
  {
85
  color.rgb = mix(vec3(0.5,0.5,0.5), color.rgb, degree*(fUniforms[effect].x-1.0)+1.0 ); 
86
  }
87
#endif
88

  
89
//////////////////////////////////////////////////////////////////////////////////////////////
90
// SATURATION EFFECT
91

  
92
#if defined(SATURATION) || defined(SMOOTH_SATURATION)
93
void saturation(float degree, int effect, inout vec4 color)
94
  {
95
  float luminance = dot(LUMI,color.rgb);
96
  color.rgb = mix(vec3(luminance,luminance,luminance), color.rgb, degree*(fUniforms[effect].x-1.0)+1.0 ); 
97
  }
98
#endif
99

  
100 44
#endif    // NUM_FRAGMENT>0
101 45

  
102 46
//////////////////////////////////////////////////////////////////////////////////////////////
103 47

  
104 48
void main()                    		
105 49
  {  
106
  vec4 pixel = TEXTURE(u_Texture,v_TexCoordinate);
50
  vec4 color = TEXTURE(u_Texture,v_TexCoordinate);
107 51

  
108 52
#if NUM_FRAGMENT>0
109 53
  vec2 diff;
110
  float pointDegree;
111
  int j=0;
54
  float degree;
55
  int effect=0;
112 56

  
113 57
  for(int i=0; i<fNumEffects; i++)
114 58
    {
115
    diff = (v_Position.xy - fUniforms[j+1].xy)/fUniforms[j+1].zw;
116
    pointDegree = max(0.0,1.0-dot(diff,diff));
59
    diff   = (v_Position.xy - fUniforms[effect+1].xy)/fUniforms[effect+1].zw;
60
    degree = max(0.0,1.0-dot(diff,diff));
117 61

  
118
#ifdef CHROMA
119
    if( fName[i]==CHROMA            ) chroma    (sign(pointDegree),j,pixel); else
120
#endif
121
#ifdef SMOOTH_CHROMA
122
    if( fName[i]==SMOOTH_CHROMA     ) chroma    (     pointDegree ,j,pixel); else
123
#endif
124
#ifdef ALPHA
125
    if( fName[i]==ALPHA             ) alpha     (sign(pointDegree),j,pixel); else
126
#endif
127
#ifdef SMOOTH_ALPHA
128
    if( fName[i]==SMOOTH_ALPHA      ) alpha     (     pointDegree ,j,pixel); else
129
#endif
130
#ifdef BRIGHTNESS
131
    if( fName[i]==BRIGHTNESS        ) brightness(sign(pointDegree),j,pixel); else
132
#endif
133
#ifdef SMOOTH_BRIGHTNESS
134
    if( fName[i]==SMOOTH_BRIGHTNESS ) brightness(     pointDegree ,j,pixel); else
135
#endif
136
#ifdef CONTRAST
137
    if( fName[i]==CONTRAST          ) contrast  (sign(pointDegree),j,pixel); else
138
#endif
139
#ifdef SMOOTH_CONTRAST
140
    if( fName[i]==SMOOTH_CONTRAST   ) contrast  (     pointDegree ,j,pixel); else
141
#endif
142
#ifdef SATURATION
143
    if( fName[i]==SATURATION        ) saturation(sign(pointDegree),j,pixel); else
144
#endif
145
#ifdef SMOOTH_SATURATION
146
    if( fName[i]==SMOOTH_SATURATION ) saturation(     pointDegree ,j,pixel); else
147
#endif
148
    {}
62
    // ENABLED EFFECTS WILL BE INSERTED HERE
149 63

  
150
    j+=2;
64
    effect+=2;
151 65
    }
152 66
#endif
153 67

  
154
  FRAG_COLOR = vec4(pixel.rgb * (1.0 + 7.0*v_Normal.z) * 0.125, pixel.a);
68
  FRAG_COLOR = vec4(color.rgb * (1.0 + 7.0*v_Normal.z) * 0.125, color.a);
155 69
  }
src/main/res/raw/main_vertex_shader.glsl
144 144
  return min(1.0/(1.0 + 1.0/(sqrt(DOT*DOT+D*one_over_ps_sq)-DOT)),E);
145 145
  }
146 146

  
147
//////////////////////////////////////////////////////////////////////////////////////////////
148
// DEFORM EFFECT
149
//
150
// Deform the whole shape of the Object by force V. Algorithm is as follows:
151
//
152
// Suppose we apply force (Vx,Vy) at point (Cx,Cy) (i.e. the center of the effect). Then, first of all,
153
// divide the rectangle into 4 smaller rectangles along the 1 horizontal + 1 vertical lines that pass
154
// through (Cx,Cy). Now suppose we have already understood the following case:
155
//
156
// A vertical (0,Vy) force applied to a rectangle (WxH) in size, at center which is the top-left corner
157
// of the rectangle.  (*)
158
//
159
// If we understand (*), then we understand everything, because in order to compute the movement of the
160
// whole rectangle we can apply (*) 8 times: for each one of the 4 sub-rectangles, apply (*) twice,
161
// once for the vertical component of the force vector, the second time for the horizontal one.
162
//
163
// Let's then compute (*):
164
// 1) the top-left point will move by exactly (0,Vy)
165
// 2) we arbitrarily decide that the top-right point will move by (|Vy|/(|Vy|+A*W))*Vy, where A is some
166
//    arbitrary constant (const float A below). The F(V,W) = (|Vy|/(|Vy|+A*W)) comes from the following:
167
//    a) we want F(V,0) = 1
168
//    b) we want lim V->inf (F) = 1
169
//    c) we actually want F() to only depend on W/V, which we have here.
170
// 3) then the top edge of the rectangle will move along the line Vy*G(x), where G(x) = (1 - (A*W/(|Vy|+A*W))*(x/W)^2)
171
// 4) Now we decide that the left edge of the rectangle will move along Vy*H(y), where H(y) = (1 - |y|/(|Vy|+C*|y|))
172
//    where C is again an arbitrary constant. Again, H(y) comes from the requirement that no matter how
173
//    strong we push the left edge of the rectangle up or down, it can never 'go over itself', but its
174
//    length will approach 0 if squeezed very hard.
175
// 5) The last point we need to compute is the left-right motion of the top-right corner (i.e. if we push
176
//    the top-left corner up very hard, we want to have the top-right corner not only move up, but also to
177
//    the left at least a little bit).
178
//    We arbitrarily decide that, in addition to moving up-down by Vy*F(V,W), the corner will also move
179
//    left-right by I(V,W) = B*W*F(V,W), where B is again an arbitrary constant.
180
// 6) combining 3), 4) and 5) together, we arrive at a movement of an arbitrary point (x,y) away from the
181
//    top-left corner:
182
//    X(x,y) = -B*x * (|Vy|/(|Vy|+A*W)) * (1-(y/H)^2)                               (**)
183
//    Y(x,y) = Vy * (1 - |y|/(|Vy|+C*|y|)) * (1 - (A*W/(|Vy|+A*W))*(x/W)^2)         (**)
184
//
185
// We notice that formulas (**) have been construed so that it is possible to continously mirror them
186
// left-right and up-down (i.e. apply not only to the 'bottom-right' rectangle of the 4 subrectangles
187
// but to all 4 of them!).
188
//
189
// Constants:
190
// a) A : valid values: (0,infinity). 'Bendiness' if the surface - the higher A is, the more the surface
191
//        bends. A<=0 destroys the system.
192
// b) B : valid values: <-1,1>. The amount side edges get 'sucked' inwards when we pull the middle of the
193
//        top edge up. B=0 --> not at all, B=1: a looot. B=-0.5: the edges will actually be pushed outwards
194
//        quite a bit. One can also set it to <-1 or >1, but it will look a bit ridiculous.
195
// c) C : valid values: <1,infinity). The derivative of the H(y) function at 0, i.e. the rate of 'squeeze'
196
//        surface gets along the force line. C=1: our point gets pulled very closely to points above it
197
//        even when we apply only small vertical force to it. The higher C is, the more 'uniform' movement
198
//        along the force line is.
199
//        0<=C<1 looks completely ridiculous and C<0 destroys the system.
200

  
201
#ifdef DEFORM
202
void deform(in int effect, inout vec3 v, inout vec3 n)
203
  {
204
  const vec2 ONE = vec2(1.0,1.0);
205

  
206
  const float A = 0.5;
207
  const float B = 0.2;
208
  const float C = 5.0;
209

  
210
  vec2 center = vUniforms[effect+1].yz;
211
  vec2 ps     = center-v.xy;
212
  vec2 aPS    = abs(ps);
213
  vec2 maxps  = u_objD.xy + abs(center);
214
  float d     = degree_region(vUniforms[effect+2],ps);
215
  vec3 force  = vUniforms[effect].xyz * d;
216
  vec2 aForce = abs(force.xy);
217
  float denom = dot(ps+(1.0-d)*force.xy,ps);
218
  float one_over_denom = 1.0/(denom-0.001*(sign(denom)-1.0));
219
  vec2 Aw = A*maxps;
220
  vec2 quot = ps / maxps;
221
  quot = quot*quot;                          // ( (x/W)^2 , (y/H)^2 ) where x,y are distances from V to center
222

  
223
  float denomV = 1.0 / (aForce.y + Aw.x);
224
  float denomH = 1.0 / (aForce.x + Aw.y);
225

  
226
  vec2 vertCorr= ONE - aPS / ( aForce+C*aPS + (ONE-sign(aForce)) );  // avoid division by 0 when force and PS both are 0
227

  
228
  float mvXvert = -B * ps.x * aForce.y * (1.0-quot.y) * denomV;      // impact the vertical   component of the force vector has on horizontal movement
229
  float mvYhorz = -B * ps.y * aForce.x * (1.0-quot.x) * denomH;      // impact the horizontal component of the force vector has on vertical   movement
230
  float mvYvert =  force.y * (1.0-quot.x*Aw.x*denomV) * vertCorr.y;  // impact the vertical   component of the force vector has on vertical   movement
231
  float mvXhorz = -force.x * (1.0-quot.y*Aw.y*denomH) * vertCorr.x;  // impact the horizontal component of the force vector has on horizontal movement
232

  
233
  v.x -= (mvXvert+mvXhorz);
234
  v.y -= (mvYvert+mvYhorz);
235

  
236
  v.z += force.z*d*d*(3.0*d*d -8.0*d +6.0);                          // thick bubble
237
  float b = -(12.0*force.z*d*(1.0-d)*(1.0-d)*(1.0-d))*one_over_denom;//
238

  
239
  n.xy += n.z*b*ps;
240
  }
241
#endif
242

  
243
//////////////////////////////////////////////////////////////////////////////////////////////
244
// DISTORT EFFECT
245
//
246
// Point (Px,Py) gets moved by vector (Wx,Wy,Wz) where Wx/Wy = Vx/Vy i.e. Wx=aVx and Wy=aVy where
247
// a=Py/Sy (N --> when (Px,Py) is above (Sx,Sy)) or a=Px/Sx (W) or a=(w-Px)/(w-Sx) (E) or a=(h-Py)/(h-Sy) (S)
248
// It remains to be computed which of the N,W,E or S case we have: answer: a = min[ Px/Sx , Py/Sy , (w-Px)/(w-Sx) , (h-Py)/(h-Sy) ]
249
// Computations above are valid for screen (0,0)x(w,h) but here we have (-w/2,-h/2)x(w/2,h/2)
250
//
251
// the vertical part
252
// Let |(v.x,v.y),(ux,uy)| = |PS|, ux-v.x=dx,uy-v.y=dy, f(x) (0<=x<=|SX|) be the shape of the side of the bubble.
253
// H(v.x,v.y) = |PS|>|SX| ? 0 : f(|PX|)
254
// N(v.x,v.y) = |PS|>|SX| ? (0,0,1) : ( -(dx/|PS|)sin(beta), -(dy/|PS|)sin(beta), cos(beta) ) where tan(beta) is f'(|PX|)
255
// ( i.e. normalize( dx, dy, -|PS|/f'(|PX|))
256
//
257
// Now we also have to take into account the effect horizontal move by V=(u_dVx[i],u_dVy[i]) will have on the normal vector.
258
// Solution:
259
// 1. Decompose the V into two subcomponents, one parallel to SX and another perpendicular.
260
// 2. Convince yourself (draw!) that the perpendicular component has no effect on normals.
261
// 3. The parallel component changes the length of |SX| by the factor of a=(|SX|-|Vpar|)/|SX| (where the length
262
//    can be negative depending on the direction)
263
// 4. that in turn leaves the x and y parts of the normal unchanged and multiplies the z component by a!
264
//
265
// |Vpar| = (u_dVx[i]*dx - u_dVy[i]*dy) / sqrt(ps_sq) = (Vx*dx-Vy*dy)/ sqrt(ps_sq)  (-Vy because y is inverted)
266
// a =  (|SX| - |Vpar|)/|SX| = 1 - |Vpar|/((sqrt(ps_sq)/(1-d)) = 1 - (1-d)*|Vpar|/sqrt(ps_sq) = 1-(1-d)*(Vx*dx-Vy*dy)/ps_sq
267
//
268
// Side of the bubble
269
//
270
// choose from one of the three bubble shapes: the cone, the thin bubble and the thick bubble
271
// Case 1:
272
// f(t) = t, i.e. f(x) = uz * x/|SX|   (a cone)
273
// -|PS|/f'(|PX|) = -|PS|*|SX|/uz but since ps_sq=|PS|^2 and d=|PX|/|SX| then |PS|*|SX| = ps_sq/(1-d)
274
// so finally -|PS|/f'(|PX|) = -ps_sq/(uz*(1-d))
275
//
276
// Case 2:
277
// f(t) = 3t^2 - 2t^3 --> f(0)=0, f'(0)=0, f'(1)=0, f(1)=1 (the bell curve)
278
// here we have t = x/|SX| which makes f'(|PX|) = 6*uz*|PS|*|PX|/|SX|^3.
279
// so -|PS|/f'(|PX|) = (-|SX|^3)/(6uz|PX|) =  (-|SX|^2) / (6*uz*d) but
280
// d = |PX|/|SX| and ps_sq = |PS|^2 so |SX|^2 = ps_sq/(1-d)^2
281
// so finally -|PS|/f'(|PX|) = -ps_sq/ (6uz*d*(1-d)^2)
282
//
283
// Case 3:
284
// f(t) = 3t^4-8t^3+6t^2 would be better as this satisfies f(0)=0, f'(0)=0, f'(1)=0, f(1)=1,
285
// f(0.5)=0.7 and f'(t)= t(t-1)^2 >=0 for t>=0 so this produces a fuller, thicker bubble!
286
// then -|PS|/f'(|PX|) = (-|PS|*|SX)) / (12uz*d*(d-1)^2) but |PS|*|SX| = ps_sq/(1-d) (see above!)
287
// so finally -|PS|/f'(|PX|) = -ps_sq/ (12uz*d*(1-d)^3)
288
//
289
// Now, new requirement: we have to be able to add up normal vectors, i.e. distort already distorted surfaces.
290
// If a surface is given by z = f(x,y), then the normal vector at (x0,y0) is given by (-df/dx (x0,y0), -df/dy (x0,y0), 1 ).
291
// so if we have two surfaces defined by f1(x,y) and f2(x,y) with their normals expressed as (f1x,f1y,1) and (f2x,f2y,1)
292
// then the normal to g = f1+f2 is simply given by (f1x+f2x,f1y+f2y,1), i.e. if the third components are equal, then we
293
// can simply add up the first and second components.
294
//
295
// Thus we actually want to compute N(v.x,v.y) = a*(-(dx/|PS|)*f'(|PX|), -(dy/|PS|)*f'(|PX|), 1) and keep adding
296
// the first two components. (a is the horizontal part)
297

  
298
#ifdef DISTORT
299
void distort(in int effect, inout vec3 v, inout vec3 n)
300
  {
301
  vec2 center = vUniforms[effect+1].yz;
302
  vec2 ps = center-v.xy;
303
  vec3 force = vUniforms[effect].xyz;
304
  float d = degree(vUniforms[effect+2],center,ps);
305
  float denom = dot(ps+(1.0-d)*force.xy,ps);
306
  float one_over_denom = 1.0/(denom-0.001*(sign(denom)-1.0));          // = denom==0 ? 1000:1/denom;
307

  
308
  //v.z += force.z*d;                                                  // cone
309
  //b = -(force.z*(1.0-d))*one_over_denom;                             //
310

  
311
  //v.z += force.z*d*d*(3.0-2.0*d);                                    // thin bubble
312
  //b = -(6.0*force.z*d*(1.0-d)*(1.0-d))*one_over_denom;               //
313

  
314
  v.z += force.z*d*d*(3.0*d*d -8.0*d +6.0);                            // thick bubble
315
  float b = -(12.0*force.z*d*(1.0-d)*(1.0-d)*(1.0-d))*one_over_denom;  //
316

  
317
  v.xy += d*force.xy;
318
  n.xy += n.z*b*ps;
319
  }
320
#endif
321

  
322
//////////////////////////////////////////////////////////////////////////////////////////////
323
// SINK EFFECT
324
//
325
// Pull P=(v.x,v.y) towards center of the effect with P' = P + (1-h)*dist(S-P)
326
// when h>1 we are pushing points away from S: P' = P + (1/h-1)*dist(S-P)
327

  
328
#ifdef SINK
329
void sink(in int effect,inout vec3 v)
330
  {
331
  vec2 center = vUniforms[effect+1].yz;
332
  vec2 ps = center-v.xy;
333
  float h = vUniforms[effect].x;
334
  float t = degree(vUniforms[effect+2],center,ps) * (1.0-h)/max(1.0,h);
335

  
336
  v.xy += t*ps;
337
  }
338
#endif
339

  
340
//////////////////////////////////////////////////////////////////////////////////////////////
341
// PINCH EFFECT
342
//
343
// Pull P=(v.x,v.y) towards the line that
344
// a) passes through the center of the effect
345
// b) forms angle defined in the 2nd interpolated value with the X-axis
346
// with P' = P + (1-h)*dist(line to P)
347
// when h>1 we are pushing points away from S: P' = P + (1/h-1)*dist(line to P)
348

  
349
#ifdef PINCH
350
void pinch(in int effect,inout vec3 v)
351
  {
352
  vec2 center = vUniforms[effect+1].yz;
353
  vec2 ps = center-v.xy;
354
  float h = vUniforms[effect].x;
355
  float t = degree(vUniforms[effect+2],center,ps) * (1.0-h)/max(1.0,h);
356
  float angle = vUniforms[effect].y;
357
  vec2 dir = vec2(sin(angle),-cos(angle));
358

  
359
  v.xy += t*dot(ps,dir)*dir;
360
  }
361
#endif
362

  
363
//////////////////////////////////////////////////////////////////////////////////////////////
364
// SWIRL EFFECT
365
//
366
// Let d be the degree of the current vertex V with respect to center of the effect S and Region vRegion.
367
// This effect rotates the current vertex V by vInterpolated.x radians clockwise around the circle dilated
368
// by (1-d) around the center of the effect S.
369

  
370
#ifdef SWIRL
371
void swirl(in int effect, inout vec3 v)
372
  {
373
  vec2 center  = vUniforms[effect+1].yz;
374
  vec2 PS = center-v.xy;
375
  vec4 SO = vUniforms[effect+2];
376
  float d1_circle = degree_region(SO,PS);
377
  float d1_bitmap = degree_bitmap(center,PS);
378

  
379
  float alpha = vUniforms[effect].x;
380
  float sinA = sin(alpha);
381
  float cosA = cos(alpha);
382

  
383
  vec2 PS2 = vec2( PS.x*cosA+PS.y*sinA,-PS.x*sinA+PS.y*cosA ); // vector PS rotated by A radians clockwise around center.
384
  vec4 SG = (1.0-d1_circle)*SO;                                // coordinates of the dilated circle P is going to get rotated around
385
  float d2 = max(0.0,degree(SG,center,PS2));                   // make it a max(0,deg) because otherwise when center=left edge of the
386
                                                               // bitmap some points end up with d2<0 and they disappear off view.
387
  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?
388
  }
389
#endif
390

  
391
//////////////////////////////////////////////////////////////////////////////////////////////
392
// WAVE EFFECT
393
//
394
// Directional sinusoidal wave effect.
395
//
396
// This is an effect from a (hopefully!) generic family of effects of the form (vec3 V: |V|=1 , f(x,y) )  (*)
397
// i.e. effects defined by a unit vector and an arbitrary function. Those effects are defined to move each
398
// point (x,y,0) of the XY plane to the point (x,y,0) + V*f(x,y).
399
//
400
// In this case V is defined by angles A and B (sines and cosines of which are precomputed in
401
// EffectQueueVertex and passed in the uniforms).
402
// Let's move V to start at the origin O, let point C be the endpoint of V, and let C' be C's projection
403
// to the XY plane. Then A is defined to be the angle C0C' and angle B is the angle C'O(axisY).
404
//
405
// Also, in this case f(x,y) = amplitude*sin(x/length), with those 2 parameters passed in uniforms.
406
//
407
//////////////////////////////////////////////////////////////////////////////////////////////
408
// How to compute any generic effect of type (*)
409
//////////////////////////////////////////////////////////////////////////////////////////////
410
//
411
// By definition, the vertices move by f(x,y)*V.
412
//
413
// Normals are much more complicated.
414
// Let angle X be the angle (0,Vy,Vz)(0,Vy,0)(Vx,Vy,Vz).
415
// Let angle Y be the angle (Vx,0,Vz)(Vx,0,0)(Vx,Vy,Vz).
416
//
417
// Then it can be shown that the resulting surface, at point to which point (x0,y0,0) got moved to,
418
// has 2 tangent vectors given by
419
//
420
// SX = (1.0+cosX*fx , cosY*sinX*fx , |sinY|*sinX*fx);  (**)
421
// SY = (cosX*sinY*fy , 1.0+cosY*fy , |sinX|*sinY*fy);  (***)
422
//
423
// and then obviously the normal N is given by N= SX x SY .
424
//
425
// We still need to remember the note from the distort function about adding up normals:
426
// we first need to 'normalize' the normals to make their third components equal, and then we
427
// simply add up the first and the second component while leaving the third unchanged.
428
//
429
// How to see facts (**) and (***) ? Briefly:
430
// a) compute the 2D analogon and conclude that in this case the tangent SX is given by
431
//    SX = ( cosA*f'(x) +1, sinA*f'(x) )    (where A is the angle vector V makes with X axis )
432
// b) cut the resulting surface with plane P which
433
//    - includes vector V
434
//    - crosses plane XY along line parallel to X axis
435
// c) apply the 2D analogon and notice that the tangent vector to the curve that is the common part of P
436
//    and our surface (I am talking about the tangent vector which belongs to P) is given by
437
//    (1+cosX*fx,0,sinX*fx) rotated by angle (90-|Y|) (where angles X,Y are defined above) along vector (1,0,0).
438
//
439
//    Matrix of rotation:
440
//
441
//    |sinY|  cosY
442
//    -cosY  |sinY|
443
//
444
// d) compute the above and see that this is equal precisely to SX from (**).
445
// e) repeat points b,c,d in direction Y and come up with (***).
446
//
447
//////////////////////////////////////////////////////////////////////////////////////////////
448
// Note: we should avoid passing certain combinations of parameters to this function. One such known
449
// combination is ( A: small but positive, B: any, amplitude >= length ).
450
// In this case, certain 'unlucky' points have their normals almost horizontal (they got moved by (almost!)
451
// amplitude, and other point length (i.e. <=amplitude) away got moved by 0, so the slope in this point is
452
// very steep). Visual effect is: vast majority of surface pretty much unchanged, but random 'unlucky'
453
// points very dark)
454
//
455
// Generally speaking I'd keep to amplitude < length, as the opposite case has some other problems as well.
456

  
457
#ifdef WAVE
458
void wave(in int effect, inout vec3 v, inout vec3 n)
459
  {
460
  vec2 center     = vUniforms[effect+1].yz;
461
  float amplitude = vUniforms[effect  ].x;
462
  float length    = vUniforms[effect  ].y;
463

  
464
  vec2 ps = center - v.xy;
465
  float deg = amplitude*degree_region(vUniforms[effect+2],ps);
466

  
467
  if( deg != 0.0 && length != 0.0 )
468
    {
469
    float phase = vUniforms[effect  ].z;
470
    float alpha = vUniforms[effect  ].w;
471
    float beta  = vUniforms[effect+1].x;
472

  
473
    float sinA = sin(alpha);
474
    float cosA = cos(alpha);
475
    float sinB = sin(beta);
476
    float cosB = cos(beta);
477

  
478
    float angle= 1.578*(ps.x*cosB-ps.y*sinB) / length + phase;
479

  
480
    vec3 dir= vec3(sinB*cosA,cosB*cosA,sinA);
481

  
482
    v += sin(angle)*deg*dir;
483

  
484
    if( n.z != 0.0 )
485
      {
486
      float sqrtX = sqrt(dir.y*dir.y + dir.z*dir.z);
487
      float sqrtY = sqrt(dir.x*dir.x + dir.z*dir.z);
488

  
489
      float sinX = ( sqrtY==0.0 ? 0.0 : dir.z / sqrtY);
490
      float cosX = ( sqrtY==0.0 ? 1.0 : dir.x / sqrtY);
491
      float sinY = ( sqrtX==0.0 ? 0.0 : dir.z / sqrtX);
492
      float cosY = ( sqrtX==0.0 ? 1.0 : dir.y / sqrtX);
493

  
494
      float abs_z = dir.z <0.0 ? -(sinX*sinY) : (sinX*sinY);
495

  
496
      float tmp = 1.578*cos(angle)*deg/length;
497

  
498
      float fx =-cosB*tmp;
499
      float fy = sinB*tmp;
500

  
501
      vec3 sx = vec3 (1.0+cosX*fx,cosY*sinX*fx,abs_z*fx);
502
      vec3 sy = vec3 (cosX*sinY*fy,1.0+cosY*fy,abs_z*fy);
503

  
504
      vec3 normal = cross(sx,sy);
505

  
506
      if( normal.z<=0.0 )                   // Why this bizarre shit rather than the straightforward
507
        {                                   //
508
        normal.x= 0.0;                      // if( normal.z>0.0 )
509
        normal.y= 0.0;                      //   {
510
        normal.z= 1.0;                      //   n.x = (n.x*normal.z + n.z*normal.x);
511
        }                                   //   n.y = (n.y*normal.z + n.z*normal.y);
512
                                            //   n.z = (n.z*normal.z);
513
                                            //   }
514
      n.x = (n.x*normal.z + n.z*normal.x);  //
515
      n.y = (n.y*normal.z + n.z*normal.y);  // ? Because if we do the above, my shitty Nexus4 crashes
516
      n.z = (n.z*normal.z);                 // during shader compilation!
517
      }
518
    }
519
  }
520
#endif
521

  
522 147
#endif  // NUM_VERTEX>0
523 148

  
524 149
//////////////////////////////////////////////////////////////////////////////////////////////
......
529 154
  vec3 n = a_Normal;
530 155

  
531 156
#if NUM_VERTEX>0
532
  int j=0;
157
  int effect=0;
533 158

  
534 159
  for(int i=0; i<vNumEffects; i++)
535 160
    {
536
#ifdef DISTORT
537
    if( vName[i]==DISTORT) distort(j,v,n); else
538
#endif
539
#ifdef DEFORM
540
    if( vName[i]==DEFORM ) deform (j,v,n); else
541
#endif
542
#ifdef SINK
543
    if( vName[i]==SINK   ) sink   (j,v);   else
544
#endif
545
#ifdef PINCH
546
    if( vName[i]==PINCH  ) pinch  (j,v);   else
547
#endif
548
#ifdef SWIRL
549
    if( vName[i]==SWIRL  ) swirl  (j,v);   else
550
#endif
551
#ifdef WAVE
552
    if( vName[i]==WAVE   ) wave   (j,v,n); else
553
#endif
554
    {}
161
    // ENABLED EFFECTS WILL BE INSERTED HERE
555 162

  
556
    j+=3;
163
    effect+=3;
557 164
    }
558 165
#endif
559 166
   

Also available in: Unified diff