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