Merge "Size negotiation patch 4: Remove SetRelayoutEnabled" into tizen
[platform/core/uifw/dali-demo.git] / examples / refraction-effect / refraction-effect-example.cpp
1 /*
2  * Copyright (c) 2014 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 #include "shared/view.h"
21
22 #include <fstream>
23 #include <sstream>
24
25 using namespace Dali;
26
27 namespace
28 {
29 const char * const APPLICATION_TITLE( "Refraction Effect" );
30 const char * const TOOLBAR_IMAGE( DALI_IMAGE_DIR "top-bar.png" );
31 const char * const CHANGE_TEXTURE_ICON( DALI_IMAGE_DIR "icon-change.png" );
32 const char * const CHANGE_MESH_ICON( DALI_IMAGE_DIR "icon-replace.png" );
33
34 const char* MESH_FILES[] =
35 {
36  DALI_MODEL_DIR "surface_pattern_v01.obj",
37  DALI_MODEL_DIR "surface_pattern_v02.obj"
38 };
39 const unsigned int NUM_MESH_FILES( sizeof( MESH_FILES ) / sizeof( MESH_FILES[0] ) );
40
41 const char* TEXTURE_IMAGES[]=
42 {
43   DALI_IMAGE_DIR "background-1.jpg",
44   DALI_IMAGE_DIR "background-2.jpg",
45   DALI_IMAGE_DIR "background-3.jpg",
46   DALI_IMAGE_DIR "background-4.jpg"
47 };
48 const unsigned int NUM_TEXTURE_IMAGES( sizeof( TEXTURE_IMAGES ) / sizeof( TEXTURE_IMAGES[0] ) );
49
50 #define MAKE_SHADER(A)#A
51
52 struct LightOffsetConstraint
53 {
54   LightOffsetConstraint( float radius )
55   : mRadius( radius )
56   {
57   }
58
59   void operator()( Vector2& current, const PropertyInputContainer& inputs )
60   {
61     float spinAngle = inputs[0]->GetFloat();
62     current.x = cos( spinAngle );
63     current.y = sin( spinAngle );
64
65     current *= mRadius;
66   }
67
68   float mRadius;
69 };
70
71 /**
72  * @brief Load an image, scaled-down to no more than the stage dimensions.
73  *
74  * Uses image scaling mode SCALE_TO_FILL to resize the image at
75  * load time to cover the entire stage with pixels with no borders,
76  * and filter mode BOX_THEN_LINEAR to sample the image with maximum quality.
77  */
78 ResourceImage LoadStageFillingImage( const char * const imagePath )
79 {
80   Size stageSize = Stage::GetCurrent().GetSize();
81   return ResourceImage::New( imagePath, ImageDimensions( stageSize.x, stageSize.y ), Dali::FittingMode::SCALE_TO_FILL, Dali::SamplingMode::BOX_THEN_LINEAR );
82 }
83
84 } // namespace
85
86 /************************************************************************************************
87  *** This shader is used when the MeshActor is not touched***
88  ************************************************************************************************/
89 class NoEffect : public ShaderEffect
90 {
91 public:
92   /**
93    * Create an empty handle.
94    */
95   NoEffect()
96   {
97   }
98
99   /**
100    * Virtual destructor
101    */
102   virtual ~NoEffect()
103   {
104   }
105
106   /**
107    * Create a NoEffect object.
108    * @return A handle to a newly allocated NoEffect
109    */
110   static NoEffect New()
111   {
112     std::string vertexShader = MAKE_SHADER(
113         precision mediump float;\n
114         uniform mediump vec4 uTextureRect;\n
115         void main()\n
116         {\n
117           gl_Position = uMvpMatrix * vec4( aPosition.xy, 0.0, 1.0 );\n
118           vTexCoord = aTexCoord.xy;\n
119         }\n
120     );
121     std::string fragmentShader = MAKE_SHADER(
122         precision mediump float;\n
123         void main()\n
124         {\n
125           gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor;\n
126         }\n
127     );
128     ShaderEffect shaderEffect = ShaderEffect::New( vertexShader, fragmentShader,
129                                                    GeometryType( GEOMETRY_TYPE_TEXTURED_MESH),
130                                                    ShaderEffect::GeometryHints( ShaderEffect::HINT_NONE ) );
131     NoEffect handle( shaderEffect );
132     return handle;
133   }
134
135 private:
136   /**
137    * Helper for New()
138    */
139   NoEffect( ShaderEffect handle )
140   : ShaderEffect( handle )
141   {
142   }
143 };
144
145 /************************************************************/
146 /* Custom refraction effect shader******************************/
147 /************************************************************/
148
149 class RefractionEffect : public ShaderEffect
150 {
151 public:
152
153   /**
154    * Create an empty RefractionEffect handle.
155    */
156   RefractionEffect()
157   {
158   }
159
160   /**
161    * Virtual destructor
162    */
163   virtual ~RefractionEffect()
164   {
165   }
166
167   /**
168    * Create a RefractionEffect object.
169    * @return A handle to a newly allocated RefractionEffect
170    */
171   static RefractionEffect New()
172   {
173     std::string vertexShader = MAKE_SHADER(
174       precision mediump float;\n
175       varying mediump vec2 vTextureOffset;\n
176       void main()\n
177       {\n
178         gl_Position = uMvpMatrix * vec4( aPosition.xy, 0.0, 1.0 );\n
179         vTexCoord = aTexCoord.xy;\n
180
181         vNormal = aNormal;\n
182         vVertex = vec4( aPosition, 1.0 );\n
183         float length = max(0.01, length(aNormal.xy)) * 40.0;\n
184         vTextureOffset = aNormal.xy / length;\n
185       }\n
186     );
187
188     std::string fragmentShader = MAKE_SHADER(
189       precision mediump float;\n
190       uniform mediump float uEffectStrength;\n
191       uniform mediump vec3 uLightPosition;\n
192       uniform mediump vec2 uLightXYOffset;\n
193       uniform mediump vec2 uLightSpinOffset;\n
194       uniform mediump float uLightIntensity;\n
195       varying mediump vec2 vTextureOffset;\n
196
197       vec3 rgb2hsl(vec3 rgb)\n
198       {\n
199         float epsilon = 1.0e-10;\n
200         vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n
201         vec4 P = mix(vec4(rgb.bg, K.wz), vec4(rgb.gb, K.xy), step(rgb.b, rgb.g));\n
202         vec4 Q = mix(vec4(P.xyw, rgb.r), vec4(rgb.r, P.yzx), step(P.x, rgb.r));\n
203         \n
204         // RGB -> HCV
205         float value = Q.x;\n
206         float chroma = Q.x - min(Q.w, Q.y);\n
207         float hue = abs(Q.z + (Q.w-Q.y) / (6.0*chroma+epsilon));\n
208         // HCV -> HSL
209         float lightness = value - chroma*0.5;\n
210         return vec3( hue, chroma/max( 1.0-abs(lightness*2.0-1.0), 1.0e-1 ), lightness );\n
211       }\n
212
213       vec3 hsl2rgb( vec3 hsl )
214       {
215         // pure hue->RGB
216         vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n
217         vec3 p = abs(fract(hsl.xxx + K.xyz) * 6.0 - K.www);\n
218         vec3 RGB = clamp(p - K.xxx, 0.0, 1.0);\n
219         \n
220         float chroma = ( 1.0 - abs( hsl.z*2.0-1.0 ) ) * hsl.y;\n
221         return ( RGB - 0.5 ) * chroma + hsl.z;
222       }
223
224       void main()\n
225       {\n
226         vec3 normal = normalize( vNormal);\n
227
228         vec3 lightPosition = uLightPosition + vec3(uLightXYOffset+uLightSpinOffset, 0.0);\n
229         mediump vec3 vecToLight = normalize( (lightPosition - vVertex.xyz) * 0.01 );\n
230         mediump float spotEffect = pow( max(0.05, vecToLight.z ) - 0.05, 8.0);\n
231
232         spotEffect = spotEffect * uEffectStrength;\n
233         mediump float lightDiffuse = ( ( dot( vecToLight, normal )-0.75 ) *uLightIntensity  ) * spotEffect;\n
234
235         lowp vec4 color = texture2D( sTexture, vTexCoord + vTextureOffset * spotEffect );\n
236         vec3 lightedColor =  hsl2rgb( rgb2hsl(color.rgb) + vec3(0.0,0.0,lightDiffuse) );\n
237
238         gl_FragColor = vec4( lightedColor, color.a ) * uColor;\n
239       }\n
240     );
241
242     ShaderEffect shaderEffect = ShaderEffect::New( vertexShader, fragmentShader,
243                                                    GeometryType( GEOMETRY_TYPE_TEXTURED_MESH),
244                                                    ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING ) );
245     RefractionEffect handle( shaderEffect );
246
247     Vector2 stageSize = Stage::GetCurrent().GetSize();
248     handle.SetLightPosition( Vector2(stageSize.x, 0.f) );
249     handle.SetUniform( "uLightXYOffset",  Vector2::ZERO );
250     handle.SetUniform( "uLightSpinOffset",  Vector2::ZERO );
251     handle.SetUniform( "uEffectStrength", 0.f );
252     handle.SetUniform( "uLightIntensity",  2.5f );
253
254     Dali::Property::Index index = handle.RegisterProperty( "uSpinAngle", 0.f );
255     Constraint constraint = Constraint::New<Vector2>( handle, handle.GetPropertyIndex("uLightSpinOffset"), LightOffsetConstraint(stageSize.x*0.1f) );
256     constraint.AddSource( LocalSource(index) );
257     constraint.Apply();
258
259     return handle;
260   }
261
262   void SetLightPosition( const Vector2& position )
263   {
264     Vector2 stageHalfSize = Stage::GetCurrent().GetSize() * 0.5f;
265     SetUniform( "uLightPosition", Vector3( position.x - stageHalfSize.x, position.y - stageHalfSize.y, stageHalfSize.x ) );
266   }
267
268   void SetLightXYOffset( const Vector2& offset )
269   {
270     SetUniform( "uLightXYOffset",  offset );
271   }
272
273   void SetEffectStrength( float strength )
274   {
275     SetUniform( "uEffectStrength", strength );
276   }
277
278   void SetLightIntensity( float intensity )
279   {
280     SetUniform( "uLightIntensity", intensity );
281   }
282
283 private:
284   /**
285    * Helper for New()
286    */
287   RefractionEffect( ShaderEffect handle )
288   : ShaderEffect( handle )
289   {
290   }
291 };
292
293 /*************************************************/
294 /*Demo using RefractionEffect*****************/
295 /*************************************************/
296 class RefractionEffectExample : public ConnectionTracker
297 {
298 public:
299   RefractionEffectExample( Application &application )
300   : mApplication( application ),
301     mIsDown( false ),
302     mCurrentTextureId( 1 ),
303     mCurrentMeshId( 0 )
304   {
305     // Connect to the Application's Init signal
306     application.InitSignal().Connect(this, &RefractionEffectExample::Create);
307   }
308
309   ~RefractionEffectExample()
310   {
311   }
312
313 private:
314
315   // The Init signal is received once (only) during the Application lifetime
316   void Create(Application& application)
317   {
318     DemoHelper::RequestThemeChange();
319
320     Stage stage = Stage::GetCurrent();
321     mStageHalfSize = stage.GetSize() * 0.5f;
322
323     stage.KeyEventSignal().Connect(this, &RefractionEffectExample::OnKeyEvent);
324
325     // Creates a default view with a default tool bar.
326     // The view is added to the stage.
327     Toolkit::ToolBar toolBar;
328     Toolkit::View    view;
329     mContent = DemoHelper::CreateView( application,
330         view,
331         toolBar,
332         "",
333         TOOLBAR_IMAGE,
334         APPLICATION_TITLE );
335
336     // Add a button to change background. (right of toolbar)
337     mChangeTextureButton = Toolkit::PushButton::New();
338     mChangeTextureButton.SetBackgroundImage( ResourceImage::New( CHANGE_TEXTURE_ICON ) );
339     mChangeTextureButton.ClickedSignal().Connect( this, &RefractionEffectExample::OnChangeTexture );
340     toolBar.AddControl( mChangeTextureButton,
341                         DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage,
342                         Toolkit::Alignment::HorizontalRight,
343                         DemoHelper::DEFAULT_MODE_SWITCH_PADDING  );
344     // Add a button to change mesh pattern. ( left of bar )
345     mChangeMeshButton = Toolkit::PushButton::New();
346     mChangeMeshButton.SetBackgroundImage( ResourceImage::New( CHANGE_MESH_ICON ) );
347     mChangeMeshButton.ClickedSignal().Connect( this, &RefractionEffectExample::OnChangeMesh );
348     toolBar.AddControl( mChangeMeshButton,
349                         DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage,
350                         Toolkit::Alignment::HorizontalLeft,
351                         DemoHelper::DEFAULT_MODE_SWITCH_PADDING  );
352
353     // creates the shader effects applied on the mesh actor
354     mRefractionEffect = RefractionEffect::New(); // used when the finger is touching the screen
355     mNoEffect = NoEffect::New(); // used in the other situations, basic render shader
356     // Create the mesh from the obj file and add to stage
357     mMaterial =  Material::New( "Material" ) ;
358     mMaterial.SetDiffuseTexture( LoadStageFillingImage( TEXTURE_IMAGES[mCurrentTextureId] ) );
359     CreateSurface( MESH_FILES[mCurrentMeshId] );
360
361     // Connect the callback to the touch signal on the mesh actor
362     mContent.TouchedSignal().Connect( this, &RefractionEffectExample::OnTouch );
363
364     // the animation which spin the light around the finger touch position
365     mLightPosition = Vector2( mStageHalfSize.x*2.f, 0.f);
366     mLightAnimation = Animation::New(2.f);
367     mLightAnimation.AnimateTo( Property( mRefractionEffect, "uSpinAngle" ), Math::PI*2.f );
368     mLightAnimation.SetLooping( true );
369     mLightAnimation.Pause();
370   }
371
372   /**
373    * Create a mesh actor with different geometry to replace the current one
374    */
375   bool OnChangeMesh( Toolkit::Button button  )
376   {
377     if( mMeshActor )
378     {
379       UnparentAndReset( mMeshActor );
380     }
381
382     mCurrentMeshId = ( mCurrentMeshId + 1 ) % NUM_MESH_FILES;
383     CreateSurface( MESH_FILES[mCurrentMeshId] );
384
385     return true;
386   }
387
388   bool OnChangeTexture( Toolkit::Button button )
389   {
390     mCurrentTextureId = ( mCurrentTextureId + 1 ) % NUM_TEXTURE_IMAGES;
391     mMaterial.SetDiffuseTexture( LoadStageFillingImage( TEXTURE_IMAGES[mCurrentTextureId] ) );
392
393     return true;
394   }
395
396   bool OnTouch( Actor actor , const TouchEvent& event )
397   {
398     const TouchPoint &point = event.GetPoint(0);
399
400     switch(point.state)
401     {
402       case TouchPoint::Down:
403       {
404         mIsDown = true;
405         mDownPosition = point.screen;
406
407         mLightAnimation.Play();
408
409         if( mStrenghAnimation )
410         {
411           mStrenghAnimation.Clear();
412         }
413
414         mRefractionEffect.SetLightXYOffset( point.screen - mLightPosition );
415         mMeshActor.SetShaderEffect( mRefractionEffect );
416         mStrenghAnimation= Animation::New(0.5f);
417         mStrenghAnimation.AnimateTo( Property( mRefractionEffect, "uEffectStrength" ), 1.f );
418         mStrenghAnimation.Play();
419
420         break;
421       }
422       case TouchPoint::Motion:
423       {
424         if(mIsDown)
425         {
426           // make the light position following the finger movement
427           mRefractionEffect.SetLightXYOffset( point.screen - mLightPosition );
428         }
429         break;
430       }
431       case TouchPoint::Up:
432       case TouchPoint::Leave:
433       case TouchPoint::Interrupted:
434       {
435         if(mIsDown)
436         {
437           mLightAnimation.Pause();
438
439           if( mStrenghAnimation )
440           {
441             mStrenghAnimation.Clear();
442           }
443           mStrenghAnimation = Animation::New(0.5f);
444           mStrenghAnimation.AnimateTo( Property( mRefractionEffect, "uEffectStrength" ), 0.f );
445           mStrenghAnimation.FinishedSignal().Connect( this, &RefractionEffectExample::OnTouchFinished );
446           mStrenghAnimation.Play();
447         }
448
449         mIsDown = false;
450         break;
451       }
452       case TouchPoint::Stationary:
453       case TouchPoint::Last:
454       default:
455       {
456         break;
457       }
458     }
459     return true;
460   }
461
462   void OnTouchFinished( Animation& source )
463   {
464     mMeshActor.SetShaderEffect( mNoEffect );
465     mRefractionEffect.SetLightXYOffset( Vector2::ZERO );
466   }
467
468   void CreateSurface( const std::string& objFileName )
469   {
470     MeshData::VertexContainer    vertices;
471     MeshData::FaceIndices        faces;
472     MeshData                     meshData;
473
474     std::vector<float> boundingBox;
475     std::vector<Vector3> vertexPositions;
476     std::vector<int> faceIndices;
477     // read the vertice and faces from the .obj file, and record the bounding box
478     ReadObjFile( objFileName, boundingBox, vertexPositions, faceIndices );
479
480     std::vector<Vector2> textureCoordinates;
481     // align the mesh, scale it to fit the screen size, and calculate the texture coordinate for each vertex
482     ShapeResizeAndTexureCoordinateCalculation( boundingBox, vertexPositions, textureCoordinates );
483
484     // re-organize the mesh, the vertices are duplicated, each vertex only belongs to one triangle.
485     // Without sharing vertex between triangle, so we can manipulate the texture offset on each triangle conveniently.
486     for( std::size_t i=0; i<faceIndices.size(); i=i+3 )
487     {
488       Vector3 edge1 = vertexPositions[ faceIndices[i+2] ] - vertexPositions[ faceIndices[i] ];
489       Vector3 edge2 = vertexPositions[ faceIndices[i+1] ] - vertexPositions[ faceIndices[i] ];
490       Vector3 normal = edge1.Cross(edge2);
491       normal.Normalize();
492
493       if( normal.z > 0 )
494       {
495         faces.push_back( i );
496         faces.push_back( i+1 );
497         faces.push_back( i+2 );
498       }
499       else
500       {
501         normal *= -1.f;
502         faces.push_back( i );
503         faces.push_back( i+2 );
504         faces.push_back( i+1 );
505       }
506
507       vertices.push_back( MeshData::Vertex( vertexPositions[ faceIndices[i] ], textureCoordinates[ faceIndices[i] ], normal ) );
508       vertices.push_back( MeshData::Vertex( vertexPositions[ faceIndices[i+1] ], textureCoordinates[ faceIndices[i+1] ], normal ) );
509       vertices.push_back( MeshData::Vertex( vertexPositions[ faceIndices[i+2] ], textureCoordinates[ faceIndices[i+2] ], normal ) );
510
511     }
512
513     // Now ready to construct the mesh actor
514     meshData.SetMaterial( mMaterial );
515     meshData.SetVertices( vertices );
516     meshData.SetFaceIndices( faces );
517     meshData.SetHasTextureCoords(true);
518     meshData.SetHasNormals(true);
519     mMeshActor = MeshActor::New( Mesh::New( meshData ) );
520     mMeshActor.SetParentOrigin(ParentOrigin::CENTER);
521     mMeshActor.SetShaderEffect( mNoEffect );
522     mContent.Add( mMeshActor );
523   }
524
525   void ReadObjFile( const std::string& objFileName,
526       std::vector<float>& boundingBox,
527       std::vector<Vector3>& vertexPositions,
528       std::vector<int>& faceIndices)
529   {
530     std::ifstream ifs( objFileName.c_str(), std::ios::in );
531
532     boundingBox.resize( 6 );
533     boundingBox[0]=boundingBox[2]=boundingBox[4] = std::numeric_limits<float>::max();
534     boundingBox[1]=boundingBox[3]=boundingBox[5] = -std::numeric_limits<float>::max();
535
536     std::string line;
537     while( std::getline( ifs, line ) )
538     {
539       if( line[0] == 'v' && std::isspace(line[1]))  // vertex
540       {
541         std::istringstream iss(line.substr(2), std::istringstream::in);
542         unsigned int i = 0;
543         Vector3 vertex;
544         while( iss >> vertex[i++] && i < 3);
545         if( vertex.x < boundingBox[0] )  boundingBox[0] = vertex.x;
546         if( vertex.x > boundingBox[1] )  boundingBox[1] = vertex.x;
547         if( vertex.y < boundingBox[2] )  boundingBox[2] = vertex.y;
548         if( vertex.y > boundingBox[3] )  boundingBox[3] = vertex.y;
549         if( vertex.z < boundingBox[4] )  boundingBox[4] = vertex.z;
550         if( vertex.z > boundingBox[5] )  boundingBox[5] = vertex.z;
551         vertexPositions.push_back( vertex );
552       }
553       else if( line[0] == 'f' ) //face
554       {
555         unsigned int numOfInt = 3;
556         while( true )
557         {
558           std::size_t found  = line.find('/');
559           if( found == std::string::npos )
560           {
561             break;
562           }
563           line[found] = ' ';
564           numOfInt++;
565         }
566
567         std::istringstream iss(line.substr(2), std::istringstream::in);
568         int indices[ numOfInt ];
569         unsigned int i=0;
570         while( iss >> indices[i++] && i < numOfInt);
571         unsigned int step = (i+1) / 3;
572         faceIndices.push_back( indices[0]-1 );
573         faceIndices.push_back( indices[step]-1 );
574         faceIndices.push_back( indices[2*step]-1 );
575       }
576     }
577
578     ifs.close();
579   }
580
581   void ShapeResizeAndTexureCoordinateCalculation( const std::vector<float>& boundingBox,
582       std::vector<Vector3>& vertexPositions,
583       std::vector<Vector2>& textureCoordinates)
584   {
585     Vector3 bBoxSize( boundingBox[1] - boundingBox[0], boundingBox[3] - boundingBox[2], boundingBox[5] - boundingBox[4]);
586     Vector3 bBoxMinCorner( boundingBox[0], boundingBox[2], boundingBox[4] );
587
588     Vector2 stageSize = Stage::GetCurrent().GetSize();
589     Vector3 scale( stageSize.x / bBoxSize.x, stageSize.y / bBoxSize.y, 1.f );
590     scale.z = (scale.x + scale.y)/2.f;
591
592     for( std::vector<Vector3>::iterator iter = vertexPositions.begin(); iter != vertexPositions.end(); iter++ )
593     {
594       Vector3 newPosition(  (*iter) - bBoxMinCorner ) ;
595
596       Vector2 textureCoord( newPosition.x / bBoxSize.x, newPosition.y / bBoxSize.y );
597       textureCoordinates.push_back( textureCoord );
598
599       newPosition -= bBoxSize * 0.5f;
600       (*iter) = newPosition * scale;
601     }
602   }
603
604   /**
605    * Main key event handler
606    */
607   void OnKeyEvent(const KeyEvent& event)
608   {
609     if(event.state == KeyEvent::Down)
610     {
611       if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
612       {
613         mApplication.Quit();
614       }
615     }
616   }
617
618 private:
619
620   Application&   mApplication;
621   Layer          mContent;
622
623   bool           mIsDown;
624   Vector2        mDownPosition;
625   Vector2        mLightPosition;
626   Vector2        mStageHalfSize;
627
628   Material       mMaterial;
629   MeshActor      mMeshActor;
630
631   RefractionEffect  mRefractionEffect;
632   NoEffect          mNoEffect;
633   Animation         mLightAnimation;
634   Animation         mStrenghAnimation;
635
636   Toolkit::PushButton        mChangeTextureButton;
637   Toolkit::PushButton        mChangeMeshButton;
638   unsigned int               mCurrentTextureId;
639   unsigned int               mCurrentMeshId;
640 };
641
642 /*****************************************************************************/
643
644 static void
645 RunTest(Application& app)
646 {
647   RefractionEffectExample theApp(app);
648   app.MainLoop();
649 }
650
651 /*****************************************************************************/
652
653 int
654 main(int argc, char **argv)
655 {
656   Application app = Application::New(&argc, &argv);
657
658   RunTest(app);
659
660   return 0;
661 }