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