22b150fe9b943ac35e3238a489e48e67e31295b8
[platform/core/uifw/dali-demo.git] / examples / metaball-explosion / metaball-explosion-example.cpp
1 /*
2  * Copyright (c) 2020 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 // EXTERNAL INCLUDES
19 #include <cstdio>
20 #include <string>
21 #include <cstdint> // uint32_t, uint16_t etc
22
23 #include <dali/public-api/rendering/renderer.h>
24 #include <dali/public-api/rendering/texture.h>
25 #include <dali/public-api/rendering/texture-set.h>
26 #include <dali/public-api/rendering/frame-buffer.h>
27 #include <dali/public-api/math/random.h>
28
29 // INTERNAL INCLUDES
30 #include "shared/utility.h" // DemoHelper::LoadTexture
31
32 using namespace Dali;
33
34 namespace // unnamed namespace for constants
35 {
36 // background image
37 const char * const BACKGROUND_IMAGE( DEMO_IMAGE_DIR "background-2.jpg" );
38
39 // number of metaballs
40 constexpr uint32_t METABALL_NUMBER = 6;
41
42 /**
43  * Vertex shader code for metaball
44  */
45 const char* const METABALL_VERTEX_SHADER = DALI_COMPOSE_SHADER (
46     attribute mediump vec2    aPosition;\n
47     attribute mediump vec2    aTexture;\n
48     uniform   mediump mat4    uMvpMatrix;\n
49     uniform   mediump vec3    uSize;\n
50     uniform   lowp    vec4    uColor;\n
51     varying   mediump vec2    vTexCoord;\n
52
53     void main()\n
54     {\n
55       vTexCoord = aTexture;\n
56       mediump vec4 vertexPosition = vec4(aPosition.x, aPosition.y, 0.0, 1.0);\n
57       gl_Position = uMvpMatrix * vertexPosition;\n
58     }\n
59 );
60
61 /**
62  * Fragment shader code for metaball
63  */
64 const char* const METABALL_FRAG_SHADER = DALI_COMPOSE_SHADER (
65   precision mediump float;\n
66   varying vec2 vTexCoord;\n
67   uniform vec2 uPositionMetaball;\n
68   uniform vec2 uPositionVar;\n
69   uniform vec2 uGravityVector;\n
70   uniform float uRadius;\n
71   uniform float uRadiusVar;\n
72   void main()\n
73   {\n
74     vec2 adjustedCoords = vTexCoord * 2.0 - 1.0;\n
75     vec2 finalMetaballPosition = uPositionMetaball + uGravityVector + uPositionVar;\n
76     \n
77     float finalRadius = uRadius + uRadiusVar;\n
78     vec2 distanceVec = adjustedCoords - finalMetaballPosition;\n
79     float result = dot(distanceVec, distanceVec);\n
80     float color = inversesqrt(result) * finalRadius;\n
81     \n
82     gl_FragColor = vec4(color,color,color,1.0);\n
83   }\n
84 );
85
86 /**
87  * Fragment shader code for metaball and background composition with refraction effect
88  */
89 const char* const REFRACTION_FRAG_SHADER = DALI_COMPOSE_SHADER (
90   precision highp float;\n
91   varying vec2 vTexCoord;\n
92   uniform sampler2D sTexture;\n
93   uniform sampler2D sEffect;\n
94   uniform vec2 uPositionMetaball;\n
95   void main()\n
96   {\n
97     vec2 zoomCoords;\n
98     vec3 normal = vec3(0.0,0.0,1.0);\n
99     vec2 fakePos = vec2(0.0,0.0);\n
100     vec3 color = vec3(1.0, 1.0, 1.0);
101     float ambient = 0.2;
102     \n
103     vec4 metaColor = texture2D(sEffect, vTexCoord);\n
104     \n
105     vec2 adjustedCoords = vTexCoord.xy * vec2(2.0) - vec2(1.0);\n
106     fakePos = adjustedCoords.xy - vec2(uPositionMetaball.x, -uPositionMetaball.y);
107     float len = length(fakePos) + 0.01;\n
108     vec3 colorPos = vec3(0,0,1);
109     \n
110     if (metaColor.r > 0.85)\n
111     {\n
112       zoomCoords = ((vTexCoord - 0.5) * 0.9);\n
113       zoomCoords = zoomCoords + 0.5;\n
114       \n
115       float interpNormal = mix(0.7, 1.0, (metaColor.r - 0.85) * 4.);\n
116       normal.xyz = vec3(fakePos.x * (1.0 - interpNormal) / len, fakePos.y * (1.0 - interpNormal) / len, interpNormal);\n
117       normal.xyz = normalize(normal.xyz);\n
118       color = vec3(0.65, 1.0, 0);\n
119       colorPos = vec3(fakePos.x,fakePos.y,0);
120     }\n
121     else if (metaColor.r > 0.75)\n
122     {\n
123       float interpolation = mix(0.9, 1.15, (0.85 - metaColor.r) * 10.0);\n
124       zoomCoords = ((vTexCoord - 0.5) * interpolation);\n
125       zoomCoords = zoomCoords + 0.5;\n
126       \n
127       float interpNormal = mix(0.7, 0.0, (0.85 - metaColor.r) * 10.0);\n
128       normal.xyz = vec3(fakePos.x * (1.0 - interpNormal) / len, fakePos.y * (1.0 - interpNormal) / len, interpNormal);\n
129       normal.xyz = normalize(normal.xyz);\n
130       color = vec3(0.65, 1.0, 0);\n
131       colorPos = vec3(fakePos.x,fakePos.y,0);
132     }\n
133     else\n
134     {\n
135       zoomCoords = vTexCoord;\n
136       normal = vec3(0,0,0);\n
137       ambient = 0.5;\n
138     }\n
139     \n
140     vec3 lightPosition = vec3(-750.0,-1000.0,2000.0);\n
141     vec3 vertex = vec3(adjustedCoords.x,adjustedCoords.y,0.0);\n
142     \n
143     vec3 vecToLight = normalize( lightPosition - vertex );\n
144     \n
145     float lightDiffuse = dot( vecToLight, normal );\n
146     lightDiffuse = max(0.0,lightDiffuse);\n
147     lightDiffuse = lightDiffuse * 0.5 + 0.5;
148     \n
149     vec3 vertexToEye = vec3(0,0,1) - vertex;\n
150     vertexToEye = normalize(vertexToEye);
151     vec3 lightReflect = normalize(reflect(-vecToLight, normal));\n
152     float specularFactor = max(0.0,dot(vertexToEye, lightReflect));\n
153     specularFactor = pow(specularFactor, 32.0) * 0.7;
154     \n
155     vec4 texColor = texture2D(sTexture, zoomCoords);\n
156     gl_FragColor.rgb = texColor.rgb * ambient + color.rgb * texColor.rgb * lightDiffuse + vec3(specularFactor);\n
157     gl_FragColor.a = 1.0;
158   }\n
159  );
160
161 /**
162  * Metadata for each ball
163  */
164 struct MetaballInfo
165 {
166   Actor         actor;
167   Vector2       position;
168   float         radius;
169   float         initRadius;
170
171   //new shader stuff
172   Property::Index positionIndex;
173   Property::Index positionVarIndex;
174 };
175
176 } // unnamed namespace
177
178 /**
179  * Demo using Metaballs
180  *
181  * When the metaball is clicked it explodes to smaller balls
182  */
183 class MetaballExplosionController : public ConnectionTracker
184 {
185 public:
186
187   /**
188    * Constructor
189    * @param application
190    */
191   MetaballExplosionController( Application& application );
192
193   /**
194    * Destructor
195    */
196   virtual ~MetaballExplosionController();
197
198   /**
199    * Creates the metaballs and initializes the scene
200    */
201   void Create( Application& app );
202
203   /**
204    * Touch event handler to center metaballs at touch position
205    * and start explosion animation on release
206    */
207   bool OnTouch( Actor actor, const TouchEvent& touch );
208
209   /**
210    * Key event handler to quit application on escape or back key
211    */
212   void OnKeyEvent(const KeyEvent& event);
213
214 private: // Data
215
216   Application&      mApplication;
217   Vector2           mScreenSize;
218
219   Texture           mBackgroundTexture;
220   FrameBuffer       mMetaballFBO;
221
222   Actor             mMetaballRoot;
223   MetaballInfo      mMetaballs[METABALL_NUMBER];
224
225   Property::Index   mPositionIndex;
226   Actor             mCompositionActor;
227
228   //Motion
229   Vector2           mCurrentTouchPosition;
230   Vector2           mMetaballPosVariation;
231   Vector2           mMetaballPosVariationFrom;
232   Vector2           mMetaballPosVariationTo;
233   Vector2           mMetaballCenter;
234
235   //Animations
236   Animation         mPositionVarAnimation[METABALL_NUMBER];
237
238   uint32_t          mDispersion;
239   Animation         mDispersionAnimation[METABALL_NUMBER];
240
241   Timer             mTimerDispersion;
242
243   float             mTimeMultiplier;
244
245   // Private helper functions
246
247   /**
248    * Create a mesh data with the geometry for the metaball rendering
249    * @param aspectMappedTexture whether texture coords should be mapped based on aspect ratio
250    */
251   Geometry CreateGeometry( bool aspectMappedTexture = true );
252
253   /**
254    * Create a actors and renderers for the metaballs
255    */
256   void CreateMetaballActors();
257
258   /**
259    * Create the render task and FBO to render the metaballs into a texture
260    */
261   void CreateMetaballImage();
262
263   /**
264    * Create the the final composition
265    */
266   void CreateComposition();
267
268   /**
269    * Function to create animations for the small variations of position inside the metaball
270    */
271   void CreateAnimations();
272
273   /**
274    * Function to reset metaball state
275    */
276   void ResetMetaballs( bool resetAnims );
277
278   /**
279    * Function to create disperse each of the ball that compose the metaball when exploding
280    */
281   void DisperseBallAnimation( uint32_t ball );
282
283   /**
284    * Function to make metaballs come back to reset position
285    */
286   void LaunchResetMetaballPosition( Animation& source );
287
288   /**
289    * Function to set things at the end of the animation
290    */
291   void EndDisperseAnimation( Animation& source );
292
293   /**
294    * Function to init dispersion of the metaballs one by one using a timer
295    * (so not all the balls begin moving at the same time)
296    */
297   bool OnTimerDispersionTick();
298
299   /**
300    * Function to set the actual position of the metaballs when the user clicks the screen
301    */
302   void SetPositionToMetaballs( const Vector2& metaballCenter );
303 };
304
305 /**
306  * Implementation
307  */
308
309 MetaballExplosionController::MetaballExplosionController( Application& application )
310 : mApplication( application ),
311   mScreenSize(),
312   mBackgroundTexture(),
313   mMetaballFBO(),
314   mMetaballRoot(),
315   mMetaballs(),
316   mPositionIndex(),
317   mCompositionActor(),
318   mCurrentTouchPosition(),
319   mMetaballPosVariation(),
320   mMetaballPosVariationFrom(),
321   mMetaballPosVariationTo(),
322   mMetaballCenter(),
323   mPositionVarAnimation(),
324   mDispersion( 0 ),
325   mDispersionAnimation(),
326   mTimerDispersion(),
327   mTimeMultiplier( 1.0f )
328 {
329   // Connect to the Application's Init signal
330   mApplication.InitSignal().Connect( this, &MetaballExplosionController::Create );
331 }
332
333 MetaballExplosionController::~MetaballExplosionController()
334 {
335   // Nothing to do here;
336 }
337
338 void MetaballExplosionController::Create( Application& app )
339 {
340   Window window = app.GetWindow();
341
342   window.KeyEventSignal().Connect( this, &MetaballExplosionController::OnKeyEvent );
343
344   mScreenSize = window.GetSize();
345
346   mTimeMultiplier = 1.0f;
347
348   window.SetBackgroundColor(Color::BLACK);
349
350   // Load background texture
351   mBackgroundTexture = DemoHelper::LoadTexture( BACKGROUND_IMAGE );
352
353   srand( static_cast<uint32_t>( time(0) ) );
354
355   //Create internal data
356   CreateMetaballActors();
357   CreateMetaballImage();
358   CreateComposition();
359
360   CreateAnimations();
361
362   mDispersion = 0;
363   mTimerDispersion = Timer::New( 150 );
364   mTimerDispersion.TickSignal().Connect( this, &MetaballExplosionController::OnTimerDispersionTick );
365
366   // Connect the callback to the touch signal on the mesh actor
367   window.GetRootLayer().TouchSignal().Connect( this, &MetaballExplosionController::OnTouch );
368 }
369
370 Geometry MetaballExplosionController::CreateGeometry( bool aspectMappedTexture )
371 {
372   const float aspect = mScreenSize.y / mScreenSize.x;
373
374   // Create vertices and specify their color
375   const float xsize = mScreenSize.x * 0.5;
376
377   // Create the meshdata for the metaballs
378   struct VertexPosition { Vector2 position; };
379   struct VertexTexture { Vector2 texture; };
380
381   VertexPosition vertices[] =
382   {
383     { Vector2( -xsize, -xsize * aspect ) },
384     { Vector2(  xsize, -xsize * aspect ) },
385     { Vector2( -xsize,  xsize * aspect ) },
386     { Vector2(  xsize,  xsize * aspect ) }
387   };
388
389   const float textureAspect = (aspectMappedTexture) ? aspect : 1.0f;
390   VertexTexture textures[] =
391   {
392     { Vector2( 0.0f, 0.0f ) },
393     { Vector2( 1.0f, 0.0f ) },
394     { Vector2( 0.0f, 1.0f * textureAspect ) },
395     { Vector2( 1.0f, 1.0f * textureAspect ) }
396   };
397
398   uint32_t numberOfVertices = sizeof(vertices)/sizeof(VertexPosition);
399
400   // Vertices
401   Property::Map positionVertexFormat;
402   positionVertexFormat["aPosition"] = Property::VECTOR2;
403   VertexBuffer positionVertices = VertexBuffer::New( positionVertexFormat );
404   positionVertices.SetData( vertices, numberOfVertices );
405
406   // Textures
407   Property::Map textureVertexFormat;
408   textureVertexFormat["aTexture"] = Property::VECTOR2;
409   VertexBuffer textureVertices = VertexBuffer::New( textureVertexFormat );
410   textureVertices.SetData( textures, numberOfVertices );
411
412   // Indices
413   const uint16_t indices[] = { 0, 3, 1, 0, 2, 3 };
414
415   // Create the geometry object
416   Geometry texturedQuadGeometry = Geometry::New();
417   texturedQuadGeometry.AddVertexBuffer( positionVertices );
418   texturedQuadGeometry.AddVertexBuffer( textureVertices );
419
420   texturedQuadGeometry.SetIndexBuffer ( &indices[0], sizeof( indices )/ sizeof( indices[0] ) );
421
422   return texturedQuadGeometry;
423 }
424
425 void MetaballExplosionController::CreateMetaballActors()
426 {
427   // Create the shader for the metaballs, tell DALi that shader modifies geometry so we dont need to set a meaningless size
428   Shader shader = Shader::New( METABALL_VERTEX_SHADER, METABALL_FRAG_SHADER, Shader::Hint::MODIFIES_GEOMETRY );
429
430   Geometry metaballGeom = CreateGeometry();
431   // Reuse same renderer for each actor
432   Renderer renderer = Renderer::New( metaballGeom, shader );
433   renderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
434   renderer.SetProperty( Renderer::Property::BLEND_FACTOR_SRC_RGB,    BlendFactor::ONE );
435   renderer.SetProperty( Renderer::Property::BLEND_FACTOR_DEST_RGB,   BlendFactor::ONE );
436   renderer.SetProperty( Renderer::Property::BLEND_FACTOR_SRC_ALPHA,  BlendFactor::ONE );
437   renderer.SetProperty( Renderer::Property::BLEND_FACTOR_DEST_ALPHA, BlendFactor::ONE  );
438
439   //Initialization of each of the metaballs
440   for( uint32_t i = 0; i < METABALL_NUMBER; i++ )
441   {
442     mMetaballs[i].position = Vector2(0.0f, 0.0f);
443     mMetaballs[i].radius = mMetaballs[i].initRadius = Random::Range(0.05f,0.07f);
444
445     mMetaballs[i].actor = Actor::New( );
446     mMetaballs[i].actor.SetProperty( Dali::Actor::Property::NAME, "Metaball" );
447     mMetaballs[i].actor.SetProperty( Actor::Property::SCALE, 1.0f );
448     mMetaballs[i].actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
449     mMetaballs[i].actor.AddRenderer( renderer );
450
451     mMetaballs[i].positionIndex = mMetaballs[i].actor.RegisterProperty( "uPositionMetaball", mMetaballs[i].position );
452
453     mMetaballs[i].positionVarIndex = mMetaballs[i].actor.RegisterProperty( "uPositionVar", Vector2(0.f,0.f) );
454
455     mMetaballs[i].actor.RegisterProperty( "uGravityVector", Vector2(Random::Range(-0.2,0.2),Random::Range(-0.2,0.2)) );
456     mMetaballs[i].actor.RegisterProperty( "uRadius", mMetaballs[i].radius );
457     mMetaballs[i].actor.RegisterProperty( "uRadiusVar", 0.f );
458   }
459
460   // Root creation
461   mMetaballRoot = Actor::New();
462   mMetaballRoot.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
463   for( uint32_t i = 0; i < METABALL_NUMBER; i++ )
464   {
465     mMetaballRoot.Add( mMetaballs[i].actor );
466   }
467
468 }
469
470 void MetaballExplosionController::CreateMetaballImage()
471 {
472   // Create an FBO and a render task to create to render the metaballs with a fragment shader
473   Window window = mApplication.GetWindow();
474
475   mMetaballFBO = FrameBuffer::New( mScreenSize.x, mScreenSize.y );
476
477   window.Add(mMetaballRoot);
478
479   // Create the render task used to render the metaballs
480   RenderTaskList taskList = window.GetRenderTaskList();
481   RenderTask task = taskList.CreateTask();
482   task.SetRefreshRate( RenderTask::REFRESH_ALWAYS );
483   task.SetSourceActor( mMetaballRoot );
484   task.SetExclusive( true );
485   task.SetClearColor( Color::BLACK );
486   task.SetClearEnabled( true );
487   task.SetFrameBuffer( mMetaballFBO );
488 }
489
490 void MetaballExplosionController::CreateComposition()
491 {
492   //Create new shader
493   Shader shader = Shader::New( METABALL_VERTEX_SHADER, REFRACTION_FRAG_SHADER );
494
495   // Create new texture set
496   auto textureSet = TextureSet::New();
497   textureSet.SetTexture( 0u, mBackgroundTexture  );
498   textureSet.SetTexture( 1u, mMetaballFBO.GetColorTexture() );
499
500   // Create geometry
501   Geometry metaballGeom = CreateGeometry( false );
502
503   Renderer mRenderer = Renderer::New( metaballGeom, shader );
504   mRenderer.SetTextures( textureSet );
505
506   // Create actor
507   mCompositionActor = Actor::New( );
508   mCompositionActor.SetProperty( Actor::Property::PARENT_ORIGIN,ParentOrigin::CENTER);
509   mCompositionActor.SetProperty( Actor::Property::POSITION, Vector3(0.0f, 0.0f, 0.0f));
510   mCompositionActor.SetProperty( Actor::Property::SIZE, Vector2(mScreenSize.x, mScreenSize.y) );
511   mCompositionActor.AddRenderer( mRenderer );
512
513   Vector2 metaballCenter(0.0,0);
514   metaballCenter.x = metaballCenter.x * 0.5;
515   metaballCenter.y = metaballCenter.y * 0.5;
516   mPositionIndex = mCompositionActor.RegisterProperty( "uPositionMetaball", metaballCenter );
517
518   SetPositionToMetaballs( metaballCenter );
519
520   mCompositionActor.SetProperty( Actor::Property::SIZE, Vector2(mScreenSize.x, mScreenSize.y) );
521
522   Window window = mApplication.GetWindow();
523   window.Add( mCompositionActor );
524 }
525
526 void MetaballExplosionController::CreateAnimations()
527 {
528   Vector2 direction;
529
530   for( uint32_t i = 0; i < METABALL_NUMBER; i++ )
531   {
532     KeyFrames keySinCosVariation = KeyFrames::New();
533     Vector2 sinCosVariation( 0,0 );
534
535     direction.x = Random::Range( -100.f,100.f );
536     direction.y = Random::Range( -100.f,100.f );
537
538     direction.Normalize();
539     direction *= 0.1f;
540
541     for( uint32_t j = 0; j < 360; j++ )
542     {
543       sinCosVariation.x = sinf( j * Math::PI/180.f ) * direction.x;
544       sinCosVariation.y = cosf( j * Math::PI/180.f ) * direction.y;
545       float key = j/360.f;
546       keySinCosVariation.Add( key, sinCosVariation );
547     }
548
549     mPositionVarAnimation[i] = Animation::New( 3.f );
550     mPositionVarAnimation[i].AnimateBetween( Property( mMetaballs[i].actor, mMetaballs[i].positionVarIndex ), keySinCosVariation );
551     mPositionVarAnimation[i].SetLooping( true );
552     mPositionVarAnimation[i].Play();
553   }
554 }
555
556 void MetaballExplosionController::ResetMetaballs( bool resetAnims )
557 {
558   for( uint32_t i = 0; i < METABALL_NUMBER; i++ )
559   {
560     if( mDispersionAnimation[i] )
561     {
562       mDispersionAnimation[i].Clear();
563     }
564
565     mMetaballs[i].position = Vector2( 0.0f, 0.0f );
566     mMetaballs[i].actor.SetProperty( mMetaballs[i].positionIndex, mMetaballs[i].position );
567   }
568   mTimerDispersion.Stop();
569   mDispersion = 0;
570
571   mCompositionActor.SetProperty( mPositionIndex, Vector2(0,0) );
572 }
573
574 void MetaballExplosionController::DisperseBallAnimation( uint32_t ball )
575 {
576   Vector2 position;
577   position.x = Random::Range(-1.5f,1.5f);
578   position.y = Random::Range(-1.5f,1.5f);
579
580   mDispersionAnimation[ball] = Animation::New(2.0f * mTimeMultiplier);
581   mDispersionAnimation[ball].AnimateTo( Property(mMetaballs[ball].actor, mMetaballs[ball].positionIndex), position);
582   mDispersionAnimation[ball].Play();
583
584   if( ball == METABALL_NUMBER - 1 )
585   {
586     mDispersionAnimation[ball].FinishedSignal().Connect( this, &MetaballExplosionController::LaunchResetMetaballPosition );
587   }
588 }
589
590 void MetaballExplosionController::LaunchResetMetaballPosition( Animation& source )
591 {
592   for( uint32_t i = 0; i < METABALL_NUMBER; i++ )
593   {
594     mDispersionAnimation[i] = Animation::New( 1.5f + i * 0.25f * mTimeMultiplier );
595     mDispersionAnimation[i].AnimateTo(Property( mMetaballs[i].actor, mMetaballs[i].positionIndex), Vector2(0,0) );
596     mDispersionAnimation[i].Play();
597
598     if( i == METABALL_NUMBER - 1 )
599     {
600       mDispersionAnimation[i].FinishedSignal().Connect( this, &MetaballExplosionController::EndDisperseAnimation );
601     }
602   }
603 }
604
605 void MetaballExplosionController::EndDisperseAnimation( Animation& source )
606 {
607   mCompositionActor.SetProperty( mPositionIndex, Vector2(0,0) );
608 }
609
610 bool MetaballExplosionController::OnTimerDispersionTick()
611 {
612   if( mDispersion < METABALL_NUMBER )
613   {
614     DisperseBallAnimation( mDispersion );
615     mDispersion++;
616   }
617   return true;
618 }
619
620 void MetaballExplosionController::SetPositionToMetaballs( const Vector2& metaballCenter )
621 {
622   //We set the position for the metaballs based on click position
623   for( uint32_t i = 0; i < METABALL_NUMBER; i++ )
624   {
625     mMetaballs[i].position = metaballCenter;
626     mMetaballs[i].actor.SetProperty( mMetaballs[i].positionIndex, mMetaballs[i].position );
627   }
628
629   mCompositionActor.SetProperty( mPositionIndex, metaballCenter );
630 }
631
632 bool MetaballExplosionController::OnTouch( Actor actor, const TouchEvent& touch )
633 {
634   float aspectR = mScreenSize.y / mScreenSize.x;
635
636   switch( touch.GetState( 0 ) )
637   {
638     case PointState::DOWN:
639     {
640       ResetMetaballs(true);
641
642       const Vector2 screen = touch.GetScreenPosition( 0 );
643       Vector2 metaballCenter = Vector2( (screen.x / mScreenSize.x) - 0.5f, (aspectR * (mScreenSize.y - screen.y) / mScreenSize.y) - 0.5f ) * 2.0f;
644       SetPositionToMetaballs(metaballCenter);
645
646       break;
647     }
648     case PointState::MOTION:
649     {
650       const Vector2 screen = touch.GetScreenPosition( 0 );
651       Vector2 metaballCenter = Vector2( (screen.x / mScreenSize.x) - 0.5f, (aspectR * (mScreenSize.y - screen.y) / mScreenSize.y) - 0.5f ) * 2.0f;
652       SetPositionToMetaballs(metaballCenter);
653       break;
654     }
655     case PointState::UP:
656     case PointState::LEAVE:
657     case PointState::INTERRUPTED:
658     {
659       mTimerDispersion.Start();
660       break;
661     }
662     default:
663       break;
664     }
665   return true;
666 }
667
668 void MetaballExplosionController::OnKeyEvent(const KeyEvent& event)
669 {
670   if(event.GetState() == KeyEvent::DOWN)
671   {
672     if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
673     {
674       mApplication.Quit();
675     }
676   }
677 }
678
679 /**
680  * Main entry point
681  */
682 int32_t DALI_EXPORT_API main( int argc, char **argv )
683 {
684   Application application = Application::New( &argc, &argv );
685
686   MetaballExplosionController test( application );
687
688   application.MainLoop();
689
690   return 0;
691 }