2 * Copyright (c) 2016 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>
25 #include "shared/utility.h"
26 #include "sparkle-effect.h"
29 using Dali::Toolkit::ImageView;
31 using namespace SparkleEffect;
33 namespace // unnamed namespace
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" );
41 float EaseOutSquare( float progress )
43 return 1.0f - (1.0f-progress) * (1.0f-progress);
46 float CustomBounce( float progress )
48 float p = 1.f-progress;
50 return 17.68f*p*p*p*progress;
53 float Mix( const Vector2& range, float a )
55 return range.x * a + range.y*(1.f-a)-0.001f;
58 const Vector4 BACKGROUND_COLOR( 0.f, 0.f, 0.05f, 1.f );
60 } // unnamed namespace
62 // This example shows a sparkle particle effect
64 class SparkleEffectExample : public ConnectionTracker
69 * Create the SparkleEffectExample
70 * @param[in] application The DALi application instance
72 SparkleEffectExample( Application& application )
73 : mApplication( application ),
74 mAnimationIndex( 0u ),
77 mApplication.InitSignal().Connect( this, &SparkleEffectExample::OnInit );
83 * Initialize the SparkleEffectExample
84 * @param[in] application The DALi application instance
86 void OnInit( Application& application )
88 Stage stage = Stage::GetCurrent();
89 stage.KeyEventSignal().Connect(this, &SparkleEffectExample::OnKeyEvent);
90 stage.SetBackgroundColor( BACKGROUND_COLOR );
92 mCircleBackground = ImageView::New( CIRCLE_BACKGROUND_IMAGE );
93 mCircleBackground.SetParentOrigin( ParentOrigin::CENTER );
94 mCircleBackground.SetAnchorPoint( AnchorPoint::CENTER );
96 stage.Add( mCircleBackground );
98 mEffect = SparkleEffect::New();
100 mMeshActor = CreateMeshActor();
102 stage.Add( mMeshActor );
104 mMeshActor.SetPosition( ACTOR_POSITION );
105 mMeshActor.SetScale( ACTOR_SCALE );
107 mTapDetector = TapGestureDetector::New();
108 mTapDetector.Attach(mCircleBackground);
109 mTapDetector.DetectedSignal().Connect( this, &SparkleEffectExample::OnTap );
111 mPanGestureDetector = PanGestureDetector::New();
112 mPanGestureDetector.DetectedSignal().Connect( this, &SparkleEffectExample::OnPan );
113 mPanGestureDetector.Attach( mCircleBackground );
115 PlayWanderAnimation( 35.f );
119 * Create the mesh representing all the particles
121 Actor CreateMeshActor()
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++ )
129 std::random_shuffle(&shuffleArray[0],&shuffleArray[NUM_PARTICLE]);
133 std::vector< Vertex > vertices;
134 std::vector< unsigned short > faces;
136 for( unsigned int i = 0; i<NUM_PARTICLE; i++ )
138 float colorIndex = GetColorIndex( shuffleArray[i] );
139 AddParticletoMesh( vertices, faces, PATHS[i], colorIndex );
142 delete [] shuffleArray;
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;
153 PropertyBuffer propertyBuffer = PropertyBuffer::New( vertexFormat );
154 propertyBuffer.SetData( &vertices[0], vertices.size() );
156 Geometry geometry = Geometry::New();
157 geometry.AddVertexBuffer( propertyBuffer );
158 geometry.SetIndexBuffer( &faces[0], faces.size() );
159 geometry.SetType( Geometry::TRIANGLES );
161 Texture particleTexture = DemoHelper::LoadTexture( PARTICLE_IMAGE );
162 TextureSet textureSet = TextureSet::New();
163 textureSet.SetTexture( 0u, particleTexture );
165 Renderer renderer = Renderer::New( geometry, mEffect );
166 renderer.SetTextures( textureSet );
168 Actor meshActor = Actor::New();
169 meshActor.SetParentOrigin( ParentOrigin::CENTER );
170 meshActor.SetSize( 1, 1 );
171 meshActor.AddRenderer( renderer );
177 * Defines a rule to assign particle with a color according to its index
179 float GetColorIndex( unsigned int particleIndex )
181 unsigned int thereshold = 0;
182 for( unsigned int i = 0; i<NUM_COLOR; i++ )
184 thereshold += PARTICLE_COLORS[i].numParticle;
185 if( particleIndex < thereshold)
187 return i + Mix( PARTICLE_COLORS[i].AlphaRange, static_cast<float>(thereshold-particleIndex)/PARTICLE_COLORS[i].numParticle );
194 * All a particle to the mesh by giving the moving path and color index
196 * Two triangles per particle
204 * The information we need to pass in through attribute include:
206 * path which contains 12 integer
207 * ---- passed in 6 Vector2 attributes
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)
212 void AddParticletoMesh( std::vector< Vertex >& vertices,
213 std::vector< unsigned short >& faces,
214 MovingPath& movingPath,
217 unsigned int idx = vertices.size();
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] );
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 ) );
234 faces.push_back(idx);
235 faces.push_back(idx+1);
236 faces.push_back(idx+2);
238 faces.push_back(idx);
239 faces.push_back(idx+2);
240 faces.push_back(idx+3);
244 * Main key event handler
246 void OnKeyEvent(const KeyEvent& event)
248 if(event.state == KeyEvent::Down)
250 if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
258 * Callback of the TapGesture
260 void OnTap( Actor actor, const TapGesture& tap )
263 PlayTapAnimation(5.f, tap.localPoint);
268 * Callback of the PanGesture
270 void OnPan( Actor actor, const PanGesture& gesture )
272 if( gesture.state == Gesture::Finished )
274 switch(mAnimationIndex)
278 PlayParticleFadeAnimation(0, NUM_PARTICLE, 0.f, 3.f );
283 PlayBreakAnimation(2.0f);
288 PlayShakeAnimation(0.5f, 2.5f);
297 mAnimationIndex = (mAnimationIndex+1)%3;
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
306 void PlayWanderAnimation( float duration, bool looping = true )
308 Animation wanderAnimation= Animation::New(duration);
309 wanderAnimation.AnimateTo( Property( mEffect, PERCENTAGE_UNIFORM_NAME ), 1.f );
310 wanderAnimation.SetLooping(looping); // infinite playing
312 wanderAnimation.Play();
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
320 void PlayShakeAnimation( float cycle, float duration )
326 DestroyAnimation( mTapAnimationAux );
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 );
334 shakeAnimation.Play();
339 * Animate the particles to appear from center and spread all over around
340 * @param[in] duration The duration for the animation
342 void PlayBreakAnimation( float duration )
344 if( GetFloatUniformValue(BREAK_UNIFORM_NAME) > 0.f )
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 );
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 );
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 );
367 float timeUnit = duration/ (NUM_PARTICLE+1) /(NUM_PARTICLE+1) ;
368 std::ostringstream oss;
369 for(unsigned int i = 0; i<NUM_PARTICLE; i++)
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 ) );
378 breakAnimation.Play();
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
389 void PlayParticleFadeAnimation( unsigned int startIndex, unsigned int numParticle, float targetValue, float duration )
391 if( GetFloatUniformValue(BREAK_UNIFORM_NAME) > 0.f )
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;
400 Animation fadeAnimation= Animation::New(duration+fadeDuration*2.f);
401 std::ostringstream oss;
402 for(unsigned int i = startIndex; i<numParticle; i++)
404 if( i>=NUM_PARTICLE ) break; // out of bound
407 oss<< OPACITY_UNIFORM_NAME<< i << "]";
408 fadeAnimation.AnimateTo(Property( mEffect, oss.str()), targetValue, TimePeriod( timeSlice*i, fadeDuration*2.f ));
411 fadeAnimation.Play();
412 mFadeAnimation = fadeAnimation;
413 mFadeAnimation.FinishedSignal().Connect( this, &SparkleEffectExample::OnFadeAnimationFinished );
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
421 void PlayTapAnimation(float duration, const Vector2& tapPoint )
423 if( mTapIndices.y > mTapIndices.x && mTapAnimation.GetCurrentProgress() < 0.2f)
428 Animation animation= Animation::New(duration);
429 int idx = int(mTapIndices.y)%MAXIMUM_ANIMATION_COUNT;
430 mTapIndices.y += 1.f;
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);
438 oss<< TAP_POINT_UNIFORM_NAME<< idx << "]";
439 mEffect.SetProperty( mEffect.GetPropertyIndex( oss.str() ), tapPoint/ACTOR_SCALE);
441 mEffect.SetProperty( mEffect.GetPropertyIndex( TAP_INDICES_UNIFORM_NAME ), mTapIndices);
445 mTapAnimationAux = Animation::New(duration*0.2f);
446 mTapAnimationAux.AnimateBy( Property( mEffect, ACCELARATION_UNIFORM_NAME ), 0.15f, AlphaFunction::EASE_IN_OUT );
447 mTapAnimationAux.Play();
450 mTapAnimationIndexPair[animation] = static_cast<int>(mTapIndices.y -1.f);
451 animation.FinishedSignal().Connect( this, &SparkleEffectExample::OnTapAnimationFinished );
452 mTapAnimation = animation;
456 * Callback of the animation finished signal
458 void OnShakeAnimationFinished( Animation& animation)
464 * Callback of the animation finished signal
466 void OnFadeAnimationFinished( Animation& animation)
468 mFadeAnimation.Clear();
469 mFadeAnimation.Reset();
473 * Callback of the animation finished signal
475 void OnBreakAnimationFinished( Animation& animation)
477 mEffect.SetProperty( mEffect.GetPropertyIndex( BREAK_UNIFORM_NAME ), 0.f );
481 * Callback of the animation finished signal
483 void OnTapAnimationFinished( Animation& animation )
485 if( mTapAnimationIndexPair[animation] == static_cast<int>(mTapIndices.x) )
487 mTapIndices.x += 1.f;
488 if( mTapIndices.x >= mTapIndices.y )
490 mTapIndices = Vector2::ZERO;
492 mEffect.SetProperty( mEffect.GetPropertyIndex( TAP_INDICES_UNIFORM_NAME ), mTapIndices);
495 mTapAnimationIndexPair.erase( animation );
496 if( mTapAnimationIndexPair.size() < 1 && mTapIndices != Vector2::ZERO)
498 mTapIndices = Vector2::ZERO;
499 mEffect.SetProperty( mEffect.GetPropertyIndex( TAP_INDICES_UNIFORM_NAME ), mTapIndices);
507 * Helper retrieve a uniform value from the Sparkle effect shader
508 * @param[in] uniformName The uniform
509 * @return The float value
511 float GetFloatUniformValue( const std::string& uniformName )
514 mEffect.GetProperty(mEffect.GetPropertyIndex(uniformName)).Get(value);
519 * Terminate the given animation
521 void DestroyAnimation( Animation& animation )
532 Application& mApplication;
534 ImageView mCircleBackground;
537 PanGestureDetector mPanGestureDetector;
538 TapGestureDetector mTapDetector;
540 Animation mFadeAnimation;
541 Animation mTapAnimation;
542 Animation mTapAnimationAux;
545 unsigned int mAnimationIndex;
548 std::map< Animation, int > mTapAnimationIndexPair;
551 void RunTest( Application& application )
553 SparkleEffectExample theApp( application );
555 application.MainLoop();
558 // Entry point for Linux & Tizen applications
560 int DALI_EXPORT_API main( int argc, char **argv )
562 Application application = Application::New( &argc, &argv );
564 RunTest( application );