Demo of Dali toolkit and core capabilities
[platform/core/uifw/dali-demo.git] / examples / shader-effect / bubble-effect-example.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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 #include <stdio.h>
18 #include <unistd.h>
19 #include <getopt.h>
20 #include <sstream>
21 #include <dali/dali.h>
22 #include "dali-toolkit/dali-toolkit.h"
23
24 #include "../shared/view.h"
25
26 using namespace Dali;
27
28 using Dali::Material;
29
30 namespace
31 {
32 const char * const TEXTURE_IMAGE ( DALI_IMAGE_DIR "bubble-effect-texture-border.png" );
33 const char * const TOOLBAR_IMAGE( DALI_IMAGE_DIR "top-bar.png" );
34 const char * const APPLICATION_TITLE( "Bubble Effect" );
35 const char * const CHANGE_IMAGE_ICON( DALI_IMAGE_DIR "icon_mode.png" );
36
37 const char* BACKGROUND_IMAGES[]=
38 {
39   DALI_IMAGE_DIR "background-1.jpg",
40   DALI_IMAGE_DIR "background-2.jpg",
41   DALI_IMAGE_DIR "background-3.jpg",
42   DALI_IMAGE_DIR "background-4.jpg",
43 };
44 const unsigned int NUM_BACKGROUND_IMAGES( sizeof(BACKGROUND_IMAGES) / sizeof(BACKGROUND_IMAGES[0]) );
45
46 const float MIN_DISTANCE( 2.f);
47 const float MAX_DISTANCE( 200.f);
48 const float BUBBLE_SIZE( 40.f );
49 const float BUBBLE_MIN_SIZE( 5.f );
50 const unsigned long MAX_TIME_INTERVAL(100);
51 const unsigned int NUMBER_OF_BUBBLES( 100 );  //this value cannot be bigger than 100;
52 const unsigned int NUMBER_OF_GROUP( 7 );
53 const unsigned int TOTAL_NUMBER_OF_BUBBLES( NUMBER_OF_BUBBLES * NUMBER_OF_GROUP );
54 } // unnamed namespace
55
56 class BubbleEffect : public ShaderEffect
57 {
58 public:
59
60   BubbleEffect()
61   {}
62
63   static BubbleEffect New( unsigned int numberOfBubble )
64   {
65     std::ostringstream meshVertexStringStream;
66     meshVertexStringStream << "#define NUMBER_OF_BUBBLE "<< numberOfBubble << "\n";
67     std::string meshVertex(
68     "  uniform float uGravity; \n"
69     "  uniform vec4 uStartAndEndPos[NUMBER_OF_BUBBLE];\n"
70     "  uniform float uEasing[NUMBER_OF_BUBBLE];\n"
71     "  uniform float uPercentage[NUMBER_OF_BUBBLE];\n"
72     "  uniform vec2 uStageSize; \n"
73     "  uniform vec2 offset[9]; \n"
74     "  varying float vPercentage;\n"
75     "  varying vec2  vEffectTexCoord;\n"
76     "  void main()\n"
77     "  {\n"
78     "    mediump vec4 position = vec4( aPosition.x, aPosition.y, 0.0, 1.0 );\n"
79     "    int zCoord = int(aPosition.z); \n"
80     "    int idx = int(mod(aPosition.z, float(NUMBER_OF_BUBBLE)));\n"
81     "    int groupIdx = (zCoord - idx) / NUMBER_OF_BUBBLE;\n"
82     "    vec4 startAndEnd = uStartAndEndPos[idx]; \n"
83     "    startAndEnd.zw += offset[groupIdx];\n"
84     "\n"
85     "    if( uPercentage[idx] < 1.0 )\n"
86     "    {\n"
87     "      float positionDelta = uPercentage[idx];\n"
88     "      if (mod(uEasing[idx], 4.0) > 1.5) { positionDelta = pow(uPercentage[idx], 2.0); } \n"
89     "      else if (mod(uEasing[idx], 8.0) > 3.5) { positionDelta = 1.0 - pow((1.0-uPercentage[idx]),2.0); }\n"
90     "      else if (mod(uEasing[idx], 16.0) > 7.5) { positionDelta = pow(2.0, 10.0 * (uPercentage[idx] - 1.0)) - 0.001; }\n"
91     "      else if (mod(uEasing[idx], 32.0) > 15.5) { positionDelta = 1.001 * (-pow(2.0, -10.0 * uPercentage[idx]) + 1.0); }\n"
92
93     "      position.xy = position.xy + startAndEnd.xy + startAndEnd.zw * positionDelta; \n"
94     "      if (mod(uEasing[idx],64.0) > 31.5) { position.y = position.y - (0.5*uGravity * pow(uPercentage[idx]+0.1, 2.0)); }\n"
95     "    }\n"
96     "    gl_Position = uMvpMatrix * position;\n"
97     "\n"
98     "    mediump vec2 start = uCustomTextureCoords.xy;\n"
99     "    mediump vec2 scale = uCustomTextureCoords.zw;\n"
100     "    vTexCoord = vec2( start.x + aTexCoord.x * scale.x, start.y + aTexCoord.y * scale.y );\n"
101     "    vPercentage = uPercentage[idx];\n"
102     "    vEffectTexCoord = startAndEnd.xy / uStageSize; \n"
103     "  }\n" );
104     meshVertexStringStream << meshVertex;
105
106     std::string meshFragment(
107     "  varying float vPercentage;\n"
108     "  varying vec2  vEffectTexCoord;\n"
109     "\n"
110     "  void main()\n"
111     "  {\n"
112     "    vec4 fragColor = texture2D(sEffect, vEffectTexCoord);\n"
113     "    fragColor.rgb *= 1.2; \n"
114     "    fragColor *= uColor;\n"
115     "    fragColor.a  *= texture2D(sTexture, vTexCoord).a * ( 1.0-vPercentage*vPercentage );\n"
116     "    gl_FragColor = fragColor;\n"
117     "  }\n");
118
119     ShaderEffect shaderEffect = ShaderEffect::New( meshVertexStringStream.str(), meshFragment,
120                                                    GeometryType( GEOMETRY_TYPE_TEXTURED_MESH),
121                                                    ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING ) );
122     BubbleEffect handle( shaderEffect );
123     handle.mNumOfBubbles = numberOfBubble;
124     handle.SetUniform( "uGravity", 100.f );
125     handle.SetUniform( "uStageSize", Stage::GetCurrent().GetSize() );
126     for( unsigned int i=0; i<numberOfBubble; i++ )
127     {
128       handle.SetPercentage( i, 1.f);
129     }
130
131     srand(time(NULL));
132
133     return handle;
134   }
135
136   void SetBubbleParameter(unsigned int idx, const Vector2& emitPosition, Vector2 direction)
137   {
138      Vector2 randomVec(rand()%400-200, rand()%400-200);
139      float length = randomVec.Length();
140      randomVec /= length;
141      direction.Normalize();
142      Vector2 endPos = (randomVec -  direction*4.f);
143      endPos.Normalize();
144      Vector4 startAndEndPos( emitPosition.x, emitPosition.y, endPos.x*length, endPos.y*length );
145      SetStartAndEndPos( idx, startAndEndPos );
146
147      float easing = pow( 2, rand()%5-1 ) + ( rand()%2-1 )*32;
148      SetEasing( idx, easing );
149
150      SetPercentage( idx, 0.f);
151
152      float offset = 100.f;
153      SetUniform("offset[0]", Vector2(0.f,0.f));
154      SetUniform("offset[1]", Vector2(offset,0.f));
155      SetUniform("offset[2]", Vector2(-offset,0.f));
156      SetUniform("offset[3]", Vector2(0.f,offset));
157      SetUniform("offset[4]", Vector2(0.f,-offset));
158      SetUniform("offset[5]", Vector2(offset,offset)*0.707f );
159      SetUniform("offset[6]", Vector2(offset,-offset)*0.707f);
160      SetUniform("offset[7]", Vector2(-offset,offset)*0.707f);
161      SetUniform("offset[8]", Vector2(-offset,-offset)*0.707f);
162   }
163
164   std::string GetPercentagePropertyName( unsigned int idx ) const
165   {
166     assert( idx < mNumOfBubbles );
167     std::ostringstream oss;
168     oss<< "uPercentage["<< idx << "]";
169     return oss.str();
170   }
171
172 private:
173   // Helper for New()
174   BubbleEffect( ShaderEffect handle )
175   : ShaderEffect( handle )
176   {}
177
178   void SetStartAndEndPos( unsigned int idx, const Vector4& startAndEndPos )
179   {
180     assert( idx < mNumOfBubbles );
181     std::ostringstream oss;
182     oss<< "uStartAndEndPos["<< idx << "]";
183     SetUniform( oss.str(), startAndEndPos );
184   }
185
186   void SetEasing( unsigned int idx, float easing )
187   {
188     assert( idx < mNumOfBubbles );
189     std::ostringstream oss;
190     oss<< "uEasing["<< idx << "]";
191     SetUniform( oss.str(), easing );
192   }
193
194   void SetPercentage( unsigned int idx, float percentage )
195   {
196     assert( idx < mNumOfBubbles );
197     std::ostringstream oss;
198     oss<< "uPercentage["<< idx << "]";
199     SetUniform( oss.str(), percentage );
200   }
201
202 private:
203
204   unsigned int mNumOfBubbles;
205 };
206
207 class BubbleEffectExample : public ConnectionTracker
208 {
209 public:
210   BubbleEffectExample(Application &app)
211   : mApp(app),
212     mCurrentUniform( 0 ),
213     mTouchable( false ),
214     mLastTouchTime( 0 ),
215     mRescoucesCleared( false ),
216     mCurrentBackgroundImageId( 0 )
217   {
218     app.InitSignal().Connect(this, &BubbleEffectExample::Create);
219   }
220
221   ~BubbleEffectExample()
222   {
223   }
224
225 public:
226
227   void Create(Application& app)
228   {
229     Stage stage = Stage::GetCurrent();
230     Vector2 size = stage.GetSize();
231
232     // Creates a default view with a default tool bar.
233     // The view is added to the stage.
234     Toolkit::ToolBar toolBar;
235     mContent = DemoHelper::CreateView( app,
236                                        mView,
237                                        toolBar,
238                                        "",
239                                        TOOLBAR_IMAGE,
240                                        APPLICATION_TITLE );
241
242     // Create a effect toggle button. (right of toolbar)
243     Image imageLayout = Image::New( CHANGE_IMAGE_ICON );
244     Toolkit::PushButton layoutButton = Toolkit::PushButton::New();
245     layoutButton.SetBackgroundImage(imageLayout);
246     layoutButton.ClickedSignal().Connect( this, &BubbleEffectExample::OnImageChageIconClicked );
247     toolBar.AddControl( layoutButton, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HorizontalRight, DemoHelper::DEFAULT_MODE_SWITCH_PADDING  );
248
249     mRoot = Actor::New();
250     mRoot.SetParentOrigin( ParentOrigin::CENTER );
251     stage.Add(mRoot);
252
253     mFrameBufferImage = FrameBufferImage::New( size.width/4.f, size.height/4.f, Pixel::RGBA8888, Dali::Image::Unused );
254     mTimer = Timer::New( 1000 );
255     mTimer.TickSignal().Connect( this, &BubbleEffectExample::ClearBlurResource );
256
257     SetImage( BACKGROUND_IMAGES[ mCurrentBackgroundImageId ] );
258
259     GenMaterial();
260     MeshData meshData;
261     ConstructBubbleMesh( meshData, NUMBER_OF_BUBBLES*9, BUBBLE_SIZE);
262     for(unsigned int i=0; i < NUMBER_OF_GROUP; i++ )
263     {
264       mMesh[i] = Mesh::New( meshData );
265       mMeshActor[i] = MeshActor::New( mMesh[i] );
266       mMeshActor[i].SetAffectedByLighting( false );
267       mMeshActor[i].SetParentOrigin(ParentOrigin::TOP_LEFT);
268       stage.Add( mMeshActor[i] );
269       mEffect[i] = BubbleEffect::New( NUMBER_OF_BUBBLES );
270       mEffect[i].SetEffectImage( mFrameBufferImage );
271       mMeshActor[i].SetShaderEffect( mEffect[i] );
272     }
273
274     Stage::GetCurrent().KeyEventSignal().Connect(this, &BubbleEffectExample::OnKeyEvent);
275   }
276
277   void OnKeyEvent(const KeyEvent& event)
278   {
279     if(event.state == KeyEvent::Down)
280     {
281       if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
282       {
283         mApp.Quit();
284       }
285     }
286   }
287
288   void SetImage( const std::string& imagePath )
289   {
290     mTouchable = false;
291
292     if( mScreen )
293     {
294       mScreen.TouchedSignal().Disconnect( this, &BubbleEffectExample::OnTouch );
295       mRoot.Remove( mScreen );
296       ClearBlurResource();
297     }
298
299     Stage stage = Stage::GetCurrent();
300
301     Image image = Image::New( imagePath );
302     mScreen = ImageActor::New( image );
303     mScreen.SetSize( stage.GetSize() );
304     mScreen.SetZ( -1.f );
305     mScreen.SetParentOrigin(ParentOrigin::CENTER);
306     mRoot.Add(mScreen);
307     mScreen.TouchedSignal().Connect( this, &BubbleEffectExample::OnTouch );
308
309     mGaussianBlurView = Toolkit::GaussianBlurView::New( 7, 2.5f, Pixel::RGBA8888, 0.25f, 0.25f, true );
310     mGaussianBlurView.SetParentOrigin(ParentOrigin::CENTER);
311     mGaussianBlurView.SetSize(stage.GetSize());
312     mGaussianBlurView.SetUserImageAndOutputRenderTarget(  image, mFrameBufferImage );
313     stage.Add( mGaussianBlurView );
314     mGaussianBlurView.Activate();
315
316     mRescoucesCleared = false;
317     mTimer.Start();
318
319     mTouchable = true;
320   }
321
322   bool ClearBlurResource()
323   {
324     if( !mRescoucesCleared )
325     {
326       Stage::GetCurrent().Remove( mGaussianBlurView );
327       mGaussianBlurView.Deactivate();
328       mGaussianBlurView.Reset();
329       mRescoucesCleared = true;
330     }
331     return false;
332  }
333
334   void GenMaterial()
335   {
336     mCustomMaterial = Material::New("CustomMaterial");
337     mCustomMaterial.SetOpacity(1.0f);
338     mCustomMaterial.SetDiffuseColor(Color::WHITE);
339     mCustomMaterial.SetAmbientColor(Vector4(0.0, 0.1, 0.1, 1.0));
340     mCustomMaterial.SetMapU( Material::MAPPING_MODE_WRAP );
341     mCustomMaterial.SetMapV( Material::MAPPING_MODE_WRAP );
342     mCustomMaterial.SetDiffuseTexture( Image::New( TEXTURE_IMAGE ) );
343   }
344
345   void AddVertex(MeshData::VertexContainer& vertices, Vector3 V, Vector2 UV)
346   {
347     MeshData::Vertex meshVertex;
348     meshVertex.x = V.x;
349     meshVertex.y = V.y;
350     meshVertex.z = V.z;
351     meshVertex.u = UV.x;
352     meshVertex.v = UV.y;
353     vertices.push_back(meshVertex);
354   }
355
356   void AddTriangle(MeshData::FaceIndices& faces,
357                    size_t v0, size_t v1, size_t v2)
358   {
359     faces.push_back(v0);
360     faces.push_back(v1);
361     faces.push_back(v2);
362   }
363
364   void ConstructBubbleMesh( MeshData& meshData, unsigned int numOfBubble, int size )
365   {
366     MeshData::VertexContainer    vertices;
367     MeshData::FaceIndices        faces;
368     BoneContainer                bones(0);
369
370     for(unsigned int index = 0; index < numOfBubble; index ++)
371     {
372       float curSize =  static_cast<float>( rand()%size + BUBBLE_MIN_SIZE);
373       float depth = static_cast<float>( index );
374       AddVertex( vertices, Vector3(0.f,0.f,depth), Vector2(0.f,0.f) );
375       AddVertex( vertices, Vector3(0.f,curSize,depth), Vector2( 0.f,1.f ));
376       AddVertex( vertices, Vector3(curSize,curSize,depth), Vector2(1.f,1.f) );
377       AddVertex( vertices, Vector3(curSize,0.f,depth), Vector2(1.f,0.f) );
378
379       unsigned int idx = index * 4;
380       AddTriangle( faces, idx, idx+1, idx+2);
381       AddTriangle( faces, idx, idx+2, idx+3);
382     }
383
384     meshData.SetData(vertices, faces, bones, mCustomMaterial);
385     meshData.SetHasColor(false);
386     meshData.SetHasNormals(true);
387     meshData.SetHasTextureCoords(true);
388   }
389
390   void SetUpAnimation( Vector2 emitPosition, Vector2 direction )
391   {
392     unsigned int curUniform = mCurrentUniform  % NUMBER_OF_BUBBLES;
393     unsigned int groupIdx = mCurrentUniform / NUMBER_OF_BUBBLES;
394     mEffect[groupIdx].SetBubbleParameter(curUniform, emitPosition, direction);
395     float duration = RandomRange( 1.f, 2.f );
396     mAnimation[mCurrentUniform] = Animation::New( duration );
397     mAnimationIndexPair[mAnimation[mCurrentUniform]] = mCurrentUniform;
398     mAnimation[mCurrentUniform].AnimateTo( Property( mEffect[groupIdx], mEffect[groupIdx].GetPercentagePropertyName(curUniform) ),
399                                            1.f, AlphaFunctions::DoubleEaseInOutSine60 );
400     mAnimation[mCurrentUniform].Play();
401     mAnimation[mCurrentUniform].FinishedSignal().Connect(this, &BubbleEffectExample::OnAnimationFinished);
402
403     mCurrentUniform = (mCurrentUniform + 1) % (TOTAL_NUMBER_OF_BUBBLES);
404   }
405
406   void OnAnimationFinished( Animation& source )
407   {
408     mAnimation[mAnimationIndexPair[source]].Reset();
409     mAnimationIndexPair.erase( source );
410   }
411
412   float RandomRange(float f0, float f1)
413   {
414     return f0 + (rand() & 0xfff) * (f1-f0) * (1.0f/4095.0f);
415   }
416
417   bool OnTouch(Dali::Actor actor, const Dali::TouchEvent& event)
418   {
419     if(!mTouchable)
420     {
421       return false;
422     }
423
424     if ( event.GetPointCount() > 0 )
425     {
426       const TouchPoint &point = event.GetPoint(0);
427       float distance = hypotf( point.local.x - mLastTouchPosition.x, point.local.y - mLastTouchPosition.y );
428       if ( distance > MIN_DISTANCE )
429       {
430         // when the finger moves fast, interpolate linearly between two touch points
431         if(event.time - mLastTouchTime < MAX_TIME_INTERVAL  && distance < MAX_DISTANCE)
432         {
433           float num = floor(distance / MIN_DISTANCE);
434           unsigned int numStep = static_cast<unsigned int>( num );
435           Vector2 step = ( point.screen - mLastTouchPosition ) * MIN_DISTANCE / distance;
436           for(unsigned int i = 0; i<numStep; i++)
437           {
438             SetUpAnimation( mLastTouchPosition + step*i, step );
439           }
440         }
441         else
442         {
443           SetUpAnimation( point.screen, point.screen-mLastTouchPosition );
444         }
445
446         mLastTouchTime = event.time;
447         mLastTouchPosition = point.screen;
448       }
449     }
450     return true;
451   }
452
453   bool OnImageChageIconClicked( Toolkit::Button button )
454   {
455     mCurrentBackgroundImageId = (mCurrentBackgroundImageId + 1 ) % NUM_BACKGROUND_IMAGES;
456     SetImage( BACKGROUND_IMAGES[ mCurrentBackgroundImageId ] );
457     return true;
458   }
459
460 private:
461   Application&                      mApp;
462   Actor                             mRoot;
463   ImageActor                        mScreen;
464   Mesh                              mMesh[NUMBER_OF_GROUP];
465   MeshActor                         mMeshActor[NUMBER_OF_GROUP];
466   Material                          mCustomMaterial;
467   BubbleEffect                      mEffect[NUMBER_OF_GROUP];
468   unsigned int                      mCurrentUniform;
469   Animation                         mAnimation[TOTAL_NUMBER_OF_BUBBLES];
470   bool                              mTouchable;
471   std::map<Animation, unsigned int> mAnimationIndexPair;
472   unsigned long                     mLastTouchTime;
473   Vector2                           mLastTouchPosition;
474
475   Toolkit::GaussianBlurView         mGaussianBlurView;
476   FrameBufferImage                  mFrameBufferImage;
477   Timer                             mTimer;
478   bool                              mRescoucesCleared;
479
480   Layer                             mContent;
481   Toolkit::View                     mView;
482   unsigned int                      mCurrentBackgroundImageId;
483 };
484
485 /*****************************************************************************/
486
487 static void
488 RunTest(Application& app)
489 {
490   BubbleEffectExample theApp(app);
491   app.MainLoop();
492 }
493
494 /*****************************************************************************/
495
496 int
497 main(int argc, char **argv)
498 {
499   Application app = Application::New(&argc, &argv);
500
501   RunTest(app);
502
503   return 0;
504 }
505
506
507