689a3694020593da2bb86f6bce6330472c519d14
[platform/core/uifw/dali-demo.git] / examples / sparkle / sparkle-effect-example.cpp
1 /*
2  * Copyright (c) 2017 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 #include <dali/dali.h>
19 #include <dali-toolkit/dali-toolkit.h>
20
21 #include <sstream>
22 #include <algorithm>
23 #include <map>
24
25 #include "shared/utility.h"
26 #include "sparkle-effect.h"
27
28 using namespace Dali;
29 using Dali::Toolkit::ImageView;
30
31 using namespace SparkleEffect;
32
33 namespace // unnamed namespace
34 {
35
36 //background image for normal status
37 const char * const CIRCLE_BACKGROUND_IMAGE( DEMO_IMAGE_DIR "sparkle_normal_background.png" );
38 //particle shape image
39 const char * const PARTICLE_IMAGE( DEMO_IMAGE_DIR "sparkle_particle.png" );
40
41 float EaseOutSquare( float progress )
42 {
43   return 1.0f - (1.0f-progress) * (1.0f-progress);
44 }
45
46 float CustomBounce( float progress )
47 {
48   float p = 1.f-progress;
49   p *=p;
50   return 17.68f*p*p*p*progress;
51 }
52
53 float Mix( const Vector2& range, float a )
54 {
55   return range.x * a + range.y*(1.f-a)-0.001f;
56 }
57
58 const Vector4 BACKGROUND_COLOR( 0.f, 0.f, 0.05f, 1.f );
59
60 } // unnamed namespace
61
62 // This example shows a sparkle particle effect
63 //
64 class SparkleEffectExample : public ConnectionTracker
65 {
66 public:
67
68   /**
69    * Create the SparkleEffectExample
70    * @param[in] application The DALi application instance
71    */
72   SparkleEffectExample( Application& application )
73   : mApplication( application ),
74     mAnimationIndex( 0u ),
75     mShaking( false )
76   {
77     mApplication.InitSignal().Connect( this, &SparkleEffectExample::OnInit );
78   }
79
80 private:
81
82   /**
83    * Initialize the SparkleEffectExample
84    * @param[in] application The DALi application instance
85    */
86   void OnInit( Application& application )
87   {
88     Stage stage = Stage::GetCurrent();
89     stage.KeyEventSignal().Connect(this, &SparkleEffectExample::OnKeyEvent);
90     stage.SetBackgroundColor( BACKGROUND_COLOR );
91
92     mCircleBackground = ImageView::New( CIRCLE_BACKGROUND_IMAGE );
93     mCircleBackground.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
94     mCircleBackground.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
95
96     stage.Add( mCircleBackground );
97
98     mEffect = SparkleEffect::New();
99
100     mMeshActor = CreateMeshActor();
101
102     stage.Add( mMeshActor );
103
104     mMeshActor.SetPosition( ACTOR_POSITION );
105     mMeshActor.SetScale( ACTOR_SCALE );
106
107     mTapDetector = TapGestureDetector::New();
108     mTapDetector.Attach(mCircleBackground);
109     mTapDetector.DetectedSignal().Connect( this, &SparkleEffectExample::OnTap );
110
111     mPanGestureDetector = PanGestureDetector::New();
112     mPanGestureDetector.DetectedSignal().Connect( this, &SparkleEffectExample::OnPan );
113     mPanGestureDetector.Attach( mCircleBackground );
114
115     PlayWanderAnimation( 35.f );
116   }
117
118   /**
119    * Create the mesh representing all the particles
120    */
121   Actor CreateMeshActor()
122   {
123     // shuffling to assign the color in random order
124     unsigned int* shuffleArray = new unsigned int[NUM_PARTICLE];
125     for( unsigned int i = 0; i<NUM_PARTICLE; i++ )
126     {
127       shuffleArray[i] = i;
128     }
129     std::random_shuffle(&shuffleArray[0],&shuffleArray[NUM_PARTICLE]);
130
131     // Create vertices
132
133     std::vector< Vertex > vertices;
134     std::vector< unsigned short > faces;
135
136     for( unsigned int i = 0; i<NUM_PARTICLE; i++ )
137     {
138       float colorIndex = GetColorIndex( shuffleArray[i] );
139       AddParticletoMesh( vertices, faces, PATHS[i], colorIndex );
140     }
141
142     delete [] shuffleArray;
143
144     Property::Map vertexFormat;
145     vertexFormat["aTexCoord"] = Property::VECTOR2;
146     vertexFormat["aParticlePath0"] = Property::VECTOR2;
147     vertexFormat["aParticlePath1"] = Property::VECTOR2;
148     vertexFormat["aParticlePath2"] = Property::VECTOR2;
149     vertexFormat["aParticlePath3"] = Property::VECTOR2;
150     vertexFormat["aParticlePath4"] = Property::VECTOR2;
151     vertexFormat["aParticlePath5"] = Property::VECTOR2;
152
153     PropertyBuffer propertyBuffer = PropertyBuffer::New( vertexFormat );
154     propertyBuffer.SetData( &vertices[0], vertices.size() );
155
156     Geometry geometry = Geometry::New();
157     geometry.AddVertexBuffer( propertyBuffer );
158     geometry.SetIndexBuffer( &faces[0], faces.size() );
159     geometry.SetType( Geometry::TRIANGLES );
160
161     Texture particleTexture = DemoHelper::LoadTexture( PARTICLE_IMAGE );
162     TextureSet textureSet = TextureSet::New();
163     textureSet.SetTexture( 0u, particleTexture );
164
165     Renderer renderer = Renderer::New( geometry, mEffect );
166     renderer.SetTextures( textureSet );
167
168     Actor meshActor = Actor::New();
169     meshActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
170     meshActor.SetSize( 1, 1 );
171     meshActor.AddRenderer( renderer );
172
173     return meshActor;
174   }
175
176   /**
177    * Defines a rule to assign particle with a color according to its index
178    */
179   float GetColorIndex( unsigned int particleIndex )
180   {
181     unsigned int thereshold = 0;
182     for( unsigned int i = 0; i<NUM_COLOR; i++ )
183     {
184       thereshold += PARTICLE_COLORS[i].numParticle;
185       if( particleIndex < thereshold)
186       {
187         return i + Mix( PARTICLE_COLORS[i].AlphaRange, static_cast<float>(thereshold-particleIndex)/PARTICLE_COLORS[i].numParticle );
188       }
189     }
190     return NUM_COLOR-1;
191   }
192
193   /**
194    * All a particle to the mesh by giving the moving path and color index
195    *
196    * Two triangles per particle
197    *  0---------3
198    *   |\      |
199    *   |  \    |
200    *   |    \  |
201    *   |      \|
202    *  1---------2
203    *
204    * The information we need to pass in through attribute include:
205    *
206    *   path which contains 12 integer
207    *          ---- passed in 6 Vector2 attributes
208    *
209    *   color index, particle index and textureCoor( (0,0) or (1,0) or (0,1) or (1,1)  )
210    *          ---- package these info into texCood attribute as: (+-colorIndex, +-particleIndex)
211    */
212   void AddParticletoMesh( std::vector< Vertex >& vertices,
213                           std::vector< unsigned short >& faces,
214                           MovingPath& movingPath,
215                           float colorIndex )
216   {
217     unsigned int idx = vertices.size();
218
219     // store the path into position and normal, which would be decoded inside the shader
220     Vector2 particlePath0( movingPath[0],  movingPath[1] );
221     Vector2 particlePath1( movingPath[2],  movingPath[3] );
222     Vector2 particlePath2( movingPath[4],  movingPath[5] );
223     Vector2 particlePath3( movingPath[6],  movingPath[7] );
224     Vector2 particlePath4( movingPath[8],  movingPath[9] );
225     Vector2 particlePath5( movingPath[10], movingPath[11] );
226
227     float particleIdx = static_cast<float>(idx/4 + 1); // count from 1
228     float colorIdx = colorIndex+1.f; // count from 1
229     vertices.push_back( Vertex( Vector2(-colorIdx, -particleIdx), particlePath0, particlePath1, particlePath2, particlePath3, particlePath4, particlePath5 ) );
230     vertices.push_back( Vertex( Vector2(-colorIdx,  particleIdx), particlePath0, particlePath1, particlePath2, particlePath3, particlePath4, particlePath5 ) );
231     vertices.push_back( Vertex( Vector2( colorIdx,  particleIdx), particlePath0, particlePath1, particlePath2, particlePath3, particlePath4, particlePath5 ) );
232     vertices.push_back( Vertex( Vector2( colorIdx, -particleIdx), particlePath0, particlePath1, particlePath2, particlePath3, particlePath4, particlePath5 ) );
233
234     faces.push_back(idx);
235     faces.push_back(idx+1);
236     faces.push_back(idx+2);
237
238     faces.push_back(idx);
239     faces.push_back(idx+2);
240     faces.push_back(idx+3);
241   }
242
243   /*
244    * Main key event handler
245    */
246   void OnKeyEvent(const KeyEvent& event)
247   {
248     if(event.state == KeyEvent::Down)
249     {
250       if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
251       {
252         mApplication.Quit();
253       }
254     }
255   }
256
257   /**
258    * Callback of the TapGesture
259    */
260   void OnTap( Actor actor, const TapGesture& tap )
261   {
262     {
263       PlayTapAnimation(5.f, tap.localPoint);
264     }
265   }
266
267   /**
268    * Callback of the PanGesture
269    */
270   void OnPan( Actor actor, const PanGesture& gesture )
271   {
272     if( gesture.state == Gesture::Finished )
273     {
274       switch(mAnimationIndex)
275       {
276       case 0:
277       {
278         PlayParticleFadeAnimation(0, NUM_PARTICLE, 0.f, 3.f );
279         break;
280       }
281       case 1:
282       {
283         PlayBreakAnimation(2.0f);
284         break;
285       }
286       case 2:
287       {
288         PlayShakeAnimation(0.5f, 2.5f);
289         break;
290       }
291       default:
292       {
293         break;
294       }
295      }
296
297       mAnimationIndex = (mAnimationIndex+1)%3;
298     }
299   }
300
301   /**
302    * Animate the particle position to make them wandering on the screen with 'seemingly' random fade in/out
303    * @param[in] duration The duration for the particle to move a cycle on the path. the bigger this value the slower the floating movement.
304    * @param[in] looping Infinite playing or not
305    */
306   void PlayWanderAnimation( float duration, bool looping = true )
307   {
308     Animation wanderAnimation= Animation::New(duration);
309     wanderAnimation.AnimateTo( Property( mEffect, PERCENTAGE_UNIFORM_NAME ), 1.f );
310     wanderAnimation.SetLooping(looping); // infinite playing
311
312     wanderAnimation.Play();
313   }
314
315   /**
316    * Accelerate the particle moving speed
317    * @param[in] cycle How many extra cycles to move during the animation
318    * @param[in] duration The duration for the animation
319    */
320   void PlayShakeAnimation(  float cycle, float duration )
321   {
322     if( mShaking )
323     {
324       return;
325     }
326     DestroyAnimation( mTapAnimationAux );
327
328     float accelaration = GetFloatUniformValue( ACCELARATION_UNIFORM_NAME );
329     mEffect.SetProperty( mEffect.GetPropertyIndex(ACCELARATION_UNIFORM_NAME), accelaration - int( accelaration) ); // Set the value as its fractional part
330     Animation shakeAnimation = Animation::New(duration);
331     shakeAnimation.AnimateBy( Property( mEffect, ACCELARATION_UNIFORM_NAME ), cycle, AlphaFunction::EASE_OUT );
332     shakeAnimation.FinishedSignal().Connect( this, &SparkleEffectExample::OnShakeAnimationFinished );
333
334     shakeAnimation.Play();
335     mShaking = true;
336   }
337
338   /**
339    * Animate the particles to appear from center and spread all over around
340    * @param[in] duration The duration for the animation
341    */
342   void PlayBreakAnimation( float duration )
343   {
344     if( GetFloatUniformValue(BREAK_UNIFORM_NAME) > 0.f )
345     {
346       return;
347     }
348
349     // Stop the fading / tap animation before the breaking
350     DestroyAnimation( mFadeAnimation);
351     mTapIndices.x = mTapIndices.y;
352     mEffect.SetProperty( mEffect.GetPropertyIndex( TAP_INDICES_UNIFORM_NAME ), mTapIndices );
353     mEffect.SetProperty( mEffect.GetPropertyIndex( ACCELARATION_UNIFORM_NAME ), 0.f );
354
355     // prepare the animation by setting the uniform to the required value
356     mEffect.SetProperty( mEffect.GetPropertyIndex( BREAK_UNIFORM_NAME ), 1.f );
357     mMeshActor.SetScale(0.01f);
358     mEffect.SetProperty( mEffect.GetPropertyIndex( "uScale" ), 0.01f );
359     mMeshActor.SetPosition( 0.f, 0.f, 1.f );
360
361     Animation breakAnimation = Animation::New(duration*1.5f);
362     breakAnimation.AnimateTo( Property(mMeshActor, Actor::Property::SCALE), Vector3(ACTOR_SCALE,ACTOR_SCALE,ACTOR_SCALE), EaseOutSquare);
363     breakAnimation.AnimateTo( Property( mEffect, "uScale" ), ACTOR_SCALE, EaseOutSquare);
364     breakAnimation.AnimateTo( Property(mMeshActor, Actor::Property::POSITION), ACTOR_POSITION, EaseOutSquare);
365     breakAnimation.FinishedSignal().Connect( this, &SparkleEffectExample::OnBreakAnimationFinished );
366
367     float timeUnit = duration/ (NUM_PARTICLE+1) /(NUM_PARTICLE+1) ;
368     std::ostringstream oss;
369     for(unsigned int i = 0; i<NUM_PARTICLE; i++)
370     {
371       oss.str("");
372       oss<< OPACITY_UNIFORM_NAME<< i << "]";
373       mEffect.SetProperty( mEffect.GetPropertyIndex( oss.str() ), 0.01f);
374       float timeSlice = timeUnit*i*i;
375       breakAnimation.AnimateTo( Property( mEffect, oss.str() ), 1.f, AlphaFunction::EASE_IN_OUT_SINE, TimePeriod( timeSlice*0.5f, timeSlice ) );
376     }
377
378     breakAnimation.Play();
379   }
380
381   /**
382    * Animate the particle opacity
383    * Particles with index between startIndex ~ startIndex+numParticle-1 fade to the target opacity one after another
384    * @param[in] startIndex The index of the first particle
385    * @param[in] numParticle The number of particle to change opacity
386    * @param[in] targetValue The final opacity
387    * @param[in] duration The duration for the animation
388    */
389   void PlayParticleFadeAnimation( unsigned int startIndex, unsigned int numParticle, float targetValue, float duration )
390   {
391     if( GetFloatUniformValue(BREAK_UNIFORM_NAME) > 0.f )
392     {
393       return;
394     }
395
396     // start the opacity animation one particle after another gradually
397     float timeSlice = duration / (numParticle+1);
398     float fadeDuration = timeSlice>0.5f ? timeSlice : 0.5f;
399
400     Animation fadeAnimation= Animation::New(duration+fadeDuration*2.f);
401     std::ostringstream oss;
402     for(unsigned int i = startIndex; i<numParticle; i++)
403     {
404       if( i>=NUM_PARTICLE ) break; // out of bound
405
406       oss.str("");
407       oss<< OPACITY_UNIFORM_NAME<< i << "]";
408       fadeAnimation.AnimateTo(Property( mEffect, oss.str()), targetValue, TimePeriod( timeSlice*i, fadeDuration*2.f ));
409     }
410
411     fadeAnimation.Play();
412     mFadeAnimation = fadeAnimation;
413     mFadeAnimation.FinishedSignal().Connect( this, &SparkleEffectExample::OnFadeAnimationFinished );
414   }
415
416   /**
417    * Push the particles to the edge all around the circle then bounce back
418    * @param[in] duration The duration for the animation
419    * @param[in] tapPoint The position of the tap point
420    */
421   void PlayTapAnimation(float duration, const Vector2& tapPoint )
422   {
423     if( mTapIndices.y > mTapIndices.x && mTapAnimation.GetCurrentProgress() < 0.2f)
424     {
425       return;
426     }
427
428     Animation animation= Animation::New(duration);
429     int idx = int(mTapIndices.y)%MAXIMUM_ANIMATION_COUNT;
430     mTapIndices.y += 1.f;
431
432     std::ostringstream oss;
433     oss<< TAP_OFFSET_UNIFORM_NAME<< idx << "]";
434     mEffect.SetProperty( mEffect.GetPropertyIndex( oss.str() ), 0.f);
435     animation.AnimateTo( Property( mEffect, oss.str() ), 0.75f, CustomBounce);
436
437     oss.str("");
438     oss<< TAP_POINT_UNIFORM_NAME<< idx << "]";
439     mEffect.SetProperty( mEffect.GetPropertyIndex( oss.str() ), tapPoint/ACTOR_SCALE);
440
441     mEffect.SetProperty( mEffect.GetPropertyIndex( TAP_INDICES_UNIFORM_NAME ), mTapIndices);
442
443     if(!mShaking)
444     {
445       mTapAnimationAux = Animation::New(duration*0.2f);
446       mTapAnimationAux.AnimateBy( Property( mEffect, ACCELARATION_UNIFORM_NAME ), 0.15f, AlphaFunction::EASE_IN_OUT );
447       mTapAnimationAux.Play();
448     }
449     animation.Play();
450     mTapAnimationIndexPair[animation] = static_cast<int>(mTapIndices.y -1.f);
451     animation.FinishedSignal().Connect( this, &SparkleEffectExample::OnTapAnimationFinished );
452     mTapAnimation = animation;
453   }
454
455   /**
456    * Callback of the animation finished signal
457    */
458   void OnShakeAnimationFinished( Animation& animation)
459   {
460     mShaking = false;
461   }
462
463   /**
464    * Callback of the animation finished signal
465    */
466   void OnFadeAnimationFinished( Animation& animation)
467   {
468     mFadeAnimation.Clear();
469     mFadeAnimation.Reset();
470   }
471
472   /**
473    * Callback of the animation finished signal
474    */
475   void OnBreakAnimationFinished( Animation& animation)
476   {
477     mEffect.SetProperty( mEffect.GetPropertyIndex( BREAK_UNIFORM_NAME ), 0.f );
478   }
479
480   /**
481    * Callback of the animation finished signal
482    */
483   void OnTapAnimationFinished( Animation& animation )
484   {
485     if( mTapAnimationIndexPair[animation] ==  static_cast<int>(mTapIndices.x) )
486     {
487       mTapIndices.x += 1.f;
488       if( mTapIndices.x >= mTapIndices.y )
489       {
490         mTapIndices = Vector2::ZERO;
491       }
492       mEffect.SetProperty( mEffect.GetPropertyIndex( TAP_INDICES_UNIFORM_NAME ), mTapIndices);
493     }
494
495     mTapAnimationIndexPair.erase( animation );
496     if( mTapAnimationIndexPair.size() < 1 && mTapIndices != Vector2::ZERO)
497     {
498       mTapIndices = Vector2::ZERO;
499       mEffect.SetProperty( mEffect.GetPropertyIndex( TAP_INDICES_UNIFORM_NAME ), mTapIndices);
500     }
501
502     animation.Clear();
503     animation.Reset();
504   }
505
506   /**
507    * Helper retrieve a uniform value from the Sparkle effect shader
508    * @param[in] uniformName The uniform
509    * @return The float value
510    */
511   float GetFloatUniformValue( const std::string& uniformName )
512   {
513     float value;
514     mEffect.GetProperty(mEffect.GetPropertyIndex(uniformName)).Get(value);
515     return value;
516   }
517
518   /**
519    * Terminate the given animation
520    */
521   void DestroyAnimation( Animation& animation )
522   {
523     if( animation )
524     {
525       animation.Clear();
526       animation.Reset();
527     }
528   }
529
530 private:
531
532   Application&       mApplication;
533   Shader             mEffect;
534   ImageView          mCircleBackground;
535   Actor              mMeshActor;
536
537   PanGestureDetector mPanGestureDetector;
538   TapGestureDetector mTapDetector;
539
540   Animation          mFadeAnimation;
541   Animation          mTapAnimation;
542   Animation          mTapAnimationAux;
543
544   Vector2            mTapIndices;
545   unsigned int       mAnimationIndex;
546   bool               mShaking;
547
548   std::map< Animation, int > mTapAnimationIndexPair;
549 };
550
551 int DALI_EXPORT_API main( int argc, char **argv )
552 {
553   Application application = Application::New( &argc, &argv );
554   SparkleEffectExample theApp( application );
555   application.MainLoop();
556   return 0;
557 }
558