Merge "Control - Trigger the size negotiation when the background is updated." into...
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / bubble-effect / bubble-emitter-impl.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 // CLASS HEADER
19 #include "bubble-emitter-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/animation/animation.h>
23 #include <dali/public-api/render-tasks/render-task-list.h>
24 #include <dali/public-api/rendering/texture.h>
25 #include <dali/public-api/rendering/shader.h>
26
27 // INTERNAL INCLUDES
28 #include <dali-toolkit/internal/controls/bubble-effect/bubble-effect.h>
29 #include <dali-toolkit/internal/controls/bubble-effect/bubble-renderer.h>
30
31 namespace
32 {
33 struct Vertex
34 {
35   Vertex()
36   : index( 0.0f ), position(), textureCoord()
37   {
38   }
39
40   Vertex( float index, const Dali::Vector2& position, const Dali::Vector2& textureCoord )
41   : index( index ), position( position ), textureCoord( textureCoord )
42   {
43   }
44
45   float index;
46   Dali::Vector2 position;
47   Dali::Vector2 textureCoord;
48 };
49
50 /**
51  * Return a random value between the given interval.
52  * @param[in] f0 The low bound
53  * @param[in] f1 The up bound
54  * @param[in] seed The seed to genergate random number
55  * @return A random value between the given interval
56  */
57 float RandomRange(float f0, float f1, unsigned int& seed)
58 {
59   return f0 + (rand_r( &seed ) & 0xfff) * (f1-f0) * (1.0f/4095.0f);
60 }
61
62 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
63   attribute mediump vec2 aPosition;\n
64   attribute mediump vec2 aTexCoord;\n
65   uniform mediump vec3 uSize;\n
66   uniform mediump mat4 uMvpMatrix;\n
67   varying mediump vec2 vTexCoord;\n
68   \n
69
70   void main()\n
71   {\n
72     gl_Position = uMvpMatrix * vec4(aPosition*uSize.xy,0.0,1.0);
73     vTexCoord = aTexCoord;\n
74   }\n
75 );
76
77 const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
78     precision highp float;\n
79     uniform vec3 uHSVDelta;\n
80     varying mediump vec2 vTexCoord;\n
81     uniform sampler2D sTexture;\n
82     float rand(vec2 co) \n
83     {\n
84       return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); \n}
85     \n
86     vec3 rgb2hsv(vec3 c)\n
87     {\n
88       vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n
89       vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n
90       vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n
91       \n
92       float d = q.x - min(q.w, q.y);\n
93       float e = 1.0e-10;\n
94       return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n
95     }\n
96     vec3 hsv2rgb(vec3 c)\n
97     {\n
98       vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n
99       vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n
100       return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n
101     }\n
102     void main() {\n
103       vec4 color = texture2D(sTexture, vTexCoord); \n
104       vec3 hsvColor = rgb2hsv( color.rgb );\n
105       // modify the hsv Value
106       hsvColor += uHSVDelta * rand(vTexCoord); \n
107       // if the new vale exceeds one, then decrease it
108       hsvColor -= max(hsvColor*2.0 - vec3(2.0), 0.0);\n
109       // if the new vale drops below zero, then increase it
110       hsvColor -= min(hsvColor*2.0, 0.0);\n
111       color = vec4( hsv2rgb( hsvColor ), 1.0 ); \n
112       gl_FragColor = color; \n
113     }\n
114   );
115
116 Dali::Geometry CreateTexturedQuad()
117 {
118   struct Vertex
119   {
120     Dali::Vector2 position;
121     Dali::Vector2 texCoord;
122   };
123
124   static const Vertex data[] = {{ Dali::Vector2( -0.5f, -0.5f ), Dali::Vector2( 0.0f, 0.0f ) },
125                                 { Dali::Vector2(  0.5f, -0.5f ), Dali::Vector2( 1.0f, 0.0f ) },
126                                 { Dali::Vector2( -0.5f,  0.5f ), Dali::Vector2( 0.0f, 1.0f ) },
127                                 { Dali::Vector2(  0.5f,  0.5f ), Dali::Vector2( 1.0f, 1.0f ) }};
128
129   //Create a vertex buffer for vertex positions and texture coordinates
130   Dali::PropertyBuffer vertexBuffer = Dali::PropertyBuffer::New( Dali::Property::Map()
131                                               .Add( "aPosition", Dali::Property::VECTOR2 )
132                                               .Add( "aTexCoord", Dali::Property::VECTOR2 ) );
133   vertexBuffer.SetData( data, 4u );
134
135   //Create the geometry
136   Dali::Geometry geometry = Dali::Geometry::New();
137   geometry.AddVertexBuffer( vertexBuffer );
138   geometry.SetType(Dali::Geometry::TRIANGLE_STRIP );
139
140   return geometry;
141 }
142
143 }
144
145 namespace Dali
146 {
147
148 namespace Toolkit
149 {
150
151 namespace Internal
152 {
153 BubbleEmitter::BubbleEmitter( const Vector2& movementArea,
154                               Texture shapeTexture,
155                               unsigned int maximumNumberOfBubble,
156                               const Vector2& bubbleSizeRange )
157 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
158   mShapeTexture( shapeTexture ),
159   mMovementArea( movementArea ),
160   mBubbleSizeRange( bubbleSizeRange ),
161   mDensity( 5 ),
162   mTotalNumOfBubble( maximumNumberOfBubble ),
163   mCurrentBubble( 0 ),
164   mRandomSeed( 0 ),
165   mRenderTaskRunning(false)
166 {
167   // Calculate how many shaders are required
168   if( mTotalNumOfBubble>100 )
169   {
170     mNumBubblePerRenderer = 100;
171     mNumRenderer = mTotalNumOfBubble / 100;
172     if( mNumRenderer*mNumBubblePerRenderer < mTotalNumOfBubble )
173     {
174       mNumRenderer++;
175       mNumBubblePerRenderer =  mTotalNumOfBubble / mNumRenderer+1;
176       mTotalNumOfBubble = mNumRenderer * mNumBubblePerRenderer;
177     }
178   }
179   else
180   {
181     mNumBubblePerRenderer = mTotalNumOfBubble;
182     mNumRenderer = 1;
183   }
184
185   mRandomSeed = time( NULL );
186 }
187
188 BubbleEmitter::~BubbleEmitter()
189 {
190 }
191
192 Toolkit::BubbleEmitter BubbleEmitter::New( const Vector2& winSize,
193                                            Texture shapeTexture,
194                                            unsigned int maximumNumberOfBubble,
195                                            const Vector2& bubbleSizeRange )
196 {
197   // Create the implementation
198    IntrusivePtr<BubbleEmitter> internalBubbleEmitter ( new BubbleEmitter( winSize, shapeTexture,
199                                                             maximumNumberOfBubble,bubbleSizeRange ) );
200
201   // Pass ownership to Toolkit::BubbleEmitter handle
202   Toolkit::BubbleEmitter bubbleEmitter( *internalBubbleEmitter );
203
204   //Second phase of implementeation : Initialization
205   internalBubbleEmitter->OnInitialize();
206
207   return bubbleEmitter;
208 }
209
210 void BubbleEmitter::OnInitialize()
211 {
212   // Create the root actor, all the meshActor should be its children
213   mBubbleRoot = Actor::New();
214   mBubbleRoot.SetSize(mMovementArea);
215
216   // Prepare the frame buffer to store the color adjusted background texture
217   Vector2 imageSize = Vector2( mMovementArea.width/4.f, mMovementArea.height/4.f );
218   mFrameBuffer = FrameBuffer::New( imageSize.x, imageSize.y, 0 );
219   mEffectTexture = Texture::New( TextureType::TEXTURE_2D, Pixel::RGBA8888, imageSize.x, imageSize.y );
220   mFrameBuffer.AttachColorTexture( mEffectTexture );
221
222   // Generate the geometry, which is used by all bubbleActors
223   mMeshGeometry =  CreateGeometry( mNumBubblePerRenderer*mDensity );
224
225   Shader bubbleShader = CreateBubbleShader( mNumBubblePerRenderer );
226
227   mTextureSet = TextureSet::New();
228   mTextureSet.SetTexture( 0u, mEffectTexture );
229   mTextureSet.SetTexture( 1u, mShapeTexture );
230
231   // Create the renderers to render the bubbles
232   mBubbleRenderers.resize( mNumRenderer );
233   for(unsigned int i=0; i < mNumRenderer; i++ )
234   {
235     mBubbleRenderers[i].Initialize( mNumBubblePerRenderer, mMovementArea, mMeshGeometry, mTextureSet, bubbleShader );
236     mBubbleRoot.AddRenderer( mBubbleRenderers[i].GetRenderer() );
237   }
238
239   // Create a cameraActor for the off screen render task.
240   mCameraActor = CameraActor::New(mMovementArea);
241   mCameraActor.SetParentOrigin(ParentOrigin::CENTER);
242
243   Stage stage = Stage::GetCurrent();
244
245   stage.Add(mCameraActor);
246   stage.ContextRegainedSignal().Connect(this, &BubbleEmitter::OnContextRegained);
247 }
248
249 Actor BubbleEmitter::GetRootActor()
250 {
251   return mBubbleRoot;
252 }
253
254 void BubbleEmitter::SetBackground( Texture bgTexture, const Vector3& hsvDelta )
255 {
256   mBackgroundTexture = bgTexture;
257   mHSVDelta = hsvDelta;
258
259   //Create RenderTask source actor
260   Actor sourceActor = Actor::New();
261   sourceActor.SetSize( mMovementArea );
262   sourceActor.SetParentOrigin(ParentOrigin::CENTER);
263   sourceActor.RegisterProperty( "uHSVDelta", hsvDelta );
264   Stage::GetCurrent().Add( sourceActor );
265
266   //Create renderer
267   Dali::Geometry geometry = CreateTexturedQuad();
268   Shader shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
269   Renderer renderer = Renderer::New( geometry, shader );
270   TextureSet textureSet = TextureSet::New();
271   textureSet.SetTexture(0u, bgTexture );
272   renderer.SetTextures( textureSet );
273   sourceActor.AddRenderer( renderer );
274
275   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
276   RenderTask task = taskList.CreateTask();
277   task.SetRefreshRate( RenderTask::REFRESH_ONCE );
278   task.SetSourceActor( sourceActor );
279   task.SetExclusive(true);
280   task.SetCameraActor(mCameraActor);
281   task.GetCameraActor().SetInvertYAxis(true);
282   task.SetFrameBuffer( mFrameBuffer );
283   task.FinishedSignal().Connect(this, &BubbleEmitter::OnRenderFinished);
284   mRenderTaskRunning = true;
285 }
286
287 void BubbleEmitter::SetBubbleShape( Texture shapeTexture )
288 {
289   mTextureSet.SetTexture( 1, shapeTexture );
290 }
291
292 void BubbleEmitter::SetBubbleScale( float scale )
293 {
294   for(unsigned int i=0; i < mNumRenderer; i++ )
295   {
296     mBubbleRenderers[i].SetDynamicScale( scale );
297   }
298 }
299
300 void BubbleEmitter::SetBubbleDensity( unsigned int density )
301 {
302   DALI_ASSERT_ALWAYS( density>0 && density<=9 && " Only densities between 1 to 9 are valid " );
303
304   if( density == mDensity )
305   {
306     return;
307   }
308   else
309   {
310     mDensity = density;
311     mMeshGeometry =  CreateGeometry( mNumBubblePerRenderer*mDensity );
312     for(unsigned int i=0; i < mNumRenderer; i++ )
313     {
314       mBubbleRenderers[i].SetGeometry( mMeshGeometry );
315     }
316   }
317 }
318
319 // clear the resources created for the off screen rendering
320 void BubbleEmitter::OnRenderFinished(RenderTask& source)
321 {
322   mRenderTaskRunning = false;
323   Actor sourceActor = source.GetSourceActor();
324   Stage stage = Stage::GetCurrent();
325   stage.Remove(sourceActor);
326   stage.GetRenderTaskList().RemoveTask(source);
327 }
328
329 void BubbleEmitter::OnContextRegained()
330 {
331   // Context was lost, so the framebuffer has been destroyed. Re-create render task
332   // and trigger re-draw if not already running
333   if( ! mRenderTaskRunning )
334   {
335     SetBackground( mBackgroundTexture, mHSVDelta );
336   }
337 }
338
339 void BubbleEmitter::EmitBubble( Animation& animation, const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement )
340 {
341   unsigned int curUniform = mCurrentBubble  % mNumBubblePerRenderer;
342   unsigned int groupIdx = mCurrentBubble / mNumBubblePerRenderer;
343   SetBubbleParameter( mBubbleRenderers[groupIdx], curUniform, emitPosition - Vector2(mMovementArea.x*0.5f,mMovementArea.y*0.5f), direction, displacement);
344   animation.AnimateTo( mBubbleRenderers[groupIdx].GetPercentageProperty(curUniform),
345                        1.f, AlphaFunction::LINEAR );
346
347   mCurrentBubble = (mCurrentBubble + 1) % mTotalNumOfBubble;
348 }
349
350 void BubbleEmitter::Restore()
351 {
352   for(unsigned int i=0; i < mNumRenderer; i++ )
353   {
354     mBubbleRenderers[i].ResetProperties();
355   }
356 }
357
358 Geometry BubbleEmitter::CreateGeometry( unsigned int numOfPatch )
359 {
360   unsigned int numVertex = numOfPatch*4u;
361   Vector<Vertex> vertexData;
362   vertexData.Reserve( numVertex );
363
364   unsigned int numIndex = numOfPatch*6u;
365   Vector<unsigned short> indexData;
366   indexData.Reserve( numIndex );
367
368   for(unsigned int i = 0; i < numOfPatch; i++)
369   {
370     float halfSize = RandomRange(mBubbleSizeRange.x, mBubbleSizeRange.y, mRandomSeed) * 0.5f;
371
372     float index = static_cast<float>( i );
373     vertexData.PushBack( Vertex( index, Vector2(-halfSize,-halfSize),Vector2(0.f,0.f) ) );
374     vertexData.PushBack( Vertex( index, Vector2(-halfSize, halfSize), Vector2(0.f,1.f) ) );
375     vertexData.PushBack( Vertex( index, Vector2( halfSize, halfSize),  Vector2(1.f,1.f) ) );
376     vertexData.PushBack( Vertex( index, Vector2( halfSize,-halfSize), Vector2(1.f,0.f) ) );
377
378     unsigned short idx = index * 4;
379     indexData.PushBack( idx );
380     indexData.PushBack( idx+1 );
381     indexData.PushBack( idx+2 );
382     indexData.PushBack( idx );
383     indexData.PushBack( idx+2 );
384     indexData.PushBack( idx+3 );
385   }
386
387   Property::Map vertexFormat;
388   vertexFormat["aIndex"] = Property::FLOAT;
389   vertexFormat["aPosition"] = Property::VECTOR2;
390   vertexFormat["aTexCoord"] = Property::VECTOR2;
391   PropertyBuffer vertices = PropertyBuffer::New( vertexFormat );
392   vertices.SetData( &vertexData[0], numVertex );
393
394   Geometry geometry = Geometry::New();
395   geometry.AddVertexBuffer( vertices );
396   geometry.SetIndexBuffer( &indexData[0], numIndex );
397
398   return geometry;
399 }
400
401 void BubbleEmitter::SetBubbleParameter( BubbleRenderer& bubbleRenderer, unsigned int curUniform,
402                                         const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement )
403 {
404   Vector2 dir(direction);
405
406   int halfRange = displacement.x / 2;
407   // for the y coordinate, always negative, so bubbles always go upwards
408   Vector2 randomVec( rand_r( &mRandomSeed ) % static_cast<int>(displacement.x) - halfRange, -rand_r( &mRandomSeed ) % static_cast<int>(displacement.y) );
409   dir.Normalize();
410   randomVec.x -= dir.x*halfRange;
411   randomVec.y *= 1.0f - fabsf(dir.x)*0.33f;
412
413   if(randomVec.y > 0.0f)
414   {
415     randomVec.y *= 0.33f;
416   }
417   Vector4 startAndEndPos( emitPosition.x, emitPosition.y, emitPosition.x+randomVec.x, emitPosition.y+randomVec.y );
418   bubbleRenderer.SetStartAndEndPosition( curUniform, startAndEndPos );
419
420   bubbleRenderer.SetPercentage( curUniform, 0.f);
421 }
422
423 } // namespace Internal
424
425 } // namespace Toolkit
426
427 } // namespace Dali