2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <dali/dali.h>
19 #include <dali-toolkit/dali-toolkit.h>
24 #include <random> // std::default_random_engine
25 #include <chrono> // std::chrono::system_clock
27 #include "shared/utility.h"
28 #include "sparkle-effect.h"
31 using Dali::Toolkit::ImageView;
33 using namespace SparkleEffect;
35 namespace // unnamed namespace
38 //background image for normal status
39 const char * const CIRCLE_BACKGROUND_IMAGE( DEMO_IMAGE_DIR "sparkle_normal_background.png" );
40 //particle shape image
41 const char * const PARTICLE_IMAGE( DEMO_IMAGE_DIR "sparkle_particle.png" );
43 float EaseOutSquare( float progress )
45 return 1.0f - (1.0f-progress) * (1.0f-progress);
48 float CustomBounce( float progress )
50 float p = 1.f-progress;
52 return 17.68f*p*p*p*progress;
55 float Mix( const Vector2& range, float a )
57 return range.x * a + range.y*(1.f-a)-0.001f;
60 const Vector4 BACKGROUND_COLOR( 0.f, 0.f, 0.05f, 1.f );
62 } // unnamed namespace
64 // This example shows a sparkle particle effect
66 class SparkleEffectExample : public ConnectionTracker
71 * Create the SparkleEffectExample
72 * @param[in] application The DALi application instance
74 SparkleEffectExample( Application& application )
75 : mApplication( application ),
76 mAnimationIndex( 0u ),
79 mApplication.InitSignal().Connect( this, &SparkleEffectExample::OnInit );
85 * Initialize the SparkleEffectExample
86 * @param[in] application The DALi application instance
88 void OnInit( Application& application )
90 Window window = application.GetWindow();
91 window.KeyEventSignal().Connect(this, &SparkleEffectExample::OnKeyEvent);
92 window.SetBackgroundColor( BACKGROUND_COLOR );
94 mCircleBackground = ImageView::New( CIRCLE_BACKGROUND_IMAGE );
95 mCircleBackground.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
96 mCircleBackground.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
98 window.Add( mCircleBackground );
100 mEffect = SparkleEffect::New();
102 mMeshActor = CreateMeshActor();
104 window.Add( mMeshActor );
106 mMeshActor.SetProperty( Actor::Property::POSITION, ACTOR_POSITION );
107 mMeshActor.SetProperty( Actor::Property::SCALE, ACTOR_SCALE );
109 mTapDetector = TapGestureDetector::New();
110 mTapDetector.Attach(mCircleBackground);
111 mTapDetector.DetectedSignal().Connect( this, &SparkleEffectExample::OnTap );
113 mPanGestureDetector = PanGestureDetector::New();
114 mPanGestureDetector.DetectedSignal().Connect( this, &SparkleEffectExample::OnPan );
115 mPanGestureDetector.Attach( mCircleBackground );
117 PlayWanderAnimation( 35.f );
121 * Create the mesh representing all the particles
123 Actor CreateMeshActor()
125 // shuffling to assign the color in random order
126 unsigned int* shuffleArray = new unsigned int[NUM_PARTICLE];
127 for( unsigned int i = 0; i<NUM_PARTICLE; i++ )
131 const unsigned int seed = std::chrono::system_clock::now().time_since_epoch().count();
132 std::shuffle(&shuffleArray[0],&shuffleArray[NUM_PARTICLE], std::default_random_engine(seed));
136 std::vector< Vertex > vertices;
137 std::vector< unsigned short > faces;
139 for( unsigned int i = 0; i<NUM_PARTICLE; i++ )
141 float colorIndex = GetColorIndex( shuffleArray[i] );
142 AddParticletoMesh( vertices, faces, PATHS[i], colorIndex );
145 delete [] shuffleArray;
147 Property::Map vertexFormat;
148 vertexFormat["aTexCoord"] = Property::VECTOR2;
149 vertexFormat["aParticlePath0"] = Property::VECTOR2;
150 vertexFormat["aParticlePath1"] = Property::VECTOR2;
151 vertexFormat["aParticlePath2"] = Property::VECTOR2;
152 vertexFormat["aParticlePath3"] = Property::VECTOR2;
153 vertexFormat["aParticlePath4"] = Property::VECTOR2;
154 vertexFormat["aParticlePath5"] = Property::VECTOR2;
156 VertexBuffer vertexBuffer = VertexBuffer::New( vertexFormat );
157 vertexBuffer.SetData( &vertices[0], vertices.size() );
159 Geometry geometry = Geometry::New();
160 geometry.AddVertexBuffer( vertexBuffer );
161 geometry.SetIndexBuffer( &faces[0], faces.size() );
162 geometry.SetType( Geometry::TRIANGLES );
164 Texture particleTexture = DemoHelper::LoadTexture( PARTICLE_IMAGE );
165 TextureSet textureSet = TextureSet::New();
166 textureSet.SetTexture( 0u, particleTexture );
168 Renderer renderer = Renderer::New( geometry, mEffect );
169 renderer.SetTextures( textureSet );
171 Actor meshActor = Actor::New();
172 meshActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
173 meshActor.SetProperty( Actor::Property::SIZE, Vector2( 1, 1 ) );
174 meshActor.AddRenderer( renderer );
180 * Defines a rule to assign particle with a color according to its index
182 float GetColorIndex( unsigned int particleIndex )
184 unsigned int thereshold = 0;
185 for( unsigned int i = 0; i<NUM_COLOR; i++ )
187 thereshold += PARTICLE_COLORS[i].numParticle;
188 if( particleIndex < thereshold)
190 return i + Mix( PARTICLE_COLORS[i].AlphaRange, static_cast<float>(thereshold-particleIndex)/PARTICLE_COLORS[i].numParticle );
197 * All a particle to the mesh by giving the moving path and color index
199 * Two triangles per particle
207 * The information we need to pass in through attribute include:
209 * path which contains 12 integer
210 * ---- passed in 6 Vector2 attributes
212 * color index, particle index and textureCoor( (0,0) or (1,0) or (0,1) or (1,1) )
213 * ---- package these info into texCood attribute as: (+-colorIndex, +-particleIndex)
215 void AddParticletoMesh( std::vector< Vertex >& vertices,
216 std::vector< unsigned short >& faces,
217 MovingPath& movingPath,
220 unsigned int idx = vertices.size();
222 // store the path into position and normal, which would be decoded inside the shader
223 Vector2 particlePath0( movingPath[0], movingPath[1] );
224 Vector2 particlePath1( movingPath[2], movingPath[3] );
225 Vector2 particlePath2( movingPath[4], movingPath[5] );
226 Vector2 particlePath3( movingPath[6], movingPath[7] );
227 Vector2 particlePath4( movingPath[8], movingPath[9] );
228 Vector2 particlePath5( movingPath[10], movingPath[11] );
230 float particleIdx = static_cast<float>(idx/4 + 1); // count from 1
231 float colorIdx = colorIndex+1.f; // count from 1
232 vertices.push_back( Vertex( Vector2(-colorIdx, -particleIdx), particlePath0, particlePath1, particlePath2, particlePath3, particlePath4, particlePath5 ) );
233 vertices.push_back( Vertex( Vector2(-colorIdx, particleIdx), particlePath0, particlePath1, particlePath2, particlePath3, particlePath4, particlePath5 ) );
234 vertices.push_back( Vertex( Vector2( colorIdx, particleIdx), particlePath0, particlePath1, particlePath2, particlePath3, particlePath4, particlePath5 ) );
235 vertices.push_back( Vertex( Vector2( colorIdx, -particleIdx), particlePath0, particlePath1, particlePath2, particlePath3, particlePath4, particlePath5 ) );
237 faces.push_back(idx);
238 faces.push_back(idx+1);
239 faces.push_back(idx+2);
241 faces.push_back(idx);
242 faces.push_back(idx+2);
243 faces.push_back(idx+3);
247 * Main key event handler
249 void OnKeyEvent(const KeyEvent& event)
251 if(event.GetState() == KeyEvent::DOWN)
253 if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
261 * Callback of the TapGesture
263 void OnTap( Actor actor, const TapGesture& tap )
266 PlayTapAnimation(5.f, tap.localPoint);
271 * Callback of the PanGesture
273 void OnPan( Actor actor, const PanGesture& gesture )
275 if( gesture.state == Gesture::Finished )
277 switch(mAnimationIndex)
281 PlayParticleFadeAnimation(0, NUM_PARTICLE, 0.f, 3.f );
286 PlayBreakAnimation(2.0f);
291 PlayShakeAnimation(0.5f, 2.5f);
300 mAnimationIndex = (mAnimationIndex+1)%3;
305 * Animate the particle position to make them wandering on the screen with 'seemingly' random fade in/out
306 * @param[in] duration The duration for the particle to move a cycle on the path. the bigger this value the slower the floating movement.
307 * @param[in] looping Infinite playing or not
309 void PlayWanderAnimation( float duration, bool looping = true )
311 Animation wanderAnimation= Animation::New(duration);
312 wanderAnimation.AnimateTo( Property( mEffect, PERCENTAGE_UNIFORM_NAME ), 1.f );
313 wanderAnimation.SetLooping(looping); // infinite playing
315 wanderAnimation.Play();
319 * Accelerate the particle moving speed
320 * @param[in] cycle How many extra cycles to move during the animation
321 * @param[in] duration The duration for the animation
323 void PlayShakeAnimation( float cycle, float duration )
329 DestroyAnimation( mTapAnimationAux );
331 float accelaration = GetFloatUniformValue( ACCELARATION_UNIFORM_NAME );
332 mEffect.SetProperty( mEffect.GetPropertyIndex(ACCELARATION_UNIFORM_NAME), accelaration - int( accelaration) ); // Set the value as its fractional part
333 Animation shakeAnimation = Animation::New(duration);
334 shakeAnimation.AnimateBy( Property( mEffect, ACCELARATION_UNIFORM_NAME ), cycle, AlphaFunction::EASE_OUT );
335 shakeAnimation.FinishedSignal().Connect( this, &SparkleEffectExample::OnShakeAnimationFinished );
337 shakeAnimation.Play();
342 * Animate the particles to appear from center and spread all over around
343 * @param[in] duration The duration for the animation
345 void PlayBreakAnimation( float duration )
347 if( GetFloatUniformValue(BREAK_UNIFORM_NAME) > 0.f )
352 // Stop the fading / tap animation before the breaking
353 DestroyAnimation( mFadeAnimation);
354 mTapIndices.x = mTapIndices.y;
355 mEffect.SetProperty( mEffect.GetPropertyIndex( TAP_INDICES_UNIFORM_NAME ), mTapIndices );
356 mEffect.SetProperty( mEffect.GetPropertyIndex( ACCELARATION_UNIFORM_NAME ), 0.f );
358 // prepare the animation by setting the uniform to the required value
359 mEffect.SetProperty( mEffect.GetPropertyIndex( BREAK_UNIFORM_NAME ), 1.f );
360 mMeshActor.SetProperty( Actor::Property::SCALE,0.01f);
361 mEffect.SetProperty( mEffect.GetPropertyIndex( "uScale" ), 0.01f );
362 mMeshActor.SetProperty( Actor::Property::POSITION, Vector3( 0.f, 0.f, 1.f ) );
364 Animation breakAnimation = Animation::New(duration*1.5f);
365 breakAnimation.AnimateTo( Property(mMeshActor, Actor::Property::SCALE), Vector3(ACTOR_SCALE,ACTOR_SCALE,ACTOR_SCALE), EaseOutSquare);
366 breakAnimation.AnimateTo( Property( mEffect, "uScale" ), ACTOR_SCALE, EaseOutSquare);
367 breakAnimation.AnimateTo( Property(mMeshActor, Actor::Property::POSITION), ACTOR_POSITION, EaseOutSquare);
368 breakAnimation.FinishedSignal().Connect( this, &SparkleEffectExample::OnBreakAnimationFinished );
370 float timeUnit = duration/ (NUM_PARTICLE+1) /(NUM_PARTICLE+1) ;
371 std::ostringstream oss;
372 for(unsigned int i = 0; i<NUM_PARTICLE; i++)
375 oss<< OPACITY_UNIFORM_NAME<< i << "]";
376 mEffect.SetProperty( mEffect.GetPropertyIndex( oss.str() ), 0.01f);
377 float timeSlice = timeUnit*i*i;
378 breakAnimation.AnimateTo( Property( mEffect, oss.str() ), 1.f, AlphaFunction::EASE_IN_OUT_SINE, TimePeriod( timeSlice*0.5f, timeSlice ) );
381 breakAnimation.Play();
385 * Animate the particle opacity
386 * Particles with index between startIndex ~ startIndex+numParticle-1 fade to the target opacity one after another
387 * @param[in] startIndex The index of the first particle
388 * @param[in] numParticle The number of particle to change opacity
389 * @param[in] targetValue The final opacity
390 * @param[in] duration The duration for the animation
392 void PlayParticleFadeAnimation( unsigned int startIndex, unsigned int numParticle, float targetValue, float duration )
394 if( GetFloatUniformValue(BREAK_UNIFORM_NAME) > 0.f )
399 // start the opacity animation one particle after another gradually
400 float timeSlice = duration / (numParticle+1);
401 float fadeDuration = timeSlice>0.5f ? timeSlice : 0.5f;
403 Animation fadeAnimation= Animation::New(duration+fadeDuration*2.f);
404 std::ostringstream oss;
405 for(unsigned int i = startIndex; i<numParticle; i++)
407 if( i>=NUM_PARTICLE ) break; // out of bound
410 oss<< OPACITY_UNIFORM_NAME<< i << "]";
411 fadeAnimation.AnimateTo(Property( mEffect, oss.str()), targetValue, TimePeriod( timeSlice*i, fadeDuration*2.f ));
414 fadeAnimation.Play();
415 mFadeAnimation = fadeAnimation;
416 mFadeAnimation.FinishedSignal().Connect( this, &SparkleEffectExample::OnFadeAnimationFinished );
420 * Push the particles to the edge all around the circle then bounce back
421 * @param[in] duration The duration for the animation
422 * @param[in] tapPoint The position of the tap point
424 void PlayTapAnimation(float duration, const Vector2& tapPoint )
426 if( mTapIndices.y > mTapIndices.x && mTapAnimation.GetCurrentProgress() < 0.2f)
431 Animation animation= Animation::New(duration);
432 int idx = int(mTapIndices.y)%MAXIMUM_ANIMATION_COUNT;
433 mTapIndices.y += 1.f;
435 std::ostringstream oss;
436 oss<< TAP_OFFSET_UNIFORM_NAME<< idx << "]";
437 mEffect.SetProperty( mEffect.GetPropertyIndex( oss.str() ), 0.f);
438 animation.AnimateTo( Property( mEffect, oss.str() ), 0.75f, CustomBounce);
441 oss<< TAP_POINT_UNIFORM_NAME<< idx << "]";
442 mEffect.SetProperty( mEffect.GetPropertyIndex( oss.str() ), tapPoint/ACTOR_SCALE);
444 mEffect.SetProperty( mEffect.GetPropertyIndex( TAP_INDICES_UNIFORM_NAME ), mTapIndices);
448 mTapAnimationAux = Animation::New(duration*0.2f);
449 mTapAnimationAux.AnimateBy( Property( mEffect, ACCELARATION_UNIFORM_NAME ), 0.15f, AlphaFunction::EASE_IN_OUT );
450 mTapAnimationAux.Play();
453 mTapAnimationIndexPair[animation] = static_cast<int>(mTapIndices.y -1.f);
454 animation.FinishedSignal().Connect( this, &SparkleEffectExample::OnTapAnimationFinished );
455 mTapAnimation = animation;
459 * Callback of the animation finished signal
461 void OnShakeAnimationFinished( Animation& animation)
467 * Callback of the animation finished signal
469 void OnFadeAnimationFinished( Animation& animation)
471 mFadeAnimation.Clear();
472 mFadeAnimation.Reset();
476 * Callback of the animation finished signal
478 void OnBreakAnimationFinished( Animation& animation)
480 mEffect.SetProperty( mEffect.GetPropertyIndex( BREAK_UNIFORM_NAME ), 0.f );
484 * Callback of the animation finished signal
486 void OnTapAnimationFinished( Animation& animation )
488 if( mTapAnimationIndexPair[animation] == static_cast<int>(mTapIndices.x) )
490 mTapIndices.x += 1.f;
491 if( mTapIndices.x >= mTapIndices.y )
493 mTapIndices = Vector2::ZERO;
495 mEffect.SetProperty( mEffect.GetPropertyIndex( TAP_INDICES_UNIFORM_NAME ), mTapIndices);
498 mTapAnimationIndexPair.erase( animation );
499 if( mTapAnimationIndexPair.size() < 1 && mTapIndices != Vector2::ZERO)
501 mTapIndices = Vector2::ZERO;
502 mEffect.SetProperty( mEffect.GetPropertyIndex( TAP_INDICES_UNIFORM_NAME ), mTapIndices);
510 * Helper retrieve a uniform value from the Sparkle effect shader
511 * @param[in] uniformName The uniform
512 * @return The float value
514 float GetFloatUniformValue( const std::string& uniformName )
517 mEffect.GetProperty(mEffect.GetPropertyIndex(uniformName)).Get(value);
522 * Terminate the given animation
524 void DestroyAnimation( Animation& animation )
535 Application& mApplication;
537 ImageView mCircleBackground;
540 PanGestureDetector mPanGestureDetector;
541 TapGestureDetector mTapDetector;
543 Animation mFadeAnimation;
544 Animation mTapAnimation;
545 Animation mTapAnimationAux;
548 unsigned int mAnimationIndex;
551 std::map< Animation, int > mTapAnimationIndexPair;
554 int DALI_EXPORT_API main( int argc, char **argv )
556 Application application = Application::New( &argc, &argv );
557 SparkleEffectExample theApp( application );
558 application.MainLoop();