Merge "Typo fixed in Control implementation doc." into tizen
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / bubble-effect / bubble-emitter-impl.cpp
1 /*
2  * Copyright (c) 2014 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 <cmath>
23 #include <dali/public-api/animation/animation.h>
24 #include <dali/public-api/render-tasks/render-task-list.h>
25 #include <dali/public-api/images/resource-image.h>
26
27 // INTERNAL INCLUDES
28 #include <dali-toolkit/public-api/shader-effects/bubble-effect/color-adjuster.h>
29
30 namespace Dali
31 {
32
33 namespace Toolkit
34 {
35
36 namespace Internal
37 {
38 BubbleEmitter::BubbleEmitter( const Vector2& movementArea,
39                               Image shapeImage,
40                               unsigned int maximumNumberOfBubble,
41                               const Vector2& bubbleSizeRange )
42 : Control( REQUIRES_TOUCH_EVENTS ),
43   mMovementArea( movementArea ),
44   mShapeImage( shapeImage ),
45   mTotalNumOfBubble( maximumNumberOfBubble ),
46   mRenderTaskRunning(false),
47   mBubbleSizeRange( bubbleSizeRange ),
48   mCurrentUniform( 0 ),
49   mDensity( 5 )
50 {
51   // Calculate how many BubbleEffect shaders are required
52   if( mTotalNumOfBubble>100 )
53   {
54     mNumBubblePerShader = 100;
55     mNumShader = mTotalNumOfBubble / 100;
56   }
57   else
58   {
59     mNumBubblePerShader = mTotalNumOfBubble;
60     mNumShader = 1;
61   }
62 }
63
64 BubbleEmitter::~BubbleEmitter()
65 {
66 }
67
68 Toolkit::BubbleEmitter BubbleEmitter::New( const Vector2& winSize,
69                                            Image shapeImage,
70                                            unsigned int maximumNumberOfBubble,
71                                            const Vector2& bubbleSizeRange )
72 {
73   // Create the implementation
74    IntrusivePtr<BubbleEmitter> internalBubbleEmitter ( new BubbleEmitter( winSize, shapeImage,
75                                                             maximumNumberOfBubble,bubbleSizeRange ) );
76
77   // Pass ownership to Toolkit::BubbleEmitter handle
78   Toolkit::BubbleEmitter bubbleEmitter( *internalBubbleEmitter );
79
80   //Second phase of implementeation : Initialization
81   internalBubbleEmitter->OnInitialize();
82
83   return bubbleEmitter;
84 }
85
86 void BubbleEmitter::OnInitialize()
87 {
88   // Create the root actor, all the meshActor should be its children
89   mBubbleRoot = Actor::New();
90   mBubbleRoot.SetSize(mMovementArea);
91
92   // Prepare the frame buffer to store the color adjusted background image
93   mEffectImage = FrameBufferImage::New( mMovementArea.width/4.f, mMovementArea.height/4.f, Pixel::RGBA8888, Dali::Image::UNUSED );
94
95   // Generate the material object, which is used by all meshActors
96   GenMaterial();
97
98   mMesh.resize( mNumShader );
99   mMeshActor.resize( mNumShader );
100   mEffect.resize( mNumShader );
101
102   // Create the meshActor group and bubbleEffect group to emit bubbles following the given track, such as finger touch track.
103   MeshData meshData;
104   ConstructBubbleMesh( meshData, mNumBubblePerShader*mDensity);
105   for(unsigned int i=0; i < mNumShader; i++ )
106   {
107     mMesh[i] = Mesh::New( meshData );
108     mMeshActor[i] = MeshActor::New( mMesh[i] );
109     mMeshActor[i].SetAffectedByLighting( false );
110     mMeshActor[i].SetParentOrigin(ParentOrigin::TOP_LEFT);
111     mEffect[i] = BubbleEffect::New( mNumBubblePerShader );
112     mEffect[i].SetEffectImage( mEffectImage );
113     mEffect[i].SetMovementArea( mMovementArea );
114     mMeshActor[i].SetShaderEffect( mEffect[i] );
115     mBubbleRoot.Add( mMeshActor[i] );
116   }
117
118   // Create the extra meshActor and bubbleEffect to emit bubbles in totally random angle.
119   MeshData meshDataForNoise;
120   ConstructBubbleMesh( meshDataForNoise, mNumBubblePerShader);
121   mMeshActorForNoise = MeshActor::New( Mesh::New(meshDataForNoise) );
122   mMeshActorForNoise.SetAffectedByLighting( false );
123   mMeshActorForNoise.SetParentOrigin(ParentOrigin::TOP_LEFT);
124   mEffectForNoise = BubbleEffect::New( mNumBubblePerShader );
125   mEffectForNoise.SetMovementArea( mMovementArea );
126   mEffectForNoise.SetEffectImage( mEffectImage );
127   mMeshActorForNoise.SetShaderEffect( mEffectForNoise );
128   mBubbleRoot.Add( mMeshActorForNoise );
129
130   // Create a cameraActor for the off screen render task.
131   mCameraActor = CameraActor::New(mMovementArea);
132   mCameraActor.SetParentOrigin(ParentOrigin::CENTER);
133
134   Stage stage = Stage::GetCurrent();
135
136   stage.Add(mCameraActor);
137   stage.ContextRegainedSignal().Connect(this, &BubbleEmitter::OnContextRegained);
138 }
139
140 Actor BubbleEmitter::GetRootActor()
141 {
142   return mBubbleRoot;
143 }
144
145 void BubbleEmitter::SetBackground( Image bgImage, const Vector3& hsvDelta )
146 {
147   mBackgroundImage = bgImage;
148   mHSVDelta = hsvDelta;
149
150   ImageActor sourceActor = ImageActor::New( bgImage );
151   sourceActor.SetSize( mMovementArea );
152   sourceActor.SetParentOrigin(ParentOrigin::CENTER);
153   Stage::GetCurrent().Add( sourceActor );
154
155   ColorAdjuster colorAdjuster = ColorAdjuster::New( hsvDelta, true /*ignore alpha to make bubble color always*/ );
156   sourceActor.SetShaderEffect( colorAdjuster );
157
158   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
159   RenderTask task = taskList.CreateTask();
160   task.SetRefreshRate( RenderTask::REFRESH_ONCE );
161   task.SetSourceActor( sourceActor );
162   task.SetExclusive(true);
163   task.SetCameraActor(mCameraActor);
164   task.GetCameraActor().SetInvertYAxis(true);
165   task.SetTargetFrameBuffer( mEffectImage );
166   task.FinishedSignal().Connect(this, &BubbleEmitter::OnRenderFinished);
167   mRenderTaskRunning = true;
168 }
169
170 void BubbleEmitter::SetShapeImage( Image shapeImage )
171 {
172   mCustomMaterial.SetDiffuseTexture( shapeImage );
173 }
174
175 void BubbleEmitter::SetBubbleScale( float scale )
176 {
177   for(unsigned int i=0; i < mNumShader; i++ )
178   {
179     mEffect[i].SetDynamicScale( scale );
180   }
181   mEffectForNoise.SetDynamicScale( scale );
182 }
183
184 void BubbleEmitter::SetBubbleDensity( unsigned int density )
185 {
186   DALI_ASSERT_ALWAYS( density>0 && density<=9 && " Only densities between 1 to 9 are valid " );
187
188   if( density == mDensity )
189   {
190     return;
191   }
192   else
193   {
194     mDensity = density;
195     MeshData meshData;
196     ConstructBubbleMesh( meshData, mNumBubblePerShader*mDensity);
197     for(unsigned int i=0; i < mNumShader; i++ )
198     {
199       mMesh[i].UpdateMeshData(meshData);
200     }
201   }
202 }
203
204 // clear the resources created for the off screen rendering
205 void BubbleEmitter::OnRenderFinished(RenderTask& source)
206 {
207   mRenderTaskRunning = false;
208   Actor sourceActor = source.GetSourceActor();
209   if( sourceActor )
210   {
211     RenderableActor renderable = RenderableActor::DownCast( sourceActor );
212     if( renderable )
213     {
214       renderable.RemoveShaderEffect();
215     }
216   }
217
218   Stage stage = Stage::GetCurrent();
219   stage.Remove(sourceActor);
220   stage.GetRenderTaskList().RemoveTask(source);
221 }
222
223 void BubbleEmitter::OnContextRegained()
224 {
225   // Context was lost, so the framebuffer has been destroyed. Re-create render task
226   // and trigger re-draw if not already running
227   if( ! mRenderTaskRunning )
228   {
229     SetBackground( mBackgroundImage, mHSVDelta );
230   }
231 }
232
233 void BubbleEmitter::SetBlendMode( bool enable )
234 {
235   if(enable)
236   {
237     for(unsigned int i=0; i < mNumShader; i++ )
238     {
239       // linear overlay
240       // TODO: if BlendColor would be public api from renderable actor, then can optimize the constant color
241       mMeshActor[i].SetBlendFunc(BlendingFactor::SRC_ALPHA, BlendingFactor::ONE,
242                                  BlendingFactor::ZERO, BlendingFactor::ONE);
243     }
244     mMeshActorForNoise.SetBlendFunc(BlendingFactor::SRC_ALPHA, BlendingFactor::ONE,
245                                     BlendingFactor::ZERO, BlendingFactor::ONE);
246   }
247   else
248   {
249     for(unsigned int i=0; i < mNumShader; i++ )
250     {
251       // using default blend func
252       mMeshActor[i].SetBlendFunc( BlendingFactor::SRC_ALPHA,   BlendingFactor::ONE_MINUS_SRC_ALPHA,
253                                   BlendingFactor::ONE, BlendingFactor::ONE_MINUS_SRC_ALPHA );
254     }
255     mMeshActorForNoise.SetBlendFunc( BlendingFactor::SRC_ALPHA,   BlendingFactor::ONE_MINUS_SRC_ALPHA,
256                                      BlendingFactor::ONE, BlendingFactor::ONE_MINUS_SRC_ALPHA );
257   }
258 }
259
260 void BubbleEmitter::EmitBubble( Animation& animation, const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement )
261 {
262   unsigned int curUniform = mCurrentUniform  % mNumBubblePerShader;
263   unsigned int groupIdx = mCurrentUniform / mNumBubblePerShader;
264   SetBubbleParameter( mEffect[groupIdx], curUniform, emitPosition, direction, displacement);
265   animation.AnimateTo( Property( mEffect[groupIdx], mEffect[groupIdx].GetPercentagePropertyName(curUniform) ),
266                        1.f, AlphaFunctions::Linear );
267
268   if( mCurrentUniform % mNumShader == 0 )
269   {
270     unsigned int uniform = mCurrentUniform / mNumShader;
271     SetBubbleParameter(mEffectForNoise, uniform, emitPosition, displacement);
272     animation.AnimateTo( Property( mEffectForNoise, mEffectForNoise.GetPercentagePropertyName(uniform) ),
273                          1.f, AlphaFunctions::Linear );
274   }
275
276   mCurrentUniform = (mCurrentUniform + 1) % mTotalNumOfBubble;
277 }
278
279 void BubbleEmitter::StartExplosion( float duration, float multiple )
280 {
281   Animation animation = Animation::New( duration );
282   for(unsigned int i=0; i < mNumShader; i++ )
283   {
284     animation.AnimateTo( Property( mEffect[i], mEffect[i].GetMagnificationPropertyName() ),
285                          multiple, AlphaFunctions::EaseOut);
286   }
287   animation.AnimateTo( Property( mEffectForNoise, mEffectForNoise.GetMagnificationPropertyName() ),
288                        multiple, AlphaFunctions::EaseOut);
289   animation.Play();
290
291   animation.FinishedSignal().Connect(this, &BubbleEmitter::OnExplosionFinished);
292 }
293
294 void BubbleEmitter::Restore()
295 {
296   for(unsigned int i=0; i < mNumShader; i++ )
297   {
298     mEffect[i].ResetParameters();
299   }
300   mEffectForNoise.ResetParameters();
301 }
302
303 void BubbleEmitter::GenMaterial()
304 {
305   mCustomMaterial = Material::New("CustomMaterial");
306   mCustomMaterial.SetOpacity(1.0f);
307   mCustomMaterial.SetDiffuseColor(Color::WHITE);
308   mCustomMaterial.SetAmbientColor(Vector4(0.0, 0.1, 0.1, 1.0));
309   mCustomMaterial.SetMapU( Material::MAPPING_MODE_WRAP );
310   mCustomMaterial.SetMapV( Material::MAPPING_MODE_WRAP );
311   mCustomMaterial.SetDiffuseTexture( mShapeImage );
312 }
313
314 void BubbleEmitter::AddVertex(MeshData::VertexContainer& vertices, Vector3 XYZ, Vector2 UV)
315 {
316   MeshData::Vertex meshVertex;
317   meshVertex.x = XYZ.x;
318   meshVertex.y = XYZ.y;
319   meshVertex.z = XYZ.z;
320   meshVertex.u = UV.x;
321   meshVertex.v = UV.y;
322   vertices.push_back(meshVertex);
323 }
324
325 void BubbleEmitter::AddTriangle(MeshData::FaceIndices& faces,
326 size_t v0, size_t v1, size_t v2)
327 {
328   faces.push_back(v0);
329   faces.push_back(v1);
330   faces.push_back(v2);
331 }
332
333 void BubbleEmitter::ConstructBubbleMesh( MeshData& meshData, unsigned int numOfBubble)
334 {
335   MeshData::VertexContainer    vertices;
336   MeshData::FaceIndices        faces;
337   BoneContainer                bones(0);
338
339   for(unsigned int index = 0; index < numOfBubble; index ++)
340   {
341     float curSize = RandomRange(mBubbleSizeRange.x, mBubbleSizeRange.y);
342     if(rand()%100 < 1)
343     {
344       curSize *= 2.f;
345     }
346     float depth = static_cast<float>( index );
347     AddVertex( vertices, Vector3(0.f,0.f,depth), Vector2(0.f,0.f) );
348     AddVertex( vertices, Vector3(0.f,curSize,depth), Vector2( 0.f,1.f ));
349     AddVertex( vertices, Vector3(curSize,curSize,depth), Vector2(1.f,1.f) );
350     AddVertex( vertices, Vector3(curSize,0.f,depth), Vector2(1.f,0.f) );
351
352     unsigned int idx = index * 4;
353     AddTriangle( faces, idx, idx+1, idx+2);
354     AddTriangle( faces, idx, idx+2, idx+3);
355   }
356
357   meshData.SetData(vertices, faces, bones, mCustomMaterial);
358   meshData.SetHasColor(false);
359   meshData.SetHasTextureCoords(true);
360 }
361
362 void BubbleEmitter::SetBubbleParameter( BubbleEffect& effect, unsigned int curUniform,
363                                         const Vector2& emitPosition, const Vector2& displacement )
364 {
365   int halfRange = displacement.x / 2;
366   Vector2 randomVec(rand()%static_cast<int>(displacement.x) - halfRange, rand()%static_cast<int>(displacement.y) - halfRange);
367   if(randomVec.y > 0.0f)
368   {
369     randomVec.y *= 0.33f;
370   }
371
372   Vector4 startAndEndPos( emitPosition.x, emitPosition.y, emitPosition.x+randomVec.x, emitPosition.y+randomVec.y );
373   effect.SetStartAndEndPosition( curUniform, startAndEndPos );
374
375   effect.SetPercentage( curUniform, 0.f);
376 }
377
378 void BubbleEmitter::SetBubbleParameter( BubbleEffect& effect, unsigned int curUniform,
379                                         const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement )
380 {
381   Vector2 dir(direction);
382
383   int halfRange = displacement.x / 2;
384   // for the y coordinate, always negative, so bubbles always go upwards
385   Vector2 randomVec(rand()%static_cast<int>(displacement.x) - halfRange, -rand()%static_cast<int>(displacement.y));
386   dir.Normalize();
387   randomVec.x -= dir.x*halfRange;
388   randomVec.y *= 1.0f - fabsf(dir.x)*0.33f;
389
390   if(randomVec.y > 0.0f)
391   {
392     randomVec.y *= 0.33f;
393   }
394   Vector4 startAndEndPos( emitPosition.x, emitPosition.y, emitPosition.x+randomVec.x, emitPosition.y+randomVec.y );
395   effect.SetStartAndEndPosition( curUniform, startAndEndPos );
396
397   effect.SetPercentage( curUniform, 0.f);
398 }
399
400 void BubbleEmitter::OnExplosionFinished( Animation& source )
401 {
402   Restore();
403 }
404
405 float BubbleEmitter::RandomRange(float f0, float f1)
406 {
407   return f0 + (rand() & 0xfff) * (f1-f0) * (1.0f/4095.0f);
408 }
409
410 } // namespace Internal
411
412 } // namespace Toolkit
413
414 } // namespace Dali