[dali_1.2.16] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / page-turn-view / page-turn-effect.cpp
1 /*
2  * Copyright (c) 2016 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 // EXTERNAL INCLUDES
19 #include <string.h>
20 #include <dali/public-api/animation/constraint.h>
21 #include <dali/public-api/actors/actor.h>
22 #include <dali/public-api/object/property-map.h>
23
24 // INTERNAL INCLUDES
25 #include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
26 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-effect.h>
27
28 using namespace Dali;
29 using namespace Dali::Toolkit;
30
31 namespace
32 {
33 #define DALI_COMPOSE_SHADER(STR) #STR
34 const char * const PROPERTY_COMMON_PARAMETERS( "uCommonParameters" );
35 const char * const PROPERTY_ORIGINAL_CENTER( "originalCenter" );
36 const char * const PROPERTY_CURRENT_CENTER( "currentCenter" );
37 }
38
39 /**
40  * This constraint updates the common parameter values used by every vertex.
41  * By using constraint, they are calculate once in CPU then pass into the vertex shader as uniforms
42  */
43 struct CommonParametersConstraint
44 {
45   CommonParametersConstraint( float pageHeight )
46   : mPageHeight( pageHeight )
47   {}
48
49   void operator()( Dali::Matrix& current, const PropertyInputContainer& inputs )
50   {
51     const Vector2& originalCenter = inputs[0]->GetVector2();
52     Vector2 currentCenter = inputs[1]->GetVector2();
53
54     // calculate the curve direction and the vanishing point
55     // here, the vanishing point is the intersection of spine with the line passing through original center and vertical to curve direction
56     Vector2 curveDirection( currentCenter - originalCenter );
57     curveDirection.Normalize();
58     if( fabs(curveDirection.y) < 0.01f) // eliminate the possibility of division by zero in the next step
59     {
60       curveDirection.y = 0.01f;
61     }
62     float vanishingPointY = originalCenter.y + curveDirection.x * originalCenter.x / curveDirection.y;
63
64     float curveEndY, cosTheta ,sinTheta ,translateX, translateY;
65     // when the vanishing point is very far away, make it infinitely, in this case, the page bent horizontally
66     const float THRESHOLD(20.0);
67     if( fabs(vanishingPointY-mPageHeight*0.5f) >= mPageHeight*THRESHOLD )
68     {
69       curveDirection = Vector2(-1.f,0.f);
70       currentCenter.y = originalCenter.y;
71
72       curveEndY = originalCenter.y;
73       cosTheta = 1.f;
74       sinTheta = 0.f;
75       translateX = currentCenter.x - originalCenter.x;
76       translateY = vanishingPointY;
77     }
78     else
79     {
80       curveEndY = currentCenter.y - curveDirection.y * (currentCenter.x/curveDirection.x) ;
81       Vector2 v1( currentCenter.x, currentCenter.y - vanishingPointY );
82       v1.Normalize();
83       Vector2 v2( originalCenter.x, originalCenter.y - vanishingPointY );
84       v2.Normalize();
85       cosTheta = v1.x*v2.x + v1.y*v2.y;
86       sinTheta = ( vanishingPointY > mPageHeight*0.5f ) ? sqrt(1.0-cosTheta*cosTheta) : -sqrt(1.0-cosTheta*cosTheta);
87       translateX = currentCenter.x - cosTheta*originalCenter.x - sinTheta*( originalCenter.y-vanishingPointY );
88       translateY = currentCenter.y + sinTheta*originalCenter.x - cosTheta*( originalCenter.y-vanishingPointY );
89     }
90
91     float originalLength = fabs(originalCenter.x/curveDirection.x);
92     float currentLength = fabs(currentCenter.x/curveDirection.x);
93     float curveHeight = 0.45f*sqrt(originalLength*originalLength - currentLength*currentLength);
94
95     float* parameterArray = current.AsFloat();
96     parameterArray[0] = cosTheta;
97     parameterArray[1] = -sinTheta;
98     parameterArray[2] = originalCenter.x;
99     parameterArray[3] = originalCenter.y;
100     parameterArray[4] = sinTheta;
101     parameterArray[5] = cosTheta;
102     parameterArray[6] = currentCenter.x;
103     parameterArray[7] = currentCenter.y;
104     parameterArray[8] = translateX;
105     parameterArray[9] = translateY;
106     parameterArray[10] = vanishingPointY;
107     parameterArray[11] = curveEndY;
108     parameterArray[12] = curveDirection.x;
109     parameterArray[13] = curveDirection.y;
110     parameterArray[14] = curveHeight;
111     parameterArray[15] = currentLength;
112   }
113
114   float mPageHeight;
115 };
116
117 void Dali::Toolkit::Internal::PageTurnApplyInternalConstraint( Actor& actor, float pageHeight )
118 {
119   Constraint constraint = Constraint::New<Dali::Matrix>( actor, actor.GetPropertyIndex( PROPERTY_COMMON_PARAMETERS ) , CommonParametersConstraint( pageHeight ) );
120   constraint.AddSource( LocalSource( actor.GetPropertyIndex( PROPERTY_ORIGINAL_CENTER ) ) );
121   constraint.AddSource( LocalSource( actor.GetPropertyIndex( PROPERTY_CURRENT_CENTER ) ) );
122   constraint.Apply();
123 }
124
125 Property::Map Dali::Toolkit::Internal::CreatePageTurnEffect()
126 {
127   const char* vertexShader = DALI_COMPOSE_SHADER(
128       /*
129        * The common parameters for all the vertices, calculate in CPU then pass into the shader as uniforms
130        *
131        *  first part of the page, (outside the the line passing through original center and vertical to curve direction)
132        * no Z change, only 2D rotation and translation
133        * ([0][0],[0][1],[1][0],[1][1]) mat2 rotateMatrix
134        * ([2][0],[2][1]) vec2 translationVector
135        *
136        * ([0][2],[0][3]) vec2 originalCenter: Typically the press down position of the Pan Gesture
137        * ([1][2],[1][3]) vec2 currentCenter: Typically the current position of the Pan Gesture
138        * ([3][0],[3][1]) vec2 curveDirection: The normalized vector pointing from original center to current center
139        * ([2][2]) float vanishingPointY: The Y coordinate of the intersection of the spine
140        *                                 and the line which goes through the original center and is vertical to the curveDirection
141        * ([2][3]) float curveEndY: The Y coordinate of intersection of the spine and the line through both original and current center
142        * ([3][2]) float curveHeight: The height of the interpolated hermite curve.
143        * ([3][3]) float currentLength: The length from the current center to the curveEnd.
144        */
145       precision mediump float;\n
146       \n
147       attribute mediump vec2 aPosition;\n
148       \n
149       uniform mediump mat4 uMvpMatrix;\n
150       uniform mediump mat3 uNormalMatrix;\n
151       uniform mediump mat4 uModelView;\n
152       \n
153       uniform mat4 uCommonParameters;\n
154       \n
155       uniform vec3 uSize;\n
156       uniform float uIsTurningBack;\n
157       uniform float uTextureWidth;\n
158       varying vec3 vNormal;\n
159       varying vec4 vPosition;\n
160       varying mediump vec2 vTexCoord;\n
161       \n
162       void main()\n
163       {\n
164         vec4 position = vec4( aPosition*uSize.xy, 0.0, 1.0);\n
165         vec2 currentCenter = vec2( uCommonParameters[1][2], uCommonParameters[1][3]);\n
166         vec2 originalCenter = vec2( uCommonParameters[0][2], uCommonParameters[0][3]);\n
167         vec3 normal = vec3(0.0,0.0,1.0);\n
168         \n
169         if(currentCenter.x < originalCenter.x)\n
170         {\n
171           // change the coordinate origin from the center of the page to its top-left
172           position.xy += uSize.xy * 0.5;\n
173           vec2 curveDirection = vec2( uCommonParameters[3]);\n
174           vec3 vanishingPoint = vec3(0.0, uCommonParameters[2][2], 0.0);\n
175           // first part of the page, (outside the the line passing through original center and vertical to curve direction)
176           //no Z change, only 2D rotation and translation
177           if( dot(curveDirection, position.xy - originalCenter) < 0.0 )
178           {\n
179             position.y -= vanishingPoint.y;\n
180             position.xy = mat2(uCommonParameters)*position.xy + vec2( uCommonParameters[2]);\n
181           }\n
182           // second part of the page, bent as a ruled surface
183           else\n
184           {\n
185             // calculate on the flat plane, between
186             // the first line passing through current vertex and vanishing point
187             // the second line passing through original center and current center
188             vec2 curveEnd = vec2( 0.0, uCommonParameters[2][3] );\n
189             vec2 curFlatDirection = vec2(0.0,1.0);\n
190             float lengthFromCurve = position.y - originalCenter.y;\n
191             float lengthOnCurve = position.x;\n
192             if(currentCenter.y != originalCenter.y)\n
193             {\n
194               curFlatDirection = normalize(position.xy - vanishingPoint.xy);\n
195               lengthFromCurve = (curveEnd.x*curveDirection.y-curveEnd.y*curveDirection.x-position.x*curveDirection.y+position.y*curveDirection.x)
196               / (curFlatDirection.x*curveDirection.y-curFlatDirection.y*curveDirection.x);\n
197               lengthOnCurve = length(position.xy+lengthFromCurve*curFlatDirection-curveEnd);\n
198             }\n
199             \n
200             // define the control points of hermite curve, composed with two segments
201             // calculation is carried out on the 2D plane which is passing through both current and original center and vertical to the image plane
202             float currentLength = uCommonParameters[3][3];\n
203             float originalLength =  abs(originalCenter.x/curveDirection.x);\n
204             float height = uCommonParameters[3][2];\n
205             float percentage = currentLength/originalLength;\n
206             //vec2 SegmentOneControlPoint0 = vec2(0.0, 0.0);
207             vec2 SegmentOneControlPoint1 = vec2((0.65*percentage - 0.15)*originalLength, (0.8 + 0.2 * percentage)*height); \n
208             vec2 SegmentTwoControlPoint0 = SegmentOneControlPoint1;\n
209             vec2 SegmentTwoControlPoint1 = vec2(currentLength, 0.0); \n
210             vec2 SegmentOneTangentVector0 = SegmentOneControlPoint1;\n
211             vec2 SegmentOneTangentVector1 = vec2(0.5*originalLength,0.0);\n
212             vec2 SegmentTwoTangentVector0 = SegmentOneTangentVector1;\n
213             vec2 SegmentTwoTangentVector1 = SegmentOneTangentVector1;\n
214             \n
215             // calculate the corresponding curve point position and its tangent vector
216             // it is a linear mapping onto nonlinear curves, might cause some unwanted deformation
217             // but as there are no analytical method to calculate the curve length on arbitrary segment
218             // no efficient way to solve this nonlinear mapping, Numerical approximation would cost too much computation in shader
219             vec2 curvePoint2D;\n
220             vec2 tangent;\n
221             float t0 = lengthOnCurve / originalLength;\n
222             if(t0<=0.5)\n
223             {\n
224               float t = 2.0*t0;\n
225               float t_2 = t*t;\n
226               float t_3 = t*t_2;\n
227               curvePoint2D = (-2.0*t_3+3.0*t_2)*SegmentOneControlPoint1
228               + (t_3-2.0*t_2+t)*SegmentOneTangentVector0 + (t_3-t_2)*SegmentOneTangentVector1;\n
229               tangent = (-6.0*t_2+6.0*t)*SegmentOneControlPoint1
230               + (3.0*t_2-4.0*t+1.0)*SegmentOneTangentVector0 + (3.0*t_2-2.0*t)*SegmentOneTangentVector1;\n
231             }\n
232             else\n
233             {\n
234               float t = 2.0*t0-1.0;\n
235               float t_2 = t*t;\n
236               float t_3 = t*t_2;\n
237               curvePoint2D = (2.0*t_3-3.0*t_2+1.0)*SegmentTwoControlPoint0 + (-2.0*t_3+3.0*t_2)*SegmentTwoControlPoint1
238               + (t_3-2.0*t_2+t)*SegmentTwoTangentVector0 + (t_3-t_2)*SegmentTwoTangentVector1;\n
239               tangent = (6.0*t_2-6.0*t)*SegmentTwoControlPoint0 + (-6.0*t_2+6.0*t)*SegmentTwoControlPoint1
240               + (3.0*t_2-4.0*t+1.0)*SegmentTwoTangentVector0 + (3.0*t_2-2.0*t)*SegmentTwoTangentVector1;\n
241               // a trick to eliminate some optical illusion caused by the gradient matter of normal in per-fragment shading
242               // which is caused by linear interpolation of normal vs. nonlinear lighting
243               // will notice some artifact in the areas with dramatically normal changes, so compress the normal differences here
244               tangent.y *=  min(1.0, length(position.xyz - vanishingPoint) / uSize.y ); \n
245             }\n
246             vec3 curvePoint = vec3(curveEnd - curvePoint2D.x*curveDirection,max(0.0,curvePoint2D.y));\n
247             vec3 tangentVector = vec3(-tangent.x*curveDirection,tangent.y);\n
248             \n
249             // locate the new vertex position on the line passing through both vanishing point and the calculated curve point position
250             vec3 curLiftDirection = vec3(0.0,-1.0,0.0);\n
251             if(currentCenter.y != originalCenter.y)\n
252             {\n
253               curLiftDirection = normalize(curvePoint - vanishingPoint);\n
254               tangentVector *= (curveDirection.y > 0.0) ? -1.0 : 1.0;\n
255               // an heuristic adjustment here, to compensate the linear parameter mapping onto the nonlinear curve
256               float Y0 = position.y - curveDirection.y * (position.x/curveDirection.x); \n
257               float proportion;
258               float refLength;\n
259               if(abs(Y0-vanishingPoint.y) > abs(curveEnd.y-vanishingPoint.y)) \n
260               {\n
261                 proportion = abs(curveEnd.y - Y0) / (abs(curveEnd.y-Y0)+abs(curveEnd.y - vanishingPoint.y)); \n
262                 refLength = proportion*length(originalCenter-vanishingPoint.xy) / (proportion-1.0); \n
263               }\n
264               else\n
265               {\n
266                 proportion = abs(curveEnd.y - Y0) / abs(curveEnd.y - vanishingPoint.y);\n
267                 refLength = proportion*length(originalCenter-vanishingPoint.xy); \n
268               }\n
269               float Y1 = currentCenter.y - (normalize(currentCenter-vanishingPoint.xy)).y * refLength; \n
270               position.y = mix(Y0, Y1, t0); \n
271             }\n
272             position.xz = curvePoint.xz - lengthFromCurve*curLiftDirection.xz;\n
273             // calculate the normal vector, will be used for lighting
274             normal = cross(curLiftDirection, normalize(tangentVector));\n
275             // the signature of Z is decided by the page turning direction:
276             // from left to right(negative); from right to left (positive)
277             position.z *= -uIsTurningBack;\n
278             normal.xy *= -uIsTurningBack;\n
279           }\n
280           // change the coordinate origin from the top-left of the page to its center
281           position.xy -= uSize.xy * 0.5; \n
282         }\n
283         vNormal =  uNormalMatrix * normal;\n
284         gl_Position = uMvpMatrix * position;
285         // varying parameters for fragment shader
286         vTexCoord = aPosition + vec2(0.5);\n
287         vTexCoord.x /= uTextureWidth;
288         vPosition = uModelView * position;\n
289       }\n
290   );
291
292   const char* fragmentShader = DALI_COMPOSE_SHADER(
293       precision mediump float;\n
294       \n
295       varying mediump vec2 vTexCoord;\n
296       \n
297       uniform sampler2D sTexture;\n
298       uniform lowp vec4 uColor;\n
299       uniform vec3 uSize;\n
300       uniform vec2 uSpineShadowParameter;\n
301       varying vec3 vNormal;\n
302       varying vec4 vPosition;\n
303       \n
304       void main()\n
305       {\n
306         // need to re-normalize the interpolated normal
307         vec3 normal = normalize( vNormal );\n
308         // display page content
309         vec4 texel;
310         // display back image of the page, flip the texture
311         if(  dot(vPosition.xyz, normal) > 0.0 ) texel = texture2D( sTexture, vec2( 1.0 - vTexCoord.x, vTexCoord.y ) );\n
312         // display front image of the page
313         else texel = texture2D( sTexture, vTexCoord );\n
314
315         // display book spine, a stripe of shadowed texture
316         float pixelPos = vTexCoord.x * uSize.x; \n
317         float spineShadowCoef = 1.0; \n
318         if( pixelPos < uSpineShadowParameter.x ) \n
319         {\n
320           float x = pixelPos - uSpineShadowParameter.x;\n
321           float y = sqrt( uSpineShadowParameter.x*uSpineShadowParameter.x - x*x );\n
322           spineShadowCoef = normalize( vec2( uSpineShadowParameter.y*x/uSpineShadowParameter.x, y ) ).y;\n
323         }\n
324         // calculate the lighting
325         // set the ambient color as vec3(0.4);
326         float lightColor = abs( normal.z ) * 0.6 + 0.4;\n
327         gl_FragColor = vec4( ( spineShadowCoef * lightColor ) * texel.rgb , texel.a ) * uColor;\n
328       }
329   );
330
331   Property::Map map;
332
333   Property::Map customShader;
334
335   customShader[ Toolkit::Visual::Shader::Property::VERTEX_SHADER ] = vertexShader;
336   customShader[ Toolkit::Visual::Shader::Property::FRAGMENT_SHADER ] = fragmentShader;
337   customShader[ Toolkit::Visual::Shader::Property::SUBDIVIDE_GRID_X ] = 20;
338   customShader[ Toolkit::Visual::Shader::Property::SUBDIVIDE_GRID_Y ] = 20;
339
340   map[ Toolkit::DevelVisual::Property::SHADER ] = customShader;
341   return map;
342
343 }