62ef78dc10ef90f9380189ae3fa8df62ea80cc31
[platform/core/uifw/dali-toolkit.git] / optional / 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   mBubbleSizeRange( bubbleSizeRange ),
46   mCurrentUniform( 0 ),
47   mDensity( 5 )
48 {
49 }
50
51 BubbleEmitter::~BubbleEmitter()
52 {
53 }
54
55 Toolkit::BubbleEmitter BubbleEmitter::New( const Vector2& winSize,
56                                            Image shapeImage,
57                                            unsigned int maximumNumberOfBubble,
58                                            const Vector2& bubbleSizeRange )
59 {
60   // Create the implementation
61    IntrusivePtr<BubbleEmitter> internalBubbleEmitter ( new BubbleEmitter( winSize, shapeImage,
62                                                             maximumNumberOfBubble,bubbleSizeRange ) );
63
64   // Pass ownership to Toolkit::BubbleEmitter handle
65   Toolkit::BubbleEmitter bubbleEmitter( *internalBubbleEmitter );
66
67   //Second phase of implementeation : Initialization
68   internalBubbleEmitter->OnInitialize();
69
70   return bubbleEmitter;
71 }
72
73 void BubbleEmitter::OnInitialize()
74 {
75   // Create the root actor, all the meshActor should be its children
76   mBubbleRoot = Actor::New();
77   mBubbleRoot.SetSize(mMovementArea);
78
79   // Prepare the frame buffer to store the color adjusted background image
80   mEffectImage = FrameBufferImage::New( mMovementArea.width/4.f, mMovementArea.height/4.f, Pixel::RGBA8888, Dali::Image::Unused );
81
82   // Generate the material object, which is used by all meshActors
83   GenMaterial();
84
85   // Calculate how many BubbleEffect shaders are required
86   if( mTotalNumOfBubble>100 )
87   {
88     mNumBubblePerShader = 100;
89     mNumShader = mTotalNumOfBubble / 100;
90   }
91   else
92   {
93     mNumBubblePerShader = mTotalNumOfBubble;
94     mNumShader = 1;
95   }
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   Stage::GetCurrent().Add(mCameraActor);
133 }
134
135 Actor BubbleEmitter::GetRootActor()
136 {
137   return mBubbleRoot;
138 }
139
140 void BubbleEmitter::SetBackground( Image bgImage, const Vector3& hsvDelta )
141 {
142   ImageActor sourceActor = ImageActor::New( bgImage );
143   sourceActor.SetSize( mMovementArea );
144   sourceActor.SetParentOrigin(ParentOrigin::CENTER);
145   Stage::GetCurrent().Add( sourceActor );
146
147   ColorAdjuster colorAdjuster = ColorAdjuster::New( hsvDelta, true /*ignore alpha to make bubble color always*/ );
148   sourceActor.SetShaderEffect( colorAdjuster );
149
150   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
151   RenderTask task = taskList.CreateTask();
152   task.SetRefreshRate( RenderTask::REFRESH_ONCE );
153   task.SetSourceActor( sourceActor );
154   task.SetExclusive(true);
155   task.SetCameraActor(mCameraActor);
156   task.GetCameraActor().SetInvertYAxis(true);
157   task.SetTargetFrameBuffer( mEffectImage );
158   task.FinishedSignal().Connect(this, &BubbleEmitter::OnRenderFinished);
159 }
160
161 void BubbleEmitter::SetShapeImage( Image shapeImage )
162 {
163   mCustomMaterial.SetDiffuseTexture( shapeImage );
164
165   //Get pixel width of the shape
166   float width = Image::GetImageSize(shapeImage.GetFilename()).width;
167
168   for(unsigned int i=0; i < mNumShader; i++ )
169   {
170     mEffect[i].SetShapeImageWidth(width);
171   }
172   mEffectForNoise.SetShapeImageWidth(width);
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   Actor sourceActor = source.GetSourceActor();
208   if( sourceActor )
209   {
210     RenderableActor renderable = RenderableActor::DownCast( sourceActor );
211     if( renderable )
212     {
213       renderable.RemoveShaderEffect();
214     }
215   }
216   Stage::GetCurrent().Remove(sourceActor);
217   Stage::GetCurrent().GetRenderTaskList().RemoveTask(source);
218 }
219
220 void BubbleEmitter::SetBlendMode( bool enable )
221 {
222   if(enable)
223   {
224     for(unsigned int i=0; i < mNumShader; i++ )
225     {
226       // linear overlay
227       // TODO: if BlendColor would be public api from renderable actor, then can optimize the constant color
228       mMeshActor[i].SetBlendFunc(BlendingFactor::SRC_ALPHA, BlendingFactor::ONE,
229                                  BlendingFactor::ZERO, BlendingFactor::ONE);
230     }
231     mMeshActorForNoise.SetBlendFunc(BlendingFactor::SRC_ALPHA, BlendingFactor::ONE,
232                                     BlendingFactor::ZERO, BlendingFactor::ONE);
233   }
234   else
235   {
236     for(unsigned int i=0; i < mNumShader; i++ )
237     {
238       // using default blend func
239       mMeshActor[i].SetBlendFunc( BlendingFactor::SRC_ALPHA,   BlendingFactor::ONE_MINUS_SRC_ALPHA,
240                                   BlendingFactor::ONE, BlendingFactor::ONE_MINUS_SRC_ALPHA );
241     }
242     mMeshActorForNoise.SetBlendFunc( BlendingFactor::SRC_ALPHA,   BlendingFactor::ONE_MINUS_SRC_ALPHA,
243                                      BlendingFactor::ONE, BlendingFactor::ONE_MINUS_SRC_ALPHA );
244   }
245 }
246
247 void BubbleEmitter::EmitBubble( Animation& animation, const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement )
248 {
249   unsigned int curUniform = mCurrentUniform  % mNumBubblePerShader;
250   unsigned int groupIdx = mCurrentUniform / mNumBubblePerShader;
251   SetBubbleParameter( mEffect[groupIdx], curUniform, emitPosition, direction, displacement);
252   animation.AnimateTo( Property( mEffect[groupIdx], mEffect[groupIdx].GetPercentagePropertyName(curUniform) ),
253                        1.f, AlphaFunctions::Linear );
254
255   if( mCurrentUniform % mNumShader == 0 )
256   {
257     unsigned int uniform = mCurrentUniform / mNumShader;
258     SetBubbleParameter(mEffectForNoise, uniform, emitPosition, displacement);
259     animation.AnimateTo( Property( mEffectForNoise, mEffectForNoise.GetPercentagePropertyName(uniform) ),
260                          1.f, AlphaFunctions::Linear );
261   }
262
263   mCurrentUniform = (mCurrentUniform + 1) % mTotalNumOfBubble;
264 }
265
266 void BubbleEmitter::StartExplosion( float duration, float multiple )
267 {
268   Animation animation = Animation::New( duration );
269   for(unsigned int i=0; i < mNumShader; i++ )
270   {
271     animation.AnimateTo( Property( mEffect[i], mEffect[i].GetMagnificationPropertyName() ),
272                          multiple, AlphaFunctions::EaseOut);
273   }
274   animation.AnimateTo( Property( mEffectForNoise, mEffectForNoise.GetMagnificationPropertyName() ),
275                        multiple, AlphaFunctions::EaseOut);
276   animation.Play();
277
278   animation.FinishedSignal().Connect(this, &BubbleEmitter::OnExplosionFinished);
279 }
280
281 void BubbleEmitter::Restore()
282 {
283   for(unsigned int i=0; i < mNumShader; i++ )
284   {
285     mEffect[i].ResetParameters();
286   }
287   mEffectForNoise.ResetParameters();
288 }
289
290 void BubbleEmitter::GenMaterial()
291 {
292   mCustomMaterial = Material::New("CustomMaterial");
293   mCustomMaterial.SetOpacity(1.0f);
294   mCustomMaterial.SetDiffuseColor(Color::WHITE);
295   mCustomMaterial.SetAmbientColor(Vector4(0.0, 0.1, 0.1, 1.0));
296   mCustomMaterial.SetMapU( Material::MAPPING_MODE_WRAP );
297   mCustomMaterial.SetMapV( Material::MAPPING_MODE_WRAP );
298   mCustomMaterial.SetDiffuseTexture( mShapeImage );
299 }
300
301 void BubbleEmitter::AddVertex(MeshData::VertexContainer& vertices, Vector3 XYZ, Vector2 UV)
302 {
303   MeshData::Vertex meshVertex;
304   meshVertex.x = XYZ.x;
305   meshVertex.y = XYZ.y;
306   meshVertex.z = XYZ.z;
307   meshVertex.u = UV.x;
308   meshVertex.v = UV.y;
309   vertices.push_back(meshVertex);
310 }
311
312 void BubbleEmitter::AddTriangle(MeshData::FaceIndices& faces,
313 size_t v0, size_t v1, size_t v2)
314 {
315   faces.push_back(v0);
316   faces.push_back(v1);
317   faces.push_back(v2);
318 }
319
320 void BubbleEmitter::ConstructBubbleMesh( MeshData& meshData, unsigned int numOfBubble)
321 {
322   MeshData::VertexContainer    vertices;
323   MeshData::FaceIndices        faces;
324   BoneContainer                bones(0);
325
326   for(unsigned int index = 0; index < numOfBubble; index ++)
327   {
328     float curSize = RandomRange(mBubbleSizeRange.x, mBubbleSizeRange.y);
329     if(rand()%100 < 1)
330     {
331       curSize *= 2.f;
332     }
333     float depth = static_cast<float>( index );
334     AddVertex( vertices, Vector3(0.f,0.f,depth), Vector2(0.f,0.f) );
335     AddVertex( vertices, Vector3(0.f,curSize,depth), Vector2( 0.f,1.f ));
336     AddVertex( vertices, Vector3(curSize,curSize,depth), Vector2(1.f,1.f) );
337     AddVertex( vertices, Vector3(curSize,0.f,depth), Vector2(1.f,0.f) );
338
339     unsigned int idx = index * 4;
340     AddTriangle( faces, idx, idx+1, idx+2);
341     AddTriangle( faces, idx, idx+2, idx+3);
342   }
343
344   meshData.SetData(vertices, faces, bones, mCustomMaterial);
345   meshData.SetHasColor(false);
346   meshData.SetHasTextureCoords(true);
347 }
348
349 void BubbleEmitter::SetBubbleParameter( BubbleEffect& effect, unsigned int curUniform,
350                                         const Vector2& emitPosition, const Vector2& displacement )
351 {
352   int halfRange = displacement.x / 2;
353   Vector2 randomVec(rand()%static_cast<int>(displacement.x) - halfRange, rand()%static_cast<int>(displacement.y) - halfRange);
354   if(randomVec.y > 0.0f)
355   {
356     randomVec.y *= 0.33f;
357   }
358
359   Vector4 startAndEndPos( emitPosition.x, emitPosition.y, emitPosition.x+randomVec.x, emitPosition.y+randomVec.y );
360   effect.SetStartAndEndPosition( curUniform, startAndEndPos );
361
362   effect.SetPercentage( curUniform, 0.f);
363 }
364
365 void BubbleEmitter::SetBubbleParameter( BubbleEffect& effect, unsigned int curUniform,
366                                         const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement )
367 {
368   Vector2 dir(direction);
369
370   int halfRange = displacement.x / 2;
371   // for the y coordinate, always negative, so bubbles always go upwards
372   Vector2 randomVec(rand()%static_cast<int>(displacement.x) - halfRange, -rand()%static_cast<int>(displacement.y));
373   dir.Normalize();
374   randomVec.x -= dir.x*halfRange;
375   randomVec.y *= 1.0f - fabsf(dir.x)*0.33f;
376
377   if(randomVec.y > 0.0f)
378   {
379     randomVec.y *= 0.33f;
380   }
381   Vector4 startAndEndPos( emitPosition.x, emitPosition.y, emitPosition.x+randomVec.x, emitPosition.y+randomVec.y );
382   effect.SetStartAndEndPosition( curUniform, startAndEndPos );
383
384   effect.SetPercentage( curUniform, 0.f);
385 }
386
387 void BubbleEmitter::OnExplosionFinished( Animation& source )
388 {
389   Restore();
390 }
391
392 float BubbleEmitter::RandomRange(float f0, float f1)
393 {
394   return f0 + (rand() & 0xfff) * (f1-f0) * (1.0f/4095.0f);
395 }
396
397 } // namespace Internal
398
399 } // namespace Toolkit
400
401 } // namespace Dali