Merge "PageTurnView cleanup" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / page-turn-view / page-turn-effect.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-effect.h>
19 #include <dali/public-api/math/matrix.h>
20
21 using namespace Dali;
22 using namespace Dali::Toolkit;
23
24 void CommonParametersConstraint( Dali::Matrix& current, const PropertyInputContainer& inputs )
25 {
26   const Vector2& originalCenter = inputs[0]->GetVector2();
27   Vector2 currentCenter = inputs[1]->GetVector2();
28   const Vector2& pageSize = inputs[2]->GetVector2();
29
30   // calculate the curve direction and the vanishing point
31   // here, the vanishing point is the intersection of spine with the line passing through original center and vertical to curve direction
32   Vector2 curveDirection( currentCenter - originalCenter );
33   curveDirection.Normalize();
34   if( fabs(curveDirection.y) < 0.01f) // eliminate the possibility of division by zero in the next step
35   {
36     curveDirection.y = 0.01f;
37   }
38   float vanishingPointY = originalCenter.y + curveDirection.x * originalCenter.x / curveDirection.y;
39
40   float curveEndY, cosTheta ,sinTheta ,translateX, translateY;
41   // when the vanishing point is very far away, make it infinitely, in this case, the page bent horizontally
42   const float THRESHOLD(20.0);
43   if( fabs(vanishingPointY-pageSize.y*0.5f) >= pageSize.y*THRESHOLD )
44   {
45     curveDirection = Vector2(-1.f,0.f);
46     currentCenter.y = originalCenter.y;
47
48     curveEndY = originalCenter.y;
49     cosTheta = 1.f;
50     sinTheta = 0.f;
51     translateX = currentCenter.x - originalCenter.x;
52     translateY = vanishingPointY;
53   }
54   else
55   {
56     curveEndY = currentCenter.y - curveDirection.y * (currentCenter.x/curveDirection.x) ;
57     Vector2 v1( currentCenter.x, currentCenter.y - vanishingPointY );
58     v1.Normalize();
59     Vector2 v2( originalCenter.x, originalCenter.y - vanishingPointY );
60     v2.Normalize();
61     cosTheta = v1.x*v2.x + v1.y*v2.y;
62     sinTheta = ( vanishingPointY > pageSize.y*0.5f ) ? sqrt(1.0-cosTheta*cosTheta) : -sqrt(1.0-cosTheta*cosTheta);
63     translateX = currentCenter.x - cosTheta*originalCenter.x - sinTheta*( originalCenter.y-vanishingPointY );
64     translateY = currentCenter.y + sinTheta*originalCenter.x - cosTheta*( originalCenter.y-vanishingPointY );
65   }
66
67   float originalLength = fabs(originalCenter.x/curveDirection.x);
68   float currentLength = fabs(currentCenter.x/curveDirection.x);
69   float curveHeight = 0.45f*sqrt(originalLength*originalLength - currentLength*currentLength);
70
71   float* parameterArray = current.AsFloat();
72   parameterArray[0] = cosTheta;
73   parameterArray[1] = -sinTheta;
74   parameterArray[2] = originalCenter.x;
75   parameterArray[3] = originalCenter.y;
76   parameterArray[4] = sinTheta;
77   parameterArray[5] = cosTheta;
78   parameterArray[6] = currentCenter.x;
79   parameterArray[7] = currentCenter.y;
80   parameterArray[8] = translateX;
81   parameterArray[9] = translateY;
82   parameterArray[10] = vanishingPointY;
83   parameterArray[11] = curveEndY;
84   parameterArray[12] = curveDirection.x;
85   parameterArray[13] = curveDirection.y;
86   parameterArray[14] = curveHeight;
87   parameterArray[15] = currentLength;
88 }
89
90 void Dali::Toolkit::Internal::PageTurnApplyInternalConstraint( ShaderEffect& shaderEffect)
91 {
92   Constraint constraint = Constraint::New<Dali::Matrix>( shaderEffect, shaderEffect.GetPropertyIndex( "uCommonParameters" ), CommonParametersConstraint );
93   constraint.AddSource( LocalSource( shaderEffect.GetPropertyIndex( "uOriginalCenter" ) ) );
94   constraint.AddSource( LocalSource( shaderEffect.GetPropertyIndex( "uCurrentCenter" ) ) );
95   constraint.AddSource( LocalSource( shaderEffect.GetPropertyIndex( "uPageSize" ) ) );
96   constraint.Apply();
97 }
98
99 ShaderEffect Dali::Toolkit::Internal::CreatePageTurnEffect()
100 {
101   std::string vertexShader = DALI_COMPOSE_SHADER(
102       /*
103        * The common parameters for all the vertices, calculate in CPU then pass into the shader as uniforms
104        *
105        *  first part of the page, (outside the the line passing through original center and vertical to curve direction)
106        * no Z change, only 2D rotation and translation
107        * ([0][0],[0][1],[1][0],[1][1]) mat2 rotateMatrix
108        * ([2][0],[2][1]) vec2 translationVector
109        *
110        * ([0][2],[0][3]) vec2 originalCenter: Typically the press down position of the Pan Gesture
111        * ([1][2],[1][3]) vec2 currentCenter: Typically the current position of the Pan Gesture
112        * ([3][0],[3][1]) vec2 curveDirection: The normalized vector pointing from original center to current center
113        * ([2][2]) float vanishingPointY: The Y coordinate of the intersection of the spine
114        *                                 and the line which goes through the original center and is vertical to the curveDirection
115        * ([2][3]) float curveEndY: The Y coordinate of intersection of the spine and the line through both original and current center
116        * ([3][2]) float curveHeight: The height of the interpolated hermite curve.
117        * ([3][3]) float currentLength: The length from the current center to the curveEnd.
118        */
119       precision mediump float;\n
120       uniform mat4 uCommonParameters;\n
121       \n
122       uniform vec2 uPageSize;\n
123       uniform float uIsTurningBack;\n
124       varying vec3 vNormal;\n
125       varying vec4 vPosition;\n
126       varying float vEdgeShadow;\n
127       \n
128       void main()\n
129       {\n
130         vec4 position = vec4( aPosition.xy, 0.0, 1.0);\n
131         vec2 currentCenter = vec2( uCommonParameters[1][2], uCommonParameters[1][3]);\n
132         vec2 originalCenter = vec2( uCommonParameters[0][2], uCommonParameters[0][3]);\n
133         vec3 normal = vec3(0.0,0.0,1.0);\n
134         \n
135         if(currentCenter.x < originalCenter.x)\n
136         {\n
137           // change the coordinate origin from the center of the page to its top-left
138           position.xy += uPageSize * 0.5;\n
139           vec2 curveDirection = vec2( uCommonParameters[3]);\n
140           vec3 vanishingPoint = vec3(0.0, uCommonParameters[2][2], 0.0);\n
141           // first part of the page, (outside the the line passing through original center and vertical to curve direction)
142           //no Z change, only 2D rotation and translation
143           if( dot(curveDirection, position.xy - originalCenter) < 0.0 )
144           {\n
145             position.y -= vanishingPoint.y;\n
146             position.xy = mat2(uCommonParameters)*position.xy + vec2( uCommonParameters[2]);\n
147           }\n
148           // second part of the page, bent as a ruled surface
149           else\n
150           {\n
151             // calculate on the flat plane, between
152             // the first line passing through current vertex and vanishing point
153             // the second line passing through original center and current center
154             vec2 curveEnd = vec2( 0.0, uCommonParameters[2][3] );\n
155             vec2 curFlatDirection = vec2(0.0,1.0);\n
156             float lengthFromCurve = position.y - originalCenter.y;\n
157             float lengthOnCurve = position.x;\n
158             if(currentCenter.y != originalCenter.y)\n
159             {\n
160               curFlatDirection = normalize(position.xy - vanishingPoint.xy);\n
161               lengthFromCurve = (curveEnd.x*curveDirection.y-curveEnd.y*curveDirection.x-position.x*curveDirection.y+position.y*curveDirection.x)
162               / (curFlatDirection.x*curveDirection.y-curFlatDirection.y*curveDirection.x);\n
163               lengthOnCurve = length(position.xy+lengthFromCurve*curFlatDirection-curveEnd);\n
164             }\n
165             \n
166             // define the control points of hermite curve, composed with two segments
167             // calulation is carried out on the 2D plane which is passing through both current and original center and vertical to the image plane
168             float currentLength = uCommonParameters[3][3];\n
169             float originalLength =  abs(originalCenter.x/curveDirection.x);\n
170             float height = uCommonParameters[3][2];\n
171             float percentage = currentLength/originalLength;\n
172             //vec2 SegmentOneControlPoint0 = vec2(0.0, 0.0);
173             vec2 SegmentOneControlPoint1 = vec2((0.65*percentage - 0.15)*originalLength, (0.8 + 0.2 * percentage)*height); \n
174             vec2 SegmentTwoControlPoint0 = SegmentOneControlPoint1;\n
175             vec2 SegmentTwoControlPoint1 = vec2(currentLength, 0.0); \n
176             vec2 SegmentOneTangentVector0 = SegmentOneControlPoint1;\n
177             vec2 SegmentOneTangentVector1 = vec2(0.5*originalLength,0.0);\n
178             vec2 SegmentTwoTangentVector0 = SegmentOneTangentVector1;\n
179             vec2 SegmentTwoTangentVector1 = SegmentOneTangentVector1;\n
180             \n
181             // calulate the corresponding curve point position and its tangent vector
182             // it is a linear mapping onto nonlinear curves, might cause some unwanted deformation
183             // but as there are no analytical method to calculate the curve length on arbitrary segment
184             // no efficient way to solve this nonlinear mapping, Numerical approximation would cost too much computation in shader
185             vec2 curvePoint2D;\n
186             vec2 tangent;\n
187             float t0 = lengthOnCurve / originalLength;\n
188             if(t0<=0.5)\n
189             {\n
190               float t = 2.0*t0;\n
191               float t_2 = t*t;\n
192               float t_3 = t*t_2;\n
193               curvePoint2D = (-2.0*t_3+3.0*t_2)*SegmentOneControlPoint1
194               + (t_3-2.0*t_2+t)*SegmentOneTangentVector0 + (t_3-t_2)*SegmentOneTangentVector1;\n
195               tangent = (-6.0*t_2+6.0*t)*SegmentOneControlPoint1
196               + (3.0*t_2-4.0*t+1.0)*SegmentOneTangentVector0 + (3.0*t_2-2.0*t)*SegmentOneTangentVector1;\n
197             }\n
198             else\n
199             {\n
200               float t = 2.0*t0-1.0;\n
201               float t_2 = t*t;\n
202               float t_3 = t*t_2;\n
203               curvePoint2D = (2.0*t_3-3.0*t_2+1.0)*SegmentTwoControlPoint0 + (-2.0*t_3+3.0*t_2)*SegmentTwoControlPoint1
204               + (t_3-2.0*t_2+t)*SegmentTwoTangentVector0 + (t_3-t_2)*SegmentTwoTangentVector1;\n
205               tangent = (6.0*t_2-6.0*t)*SegmentTwoControlPoint0 + (-6.0*t_2+6.0*t)*SegmentTwoControlPoint1
206               + (3.0*t_2-4.0*t+1.0)*SegmentTwoTangentVector0 + (3.0*t_2-2.0*t)*SegmentTwoTangentVector1;\n
207               // a trick to eliminate some optical illusion caused by the gradient matter of normal in per-fragment shading
208               // which is caused by linear interpolation of normal vs. nonlinear lighting
209               // will notice some artifact in the areas with dramatically normal changes, so compress the normal differences here
210               tangent.y *=  min(1.0, length(position.xyz - vanishingPoint) / uPageSize.y ); \n
211             }\n
212             vec3 curvePoint = vec3(curveEnd - curvePoint2D.x*curveDirection,max(0.0,curvePoint2D.y));\n
213             vec3 tangentVector = vec3(-tangent.x*curveDirection,tangent.y);\n
214             \n
215             // locate the new vertex position on the line passing through both vanishing point and the calculated curve point position
216             vec3 curLiftDirection = vec3(0.0,-1.0,0.0);\n
217             if(currentCenter.y != originalCenter.y)\n
218             {\n
219               curLiftDirection = normalize(curvePoint - vanishingPoint);\n
220               tangentVector *= (curveDirection.y > 0.0) ? -1.0 : 1.0;\n
221               // an heuristic adjustment here, to compensate the linear parameter mapping onto the nonlinear curve
222               float Y0 = position.y - curveDirection.y * (position.x/curveDirection.x); \n
223               float proportion;
224               float refLength;\n
225               if(abs(Y0-vanishingPoint.y) > abs(curveEnd.y-vanishingPoint.y)) \n
226               {\n
227                 proportion = abs(curveEnd.y - Y0) / (abs(curveEnd.y-Y0)+abs(curveEnd.y - vanishingPoint.y)); \n
228                 refLength = proportion*length(originalCenter-vanishingPoint.xy) / (proportion-1.0); \n
229               }\n
230               else\n
231               {\n
232                 proportion = abs(curveEnd.y - Y0) / abs(curveEnd.y - vanishingPoint.y);\n
233                 refLength = proportion*length(originalCenter-vanishingPoint.xy); \n
234               }\n
235               float Y1 = currentCenter.y - (normalize(currentCenter-vanishingPoint.xy)).y * refLength; \n
236               position.y = mix(Y0, Y1, t0); \n
237             }\n
238             position.xz = curvePoint.xz - lengthFromCurve*curLiftDirection.xz;\n
239             // calculate the normal vector, will be used for lighting
240             normal = cross(curLiftDirection, normalize(tangentVector));\n
241             // the signature of Z is decided by the page turning direction:
242             // from left to right(negative); from right to left (positive)
243             position.z *= -uIsTurningBack;\n
244             normal.xy *= -uIsTurningBack;\n
245           }\n
246           // change the coordinate origin from the top-left of the page to its center
247           position.xy -= uPageSize * 0.5; \n
248         }\n
249         position.z += aPosition.z;\n
250         gl_Position = uMvpMatrix * position;\n
251         // varying parameters for fragment shader
252         vTexCoord = aTexCoord;
253         vNormal = uNormalMatrix*normal;\n
254         vPosition = uModelView * position;\n
255       }\n
256   );
257
258   std::string fragmentShader = DALI_COMPOSE_SHADER(
259       precision mediump float;\n
260       uniform vec2 uPageSize;\n
261       uniform vec2 uSpineShadowParameter;\n
262       varying vec3 vNormal;\n
263       varying vec4 vPosition;\n
264       varying float vEdgeShadow;\n
265       \n
266       void main()\n
267       {\n
268         // need to re-normalize the interpolated normal
269         vec3 normal = normalize(vNormal);\n
270         vec4 texel;\n
271         float spineShadowCoef = 1.0; \n
272         // display page content
273         // display back image of the page, flip the texture
274         if(  dot(vPosition.xyz, normal) > 0.0 ) texel = texture2D( sTexture, vec2( sTextureRect.p+sTextureRect.s-vTexCoord.x, vTexCoord.y ) );\n
275         // display front image of the page
276         else texel = texture2D( sTexture, vTexCoord );\n
277         // display book spine, a stripe of shadowed texture
278         float pixelPos = (vTexCoord.x-sTextureRect.s)*uPageSize.x; \n
279         if(pixelPos < uSpineShadowParameter.x) \n
280         {\n
281           float x = pixelPos - uSpineShadowParameter.x;\n
282           float y = sqrt( uSpineShadowParameter.x*uSpineShadowParameter.x - x*x);\n
283           spineShadowCoef = normalize( vec2( uSpineShadowParameter.y*x/uSpineShadowParameter.x, y ) ).y;\n
284         }\n
285         // calculate the lighting
286         // set the ambient color as vec3(0.4);
287         float lightColor = abs( normal.z ) * 0.6 + 0.4;\n
288         gl_FragColor = vec4( ( spineShadowCoef* lightColor)* texel.rgb , texel.a ) * uColor;\n
289       }
290   );
291
292   // Create the implementation, temporarily owned on stack,
293   Dali::ShaderEffect shaderEffectCustom = Dali::ShaderEffect::New( vertexShader, fragmentShader,ShaderEffect::HINT_GRID );
294
295   static const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
296
297   Vector2 defaultPageSize = Dali::Stage::GetCurrent().GetSize();
298   Dali::Matrix zeroMatrix(true);
299   shaderEffectCustom.SetUniform( "uCommonParameters", zeroMatrix );
300   shaderEffectCustom.SetUniform( "uPageSize", defaultPageSize );
301   shaderEffectCustom.SetUniform( "uSpineShadowParameter", DEFAULT_SPINE_SHADOW_PARAMETER );
302
303   shaderEffectCustom.RegisterProperty( "uOriginalCenter", Vector2( defaultPageSize[0], defaultPageSize[1]*0.5f ) );
304   shaderEffectCustom.RegisterProperty( "uCurrentCenter", Vector2( defaultPageSize[0], defaultPageSize[1]*0.5f ) );
305
306   PageTurnApplyInternalConstraint(shaderEffectCustom);
307
308   // setting isTurningBack to -1.0f here means turning page forward
309   shaderEffectCustom.SetUniform( "uIsTurningBack", -1.0f );
310
311   return shaderEffectCustom;
312 }
313