Allowed Bubble emitter to recover from context loss
[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   mRenderTaskRunning(false),
46   mBubbleSizeRange( bubbleSizeRange ),
47   mCurrentUniform( 0 ),
48   mDensity( 5 )
49 {
50 }
51
52 BubbleEmitter::~BubbleEmitter()
53 {
54 }
55
56 Toolkit::BubbleEmitter BubbleEmitter::New( const Vector2& winSize,
57                                            Image shapeImage,
58                                            unsigned int maximumNumberOfBubble,
59                                            const Vector2& bubbleSizeRange )
60 {
61   // Create the implementation
62    IntrusivePtr<BubbleEmitter> internalBubbleEmitter ( new BubbleEmitter( winSize, shapeImage,
63                                                             maximumNumberOfBubble,bubbleSizeRange ) );
64
65   // Pass ownership to Toolkit::BubbleEmitter handle
66   Toolkit::BubbleEmitter bubbleEmitter( *internalBubbleEmitter );
67
68   //Second phase of implementeation : Initialization
69   internalBubbleEmitter->OnInitialize();
70
71   return bubbleEmitter;
72 }
73
74 void BubbleEmitter::OnInitialize()
75 {
76   // Create the root actor, all the meshActor should be its children
77   mBubbleRoot = Actor::New();
78   mBubbleRoot.SetSize(mMovementArea);
79
80   // Prepare the frame buffer to store the color adjusted background image
81   mEffectImage = FrameBufferImage::New( mMovementArea.width/4.f, mMovementArea.height/4.f, Pixel::RGBA8888, Dali::Image::Unused );
82
83   // Generate the material object, which is used by all meshActors
84   GenMaterial();
85
86   // Calculate how many BubbleEffect shaders are required
87   if( mTotalNumOfBubble>100 )
88   {
89     mNumBubblePerShader = 100;
90     mNumShader = mTotalNumOfBubble / 100;
91   }
92   else
93   {
94     mNumBubblePerShader = mTotalNumOfBubble;
95     mNumShader = 1;
96   }
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, mShapeImage.GetFilename() );
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, mShapeImage.GetFilename() );
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   //Get pixel width of the shape
175   float width = Image::GetImageSize(shapeImage.GetFilename()).width;
176
177   for(unsigned int i=0; i < mNumShader; i++ )
178   {
179     mEffect[i].SetShapeImageWidth(width);
180   }
181   mEffectForNoise.SetShapeImageWidth(width);
182 }
183
184 void BubbleEmitter::SetBubbleScale( float scale )
185 {
186   for(unsigned int i=0; i < mNumShader; i++ )
187   {
188     mEffect[i].SetDynamicScale( scale );
189   }
190   mEffectForNoise.SetDynamicScale( scale );
191 }
192
193 void BubbleEmitter::SetBubbleDensity( unsigned int density )
194 {
195   DALI_ASSERT_ALWAYS( density>0 && density<=9 && " Only densities between 1 to 9 are valid " );
196
197   if( density == mDensity )
198   {
199     return;
200   }
201   else
202   {
203     mDensity = density;
204     MeshData meshData;
205     ConstructBubbleMesh( meshData, mNumBubblePerShader*mDensity);
206     for(unsigned int i=0; i < mNumShader; i++ )
207     {
208       mMesh[i].UpdateMeshData(meshData);
209     }
210   }
211 }
212
213 // clear the resources created for the off screen rendering
214 void BubbleEmitter::OnRenderFinished(RenderTask& source)
215 {
216   mRenderTaskRunning = false;
217   Actor sourceActor = source.GetSourceActor();
218   if( sourceActor )
219   {
220     RenderableActor renderable = RenderableActor::DownCast( sourceActor );
221     if( renderable )
222     {
223       renderable.RemoveShaderEffect();
224     }
225   }
226
227   Stage stage = Stage::GetCurrent();
228   stage.Remove(sourceActor);
229   stage.GetRenderTaskList().RemoveTask(source);
230 }
231
232 void BubbleEmitter::OnContextRegained()
233 {
234   // Context was lost, so the framebuffer has been destroyed. Re-create render task
235   // and trigger re-draw if not already running
236   if( ! mRenderTaskRunning )
237   {
238     SetBackground( mBackgroundImage, mHSVDelta );
239   }
240 }
241
242 void BubbleEmitter::SetBlendMode( bool enable )
243 {
244   if(enable)
245   {
246     for(unsigned int i=0; i < mNumShader; i++ )
247     {
248       // linear overlay
249       // TODO: if BlendColor would be public api from renderable actor, then can optimize the constant color
250       mMeshActor[i].SetBlendFunc(BlendingFactor::SRC_ALPHA, BlendingFactor::ONE,
251                                  BlendingFactor::ZERO, BlendingFactor::ONE);
252     }
253     mMeshActorForNoise.SetBlendFunc(BlendingFactor::SRC_ALPHA, BlendingFactor::ONE,
254                                     BlendingFactor::ZERO, BlendingFactor::ONE);
255   }
256   else
257   {
258     for(unsigned int i=0; i < mNumShader; i++ )
259     {
260       // using default blend func
261       mMeshActor[i].SetBlendFunc( BlendingFactor::SRC_ALPHA,   BlendingFactor::ONE_MINUS_SRC_ALPHA,
262                                   BlendingFactor::ONE, BlendingFactor::ONE_MINUS_SRC_ALPHA );
263     }
264     mMeshActorForNoise.SetBlendFunc( BlendingFactor::SRC_ALPHA,   BlendingFactor::ONE_MINUS_SRC_ALPHA,
265                                      BlendingFactor::ONE, BlendingFactor::ONE_MINUS_SRC_ALPHA );
266   }
267 }
268
269 void BubbleEmitter::EmitBubble( Animation& animation, const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement )
270 {
271   unsigned int curUniform = mCurrentUniform  % mNumBubblePerShader;
272   unsigned int groupIdx = mCurrentUniform / mNumBubblePerShader;
273   SetBubbleParameter( mEffect[groupIdx], curUniform, emitPosition, direction, displacement);
274   animation.AnimateTo( Property( mEffect[groupIdx], mEffect[groupIdx].GetPercentagePropertyName(curUniform) ),
275                        1.f, AlphaFunctions::Linear );
276
277   if( mCurrentUniform % mNumShader == 0 )
278   {
279     unsigned int uniform = mCurrentUniform / mNumShader;
280     SetBubbleParameter(mEffectForNoise, uniform, emitPosition, displacement);
281     animation.AnimateTo( Property( mEffectForNoise, mEffectForNoise.GetPercentagePropertyName(uniform) ),
282                          1.f, AlphaFunctions::Linear );
283   }
284
285   mCurrentUniform = (mCurrentUniform + 1) % mTotalNumOfBubble;
286 }
287
288 void BubbleEmitter::StartExplosion( float duration, float multiple )
289 {
290   Animation animation = Animation::New( duration );
291   for(unsigned int i=0; i < mNumShader; i++ )
292   {
293     animation.AnimateTo( Property( mEffect[i], mEffect[i].GetMagnificationPropertyName() ),
294                          multiple, AlphaFunctions::EaseOut);
295   }
296   animation.AnimateTo( Property( mEffectForNoise, mEffectForNoise.GetMagnificationPropertyName() ),
297                        multiple, AlphaFunctions::EaseOut);
298   animation.Play();
299
300   animation.FinishedSignal().Connect(this, &BubbleEmitter::OnExplosionFinished);
301 }
302
303 void BubbleEmitter::Restore()
304 {
305   for(unsigned int i=0; i < mNumShader; i++ )
306   {
307     mEffect[i].ResetParameters();
308   }
309   mEffectForNoise.ResetParameters();
310 }
311
312 void BubbleEmitter::GenMaterial()
313 {
314   mCustomMaterial = Material::New("CustomMaterial");
315   mCustomMaterial.SetOpacity(1.0f);
316   mCustomMaterial.SetDiffuseColor(Color::WHITE);
317   mCustomMaterial.SetAmbientColor(Vector4(0.0, 0.1, 0.1, 1.0));
318   mCustomMaterial.SetMapU( Material::MAPPING_MODE_WRAP );
319   mCustomMaterial.SetMapV( Material::MAPPING_MODE_WRAP );
320   mCustomMaterial.SetDiffuseTexture( mShapeImage );
321 }
322
323 void BubbleEmitter::AddVertex(MeshData::VertexContainer& vertices, Vector3 XYZ, Vector2 UV)
324 {
325   MeshData::Vertex meshVertex;
326   meshVertex.x = XYZ.x;
327   meshVertex.y = XYZ.y;
328   meshVertex.z = XYZ.z;
329   meshVertex.u = UV.x;
330   meshVertex.v = UV.y;
331   vertices.push_back(meshVertex);
332 }
333
334 void BubbleEmitter::AddTriangle(MeshData::FaceIndices& faces,
335 size_t v0, size_t v1, size_t v2)
336 {
337   faces.push_back(v0);
338   faces.push_back(v1);
339   faces.push_back(v2);
340 }
341
342 void BubbleEmitter::ConstructBubbleMesh( MeshData& meshData, unsigned int numOfBubble)
343 {
344   MeshData::VertexContainer    vertices;
345   MeshData::FaceIndices        faces;
346   BoneContainer                bones(0);
347
348   for(unsigned int index = 0; index < numOfBubble; index ++)
349   {
350     float curSize = RandomRange(mBubbleSizeRange.x, mBubbleSizeRange.y);
351     if(rand()%100 < 1)
352     {
353       curSize *= 2.f;
354     }
355     float depth = static_cast<float>( index );
356     AddVertex( vertices, Vector3(0.f,0.f,depth), Vector2(0.f,0.f) );
357     AddVertex( vertices, Vector3(0.f,curSize,depth), Vector2( 0.f,1.f ));
358     AddVertex( vertices, Vector3(curSize,curSize,depth), Vector2(1.f,1.f) );
359     AddVertex( vertices, Vector3(curSize,0.f,depth), Vector2(1.f,0.f) );
360
361     unsigned int idx = index * 4;
362     AddTriangle( faces, idx, idx+1, idx+2);
363     AddTriangle( faces, idx, idx+2, idx+3);
364   }
365
366   meshData.SetData(vertices, faces, bones, mCustomMaterial);
367   meshData.SetHasColor(false);
368   meshData.SetHasTextureCoords(true);
369 }
370
371 void BubbleEmitter::SetBubbleParameter( BubbleEffect& effect, unsigned int curUniform,
372                                         const Vector2& emitPosition, const Vector2& displacement )
373 {
374   int halfRange = displacement.x / 2;
375   Vector2 randomVec(rand()%static_cast<int>(displacement.x) - halfRange, rand()%static_cast<int>(displacement.y) - halfRange);
376   if(randomVec.y > 0.0f)
377   {
378     randomVec.y *= 0.33f;
379   }
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::SetBubbleParameter( BubbleEffect& effect, unsigned int curUniform,
388                                         const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement )
389 {
390   Vector2 dir(direction);
391
392   int halfRange = displacement.x / 2;
393   // for the y coordinate, always negative, so bubbles always go upwards
394   Vector2 randomVec(rand()%static_cast<int>(displacement.x) - halfRange, -rand()%static_cast<int>(displacement.y));
395   dir.Normalize();
396   randomVec.x -= dir.x*halfRange;
397   randomVec.y *= 1.0f - fabsf(dir.x)*0.33f;
398
399   if(randomVec.y > 0.0f)
400   {
401     randomVec.y *= 0.33f;
402   }
403   Vector4 startAndEndPos( emitPosition.x, emitPosition.y, emitPosition.x+randomVec.x, emitPosition.y+randomVec.y );
404   effect.SetStartAndEndPosition( curUniform, startAndEndPos );
405
406   effect.SetPercentage( curUniform, 0.f);
407 }
408
409 void BubbleEmitter::OnExplosionFinished( Animation& source )
410 {
411   Restore();
412 }
413
414 float BubbleEmitter::RandomRange(float f0, float f1)
415 {
416   return f0 + (rand() & 0xfff) * (f1-f0) * (1.0f/4095.0f);
417 }
418
419 } // namespace Internal
420
421 } // namespace Toolkit
422
423 } // namespace Dali