1
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2
|
// Copyright 2017 Leszek Koltunski //
|
3
|
// //
|
4
|
// This file is part of Distorted. //
|
5
|
// //
|
6
|
// Distorted is free software: you can redistribute it and/or modify //
|
7
|
// it under the terms of the GNU General Public License as published by //
|
8
|
// the Free Software Foundation, either version 2 of the License, or //
|
9
|
// (at your option) any later version. //
|
10
|
// //
|
11
|
// Distorted is distributed in the hope that it will be useful, //
|
12
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
13
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
14
|
// GNU General Public License for more details. //
|
15
|
// //
|
16
|
// You should have received a copy of the GNU General Public License //
|
17
|
// along with Distorted. If not, see <http://www.gnu.org/licenses/>. //
|
18
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
19
|
|
20
|
package org.distorted.library.effect;
|
21
|
|
22
|
import org.distorted.library.type.Data3D;
|
23
|
import org.distorted.library.type.Data4D;
|
24
|
|
25
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
26
|
/**
|
27
|
* Distort the Mesh by applying a 3D vector of force.
|
28
|
*/
|
29
|
public class VertexEffectDistort extends VertexEffect
|
30
|
{
|
31
|
private Data3D mVector, mCenter;
|
32
|
private Data4D mRegion;
|
33
|
|
34
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
35
|
/**
|
36
|
* Only for use by the library itself.
|
37
|
*
|
38
|
* @y.exclude
|
39
|
*/
|
40
|
public boolean compute(float[] uniforms, int index, long currentDuration, long step )
|
41
|
{
|
42
|
mCenter.get(uniforms,index+CENTER_OFFSET,currentDuration,step);
|
43
|
mRegion.get(uniforms,index+REGION_OFFSET,currentDuration,step);
|
44
|
return mVector.get(uniforms,index,currentDuration,step);
|
45
|
}
|
46
|
|
47
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
48
|
// PUBLIC API
|
49
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
50
|
// Def: P-current vertex being distorted, S - center of the effect, X- point where half-line SP meets
|
51
|
// the region circle (if P is inside the circle); (Vx,Vy,Vz) - force vector.
|
52
|
//
|
53
|
// horizontal part
|
54
|
// Point (Px,Py) gets moved by vector (Wx,Wy) where Wx/Wy = Vx/Vy i.e. Wx=aVx and Wy=aVy where
|
55
|
// 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)
|
56
|
// 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) ]
|
57
|
// Computations above are valid for screen (0,0)x(w,h) but here we have (-w/2,-h/2)x(w/2,h/2)
|
58
|
//
|
59
|
// vertical part
|
60
|
// Let vector PS = (dx,dy), f(x) (0<=x<=|SX|) be the shape of the side of the bubble.
|
61
|
// H(Px,Py) = |PS|>|SX| ? 0 : f(|PX|)
|
62
|
// N(Px,Py) = |PS|>|SX| ? (0,0,1) : ( -(dx/|PS|)sin(beta), -(dy/|PS|)sin(beta), cos(beta) ) where tan(beta) is f'(|PX|)
|
63
|
// ( i.e. normalize( dx, dy, -|PS|/f'(|PX|) )
|
64
|
//
|
65
|
// Now we also have to take into account the effect horizontal move by V=(Vx,Vy) will have on the normal vector.
|
66
|
// Solution:
|
67
|
// 1. Decompose the V into two subcomponents, one parallel to SX and another perpendicular.
|
68
|
// 2. Convince yourself (draw!) that the perpendicular component has no effect on normals.
|
69
|
// 3. The parallel component changes the length of |SX| by the factor of a=(|SX|-|Vpar|)/|SX| (where the length
|
70
|
// can be negative depending on the direction)
|
71
|
// 4. that in turn leaves the x and y parts of the normal unchanged and multiplies the z component by a!
|
72
|
//
|
73
|
// |Vpar| = (Vx*dx-Vy*dy) / sqrt(ps_sq) (-Vy because y is inverted)
|
74
|
// 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
|
75
|
//
|
76
|
// Side of the bubble
|
77
|
//
|
78
|
// choose from one of the three bubble shapes: the cone, the thin bubble and the thick bubble
|
79
|
// Case 1:
|
80
|
// f(t) = t, i.e. f(x) = Vz * x/|SX| (a cone)
|
81
|
// -|PS|/f'(|PX|) = -|PS|*|SX|/Vz but since ps_sq=|PS|^2 and d=|PX|/|SX| then |PS|*|SX| = ps_sq/(1-d)
|
82
|
// so finally -|PS|/f'(|PX|) = -ps_sq/(Vz*(1-d))
|
83
|
//
|
84
|
// Case 2:
|
85
|
// f(t) = 3t^2 - 2t^3 --> f(0)=0, f'(0)=0, f'(1)=0, f(1)=1 (the bell curve)
|
86
|
// here we have t = x/|SX| which makes f'(|PX|) = 6*Vz*|PS|*|PX|/|SX|^3.
|
87
|
// so -|PS|/f'(|PX|) = (-|SX|^3)/(6Vz|PX|) = (-|SX|^2) / (6*Vz*d) but
|
88
|
// d = |PX|/|SX| and ps_sq = |PS|^2 so |SX|^2 = ps_sq/(1-d)^2
|
89
|
// so finally -|PS|/f'(|PX|) = -ps_sq/ (6Vz*d*(1-d)^2)
|
90
|
//
|
91
|
// Case 3:
|
92
|
// 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,
|
93
|
// f(0.5)=0.7 and f'(t)= t(t-1)^2 >=0 for t>=0 so this produces a fuller, thicker bubble!
|
94
|
// then -|PS|/f'(|PX|) = (-|PS|*|SX)) / (12Vz*d*(d-1)^2) but |PS|*|SX| = ps_sq/(1-d) (see above!)
|
95
|
// so finally -|PS|/f'(|PX|) = -ps_sq/ (12Vz*d*(1-d)^3)
|
96
|
//
|
97
|
// Now, new requirement: we have to be able to add up normal vectors, i.e. distort already distorted surfaces.
|
98
|
// 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 ).
|
99
|
// 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)
|
100
|
// 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
|
101
|
// can simply add up the first and second components.
|
102
|
//
|
103
|
// Thus we actually want to compute N(Px,Py) = a*(-(dx/|PS|)*f'(|PX|), -(dy/|PS|)*f'(|PX|), 1) and keep adding
|
104
|
// the first two components. (a is the horizontal part)
|
105
|
//
|
106
|
// 2019-01-09 fixes for stuff discovered with the Earth testing app
|
107
|
// Now, the above stuff works only if the normal vector is close to (0,0,1). To make it work for any situation,
|
108
|
// rotate the normal, force and ps vectors along the axis (n.y,-n.x,0) and angle A between the normal and (0,0,1)
|
109
|
// then modify the rotated normal (so (0,0,1)) and rotate the modified normal back.
|
110
|
//
|
111
|
// if we have a vector (vx,vy,vz) that w want to rotate with a rotation that turns the normal into (0,0,1), then
|
112
|
// a) axis of rotation = (n.x,n.y,n.z) x (0,0,1) = (n.y,-n.x,0)
|
113
|
// b) angle of rotation A: cosA = (n.x,n.y,n.z)*(0,0,1) = n.z (and sinA = + sqrt(1-n.z^2))
|
114
|
//
|
115
|
// so normalized axisNor = (n.x/ sinA, -n.y/sinA, 0) and now from Rodrigues rotation formula
|
116
|
// Vrot = v*cosA + (axisNor x v)*sinA + axisNor*(axis*v)*(1-cosA)
|
117
|
//
|
118
|
// which makes Vrot = (a+n.y*c , b-n.y*c , v*n) where
|
119
|
// a = vx*nz-vz*nx , b = vy*nz-vz*ny , c = (vx*ny-vy*nx)/(1+nz) (unless n=(0,0,-1))
|
120
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
121
|
/**
|
122
|
* Have to call this before the shaders get compiled (i.e before DistortedLibrary.onCreate()) for the Effect to work.
|
123
|
*/
|
124
|
public static void enable()
|
125
|
{
|
126
|
addEffect(EffectName.DISTORT,
|
127
|
|
128
|
"vec3 center = vUniforms[effect+1].yzw; \n"
|
129
|
+ "vec3 ps = center-v; \n"
|
130
|
+ "vec3 force = vUniforms[effect].xyz; \n"
|
131
|
+ "vec4 region= vUniforms[effect+2]; \n"
|
132
|
+ "float d = degree(region,center,ps); \n"
|
133
|
|
134
|
+ "if( d>0.0 ) \n"
|
135
|
+ " { \n"
|
136
|
+ " v += d*force; \n"
|
137
|
|
138
|
+ " float tp = 1.0+n.z; \n"
|
139
|
+ " float tr = 1.0 / (tp - (1.0 - sign(tp))); \n"
|
140
|
|
141
|
+ " float ap = ps.x*n.z - ps.z*n.x; \n" // likewise rotate the ps vector
|
142
|
+ " float bp = ps.y*n.z - ps.z*n.y; \n" //
|
143
|
+ " float cp =(ps.x*n.y - ps.y*n.x)*tr; \n" //
|
144
|
+ " vec3 psRot = vec3( ap+n.y*cp , bp-n.x*cp , dot(ps,n) ); \n" //
|
145
|
|
146
|
+ " float len = length(psRot); \n"
|
147
|
+ " float corr= sign(len)-1.0; \n" // make N (0,0,1) if ps=(0,0,0)
|
148
|
+ " vec3 N = vec3( -dot(force,n)*psRot.xy, region.w*len-corr ); \n" // modified rotated normal
|
149
|
// dot(force,n) is rotated force.z
|
150
|
+ " float an = N.x*n.z + N.z*n.x; \n" // now create the normal vector
|
151
|
+ " float bn = N.y*n.z + N.z*n.y; \n" // back from our modified normal
|
152
|
+ " float cn =(N.x*n.y - N.y*n.x)*tr; \n" // rotated back
|
153
|
+ " n = vec3( an+n.y*cn , bn-n.x*cn , -N.x*n.x-N.y*n.y+N.z*n.z);\n" // notice 4 signs change!
|
154
|
|
155
|
+ " n = normalize(n); \n"
|
156
|
+ " } \n"
|
157
|
);
|
158
|
}
|
159
|
|
160
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
161
|
/**
|
162
|
* Distort a (possibly changing in time) part of the Mesh by a (possibly changing in time) vector of force.
|
163
|
*
|
164
|
* @param vector vector of force the Center of the Effect is currently being dragged with.
|
165
|
* @param center 3-dimensional Data that, at any given time, returns the Center of the Effect.
|
166
|
* @param region Region that masks the Effect.
|
167
|
*/
|
168
|
public VertexEffectDistort(Data3D vector, Data3D center, Data4D region)
|
169
|
{
|
170
|
super(EffectName.DISTORT);
|
171
|
mVector = vector;
|
172
|
mCenter = center;
|
173
|
mRegion = (region==null ? MAX_REGION : region);
|
174
|
}
|
175
|
|
176
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
177
|
/**
|
178
|
* Distort the whole Mesh by a (possibly changing in time) vector of force.
|
179
|
*
|
180
|
* @param vector vector of force the Center of the Effect is currently being dragged with.
|
181
|
* @param center 3-dimensional Data that, at any given time, returns the Center of the Effect.
|
182
|
*/
|
183
|
public VertexEffectDistort(Data3D vector, Data3D center)
|
184
|
{
|
185
|
super(EffectName.DISTORT);
|
186
|
mVector = vector;
|
187
|
mCenter = center;
|
188
|
mRegion = MAX_REGION;
|
189
|
}
|
190
|
}
|
191
|
|
192
|
|