Merge "Updating for NativeImageInterface" into devel/master
[platform/core/uifw/dali-demo.git] / examples / reflection-demo / reflection-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 #include <dali-toolkit/dali-toolkit.h>
19 #include <dali/devel-api/actors/camera-actor-devel.h>
20 #include <dali/devel-api/adaptor-framework/file-stream.h>
21
22 #include <map>
23
24 #include "gltf-scene.h"
25
26 using namespace Dali;
27 using Dali::Toolkit::TextLabel;
28
29 namespace
30 {
31
32 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
33   attribute mediump vec3 aPosition;\n
34   attribute mediump vec3 aNormal;\n
35   attribute mediump vec2 aTexCoord;\n
36   uniform mediump mat4 uMvpMatrix;\n
37   uniform mediump mat3 uNormalMatrix;\n
38   uniform mediump vec3 uSize;\n
39   \n
40   varying mediump vec2 vTexCoord; \n
41   varying mediump vec3 vNormal; \n
42   varying mediump vec3 vPosition; \n
43   void main()\n
44 {\n
45   mediump vec4 vertexPosition = vec4(aPosition, 1.0);\n
46   vertexPosition.xyz *= uSize;\n
47   vTexCoord = aTexCoord;\n
48   vNormal = normalize(uNormalMatrix * aNormal);\n
49   vPosition = aPosition; \n
50   gl_Position = uMvpMatrix * vertexPosition;\n
51 }\n
52 );
53
54 const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
55   uniform lowp vec4 uColor;\n
56   uniform sampler2D sTexture; \n
57   varying mediump vec3 vNormal;\n
58   varying mediump vec3 vPosition; \n
59   varying mediump vec2 vTexCoord; \n
60   \n
61   void main()\n
62 {\n
63   gl_FragColor = texture2D(sTexture, vTexCoord) * 50.0;\n
64 }\n
65 );
66
67 const char* FRAGMENT_SIMPLE_SHADER = DALI_COMPOSE_SHADER(
68         uniform lowp vec4 uColor;\n
69         uniform sampler2D sTexture; \n
70         varying mediump vec3 vNormal;\n
71         varying mediump vec3 vPosition; \n
72         varying mediump vec2 vTexCoord; \n
73         \n
74         void main()\n
75 {\n
76         gl_FragColor = texture2D(sTexture, vTexCoord) * 2.0;\n
77 }\n
78 );
79
80 const char* TEXTURED_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
81   uniform lowp vec4 uColor;\n
82   uniform sampler2D sTexture; \n
83   uniform mediump vec2 uScreenSize;\n
84
85   uniform mediump vec3 eyePos;\n
86   uniform mediump vec3 lightDir;\n
87
88   varying mediump vec3 vNormal;\n
89   varying mediump vec3 vPosition; \n
90   varying mediump vec2 vTexCoord; \n
91   \n
92   void main()\n
93 {\n
94   mediump vec3 n = normalize( vNormal );\n
95   mediump vec3 l = normalize( lightDir );\n
96   mediump vec3 e = normalize( eyePos );\n
97   mediump float intensity = max(dot(n,l), 0.0);\n
98   gl_FragColor = texture2D(sTexture, vTexCoord) * intensity;\n
99 }\n
100 );
101
102 const char* PLASMA_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
103   precision mediump float;\n
104   uniform sampler2D sTexture; \n
105
106   uniform float uTime;
107   uniform float uKFactor;
108   uniform mediump vec3 eyePos;\n
109   uniform mediump vec3 lightDir;\n
110   varying mediump vec3 vNormal;\n
111   varying mediump vec3 vPosition; \n
112   varying mediump vec2 vTexCoord; \n
113   \n
114   void main()\n
115 {\n
116   mediump vec3 n = normalize( vNormal );\n
117   mediump vec3 l = normalize( lightDir );\n
118   mediump vec3 e = normalize( eyePos );\n
119   mediump float intensity = max(dot(n,l), 0.0);\n
120 \n
121   const mediump float PI = 3.1415926535897932384626433832795;\n
122   mediump float v = 0.0;\n
123   mediump vec2 c = vTexCoord * uKFactor - uKFactor/2.0;\n
124   v += sin((c.x+uTime));\n
125   v += sin((c.y+uTime)/2.0);\n
126   v += sin((c.x+c.y+uTime)/2.0);\n
127   c += uKFactor/2.0 * vec2(sin(uTime/3.0), cos(uTime/2.0));\n
128   v += sin(sqrt(c.x*c.x+c.y*c.y+1.0)+uTime);\n
129   v = v/2.0;\n
130   mediump vec3 col = vec3(1, sin(PI*v), cos(PI*v));\n
131   gl_FragColor = (texture2D(sTexture, vTexCoord)) * (((col.r+col.g+col.b)/3.0)+1.0+intensity);\n
132 }\n
133 );
134
135 const char* TEX_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
136   uniform lowp vec4 uColor;\n
137   uniform sampler2D sTexture0; \n
138   uniform sampler2D sTexture1; \n
139   uniform mediump vec3 eyePos;\n
140   uniform mediump vec3 lightDir;\n
141   uniform mediump vec2 uScreenSize;\n
142   varying mediump vec3 vNormal;\n
143   varying mediump vec3 vPosition; \n
144   varying mediump vec2 vTexCoord; \n
145   \n
146
147   mediump float rand(mediump vec2 co){\n
148     return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);\n
149   }\n
150   \n
151   void main()\n
152 {\n
153   mediump vec2 tx = (gl_FragCoord.xy / uScreenSize.xy);\n
154   mediump vec3 n = normalize( vNormal );\n
155   mediump vec3 l = normalize( lightDir );\n
156   mediump vec3 e = normalize( eyePos );\n
157   mediump float factor = gl_FragCoord.y / uScreenSize.y;\n
158   mediump float intensity = max(dot(n,l), 0.0);\n
159   mediump vec2 uv = tx;\n
160   gl_FragColor = ((texture2D(sTexture0, vTexCoord) * factor ) + \n
161                  (texture2D(sTexture1, uv))) * intensity;\n
162 }\n
163 );
164
165 struct Model
166 {
167   Shader    shader;
168   Geometry  geometry;
169 };
170
171 template<class T>
172 bool LoadFile( const std::string& filename, std::vector<T>& bytes )
173 {
174   Dali::FileStream fileStream( filename, Dali::FileStream::READ | Dali::FileStream::BINARY );
175   FILE* fin = fileStream.GetFile();
176
177   if( fin )
178   {
179     if( fseek( fin, 0, SEEK_END ) )
180     {
181       return false;
182     }
183     bytes.resize( uint32_t(ftell( fin )) );
184     std::fill( bytes.begin(), bytes.end(), 0 );
185     if( fseek( fin, 0, SEEK_SET ) )
186     {
187       return false;
188     }
189     size_t result = fread( bytes.data(), 1, bytes.size(), fin );
190     return ( result != 0 );
191   }
192
193   return false;
194 }
195
196 Shader CreateShader( const std::string& vsh, const std::string& fsh )
197 {
198   std::vector<char> vshShaderSource;
199   std::vector<char> fshShaderSource;
200
201   // VSH
202   if(vsh[0] == '/')
203   {
204     std::string vshPath( DEMO_GAME_DIR );
205     vshPath += '/';
206     vshPath += vsh;
207     LoadFile( vshPath, vshShaderSource );
208   }
209   else
210   {
211     vshShaderSource.insert(vshShaderSource.end(), vsh.begin(), vsh.end());
212   }
213
214   // FSH
215   if(fsh[0] == '/')
216   {
217     std::string fshPath( DEMO_GAME_DIR );
218     fshPath += '/';
219     fshPath += fsh;
220     LoadFile( fshPath, fshShaderSource );
221   }
222   else
223   {
224     fshShaderSource.insert(fshShaderSource.end(), fsh.begin(), fsh.end());
225   }
226
227   vshShaderSource.emplace_back(0);
228   fshShaderSource.emplace_back(0);
229   return Shader::New( std::string(vshShaderSource.data()), std::string(fshShaderSource.data()) );
230 }
231
232 std::unique_ptr<Model> CreateModel( glTF& gltf,
233                                     const glTF_Mesh* mesh,
234                                     const std::string& vertexShaderSource,
235                                     const std::string& fragmentShaderSource )
236 {
237   /*
238    * Obtain interleaved buffer for first mesh with position and normal attributes
239    */
240   auto positionBuffer = gltf.GetMeshAttributeBuffer( *mesh,
241                                                      {
242                                                        glTFAttributeType::POSITION,
243                                                        glTFAttributeType::NORMAL,
244                                                        glTFAttributeType::TEXCOORD_0
245                                                      } );
246
247   auto attributeCount = gltf.GetMeshAttributeCount( mesh );
248   /**
249    * Create matching property buffer
250    */
251   auto vertexBuffer = PropertyBuffer::New( Property::Map()
252                                              .Add("aPosition", Property::VECTOR3 )
253                                              .Add("aNormal", Property::VECTOR3)
254                                              .Add("aTexCoord", Property::VECTOR2)
255   );
256
257   // set vertex data
258   vertexBuffer.SetData( positionBuffer.data(), attributeCount );
259
260   auto geometry = Geometry::New();
261   geometry.AddVertexBuffer( vertexBuffer );
262   auto indexBuffer = gltf.GetMeshIndexBuffer( mesh );
263   geometry.SetIndexBuffer( indexBuffer.data(), indexBuffer.size() );
264   geometry.SetType( Geometry::Type::TRIANGLES );
265   std::unique_ptr<Model> retval( new Model() );
266   retval->shader = CreateShader( vertexShaderSource, fragmentShaderSource );
267   retval->geometry = geometry;
268   return retval;
269 }
270
271 void ReplaceShader( Actor& actor, const std::string& vsh, const std::string& fsh )
272 {
273   auto renderer = actor.GetRendererAt(0);
274   auto shader = CreateShader(vsh, fsh);
275   renderer.SetShader( shader );
276 }
277
278 } // unnamed namespace
279
280 // This example shows how to create and display mirrored reflection using CameraActor
281 //
282 class ReflectionExample : public ConnectionTracker
283 {
284 public:
285
286   ReflectionExample( Application& application )
287   : mApplication( application )
288   {
289     // Connect to the Application's Init signal
290     mApplication.InitSignal().Connect( this, &ReflectionExample::Create );
291   }
292
293   ~ReflectionExample() = default;
294
295 private:
296
297   // The Init signal is received once (only) during the Application lifetime
298   void Create( Application& application )
299   {
300     // Get a handle to the window
301     Window window = application.GetWindow();
302     uint32_t windowWidth  = uint32_t(window.GetSize().GetWidth());
303     uint32_t windowHeight = uint32_t(window.GetSize().GetHeight());
304
305     window.GetRenderTaskList().GetTask(0).SetClearEnabled(false);
306     mLayer3D = Layer::New();
307     mLayer3D.SetProperty( Actor::Property::SIZE, Vector2( windowWidth, windowHeight ) );
308     window.Add(mLayer3D);
309
310     mLayer3D.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
311     mLayer3D.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
312     mLayer3D.SetProperty( Layer::Property::BEHAVIOR, Layer::LAYER_3D );
313     mLayer3D.SetProperty( Layer::Property::DEPTH_TEST, true );
314
315
316     auto gltf = glTF(DEMO_GAME_DIR "/reflection");
317
318     // Define direction of light
319     mLightDir = Vector3( 0.5, 0.5, -1 );
320
321     /**
322      * Instantiate texture sets
323      */
324     CreateTextureSetsFromGLTF( &gltf, DEMO_GAME_DIR );
325
326     /**
327      * Create models
328      */
329     CreateModelsFromGLTF( &gltf );
330
331     /**
332      * Create scene nodes
333      */
334     CreateSceneFromGLTF( window, &gltf );
335
336     auto planeActor = mLayer3D.FindChildByName( "Plane" );
337     auto solarActor = mLayer3D.FindChildByName( "solar_root" );
338     auto backgroundActor = mLayer3D.FindChildByName( "background" );
339     ReplaceShader( backgroundActor, VERTEX_SHADER, FRAGMENT_SIMPLE_SHADER );
340     mCenterActor = mLayer3D.FindChildByName( "center" );
341     mCenterHorizActor = mLayer3D.FindChildByName( "center2" );
342
343     // Prepare Sun
344     auto sun = mLayer3D.FindChildByName( "sun" );
345     ReplaceShader( sun, VERTEX_SHADER, PLASMA_FRAGMENT_SHADER );
346     mSunTimeUniformIndex = sun.RegisterProperty( "uTime", 0.0f );
347     mSunKFactorUniformIndex = sun.RegisterProperty( "uKFactor", 0.0f );
348
349     mTickTimer = Timer::New( 16 );
350     mTickTimer.TickSignal().Connect( this, &ReflectionExample::TickTimerSignal);
351
352     // Milkyway
353     auto milkyway = mLayer3D.FindChildByName( "milkyway" );
354     ReplaceShader( milkyway, VERTEX_SHADER, FRAGMENT_SHADER );
355
356     auto renderTaskSourceActor = mLayer3D.FindChildByName( "RenderTaskSource" );
357
358     /**
359      * Access camera (it's a child of "Camera" node)
360      */
361     auto camera = mLayer3D.FindChildByName( "Camera_Orientation" );
362     auto cameraRef = mLayer3D.FindChildByName( "CameraReflection_Orientation" );
363
364     auto cameraActor = CameraActor::DownCast( camera );
365     mCamera3D = cameraActor;
366
367     auto cameraRefActor = CameraActor::DownCast( cameraRef );
368     cameraRefActor.SetProperty( DevelCameraActor::Property::REFLECTION_PLANE, Vector4(0.0f, -1.0f, 0.0f, 0.0f));
369     mReflectionCamera3D = cameraRefActor;
370
371     auto task3D = window.GetRenderTaskList().CreateTask();
372     task3D.SetSourceActor( mLayer3D );
373     task3D.SetViewport( Rect<int>(0, 0, windowWidth, windowHeight ) );
374     task3D.SetCameraActor( cameraActor );
375     task3D.SetClearColor( Color::BLACK );
376     task3D.SetClearEnabled( true );
377     task3D.SetExclusive( false );
378     task3D.SetCameraActor( cameraActor );
379
380     /**
381      * Change shader to textured
382      */
383     Shader texShader = CreateShader( VERTEX_SHADER, TEX_FRAGMENT_SHADER );
384     planeActor.RegisterProperty( "uScreenSize", Vector2(windowWidth, windowHeight) );
385     auto renderer = planeActor.GetRendererAt(0);
386     auto textureSet = renderer.GetTextures();
387     renderer.SetShader( texShader );
388
389     Texture fbTexture = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGBA8888, windowWidth, windowHeight );
390     textureSet.SetTexture( 1u, fbTexture );
391
392     auto fb = FrameBuffer::New(windowWidth, windowHeight,
393                                FrameBuffer::Attachment::DEPTH );
394
395     fb.AttachColorTexture( fbTexture );
396
397     auto renderTask = window.GetRenderTaskList().CreateTask();
398     renderTask.SetFrameBuffer( fb );
399     renderTask.SetSourceActor( renderTaskSourceActor );
400     renderTask.SetViewport( Rect<int>(0, 0, windowWidth, windowHeight ) );
401     renderTask.SetCameraActor( cameraRefActor );
402     renderTask.SetClearColor( Color::BLACK );
403     renderTask.SetClearEnabled( true );
404     renderTask.SetExclusive( false );
405
406     mAnimation = Animation::New(30.0f );
407     mAnimation.AnimateBy(Property(solarActor, Actor::Property::ORIENTATION ),
408                          Quaternion( Degree(359), Vector3(0.0, 1.0, 0.0)));
409     mAnimation.AnimateBy(Property(milkyway, Actor::Property::ORIENTATION ),
410                          Quaternion( Degree(-359), Vector3(0.0, 1.0, 0.0)));
411     mAnimation.SetLooping(true );
412     mAnimation.Play();
413
414     Actor panScreen = Actor::New();
415     Vector2 windowSize = window.GetSize();
416     panScreen.SetProperty( Actor::Property::SIZE, windowSize );
417     panScreen.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
418     panScreen.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
419     auto camera2d = window.GetRenderTaskList().GetTask(0).GetCameraActor();
420     panScreen.SetProperty( Actor::Property::POSITION, Vector3( 0, 0, camera2d.GetNearClippingPlane() ));
421     camera2d.Add(panScreen);
422     camera2d.RotateBy( Degree(180.0f), Vector3( 0.0, 1.0, 0.0 ) );
423     mPanGestureDetector = PanGestureDetector::New();
424     mPanGestureDetector.Attach( panScreen );
425     mPanGestureDetector.DetectedSignal().Connect( this, &ReflectionExample::OnPan );
426
427     // Respond to key events
428     window.KeyEventSignal().Connect( this, &ReflectionExample::OnKeyEvent );
429
430     mTickTimer.Start();
431   }
432
433   void CreateSceneFromGLTF( Window window, glTF* gltf )
434   {
435     const auto& nodes = gltf->GetNodes();
436
437     // for each node create nodes and children
438     // resolve parents later
439     std::vector<Actor> actors;
440     actors.reserve( nodes.size() );
441     for( const auto& node : nodes )
442     {
443       auto actor = node.cameraId != 0xffffffff ? CameraActor::New( window.GetSize() ) : Actor::New();
444
445       actor.SetProperty( Actor::Property::SIZE, Vector3( 1, 1, 1 ) );
446       actor.SetProperty( Dali::Actor::Property::NAME, node.name );
447       actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
448       actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
449       actor.SetProperty( Actor::Property::POSITION, Vector3( node.translation[0], node.translation[1], node.translation[2] ));
450       actor.SetProperty( Actor::Property::SCALE, Vector3( node.scale[0], node.scale[1], node.scale[2] ) );
451       actor.SetProperty( Actor::Property::ORIENTATION, Quaternion(node.rotationQuaternion[3],
452                                        node.rotationQuaternion[0],
453                                        node.rotationQuaternion[1],
454                                        node.rotationQuaternion[2]));
455
456       actors.emplace_back( actor );
457
458       // Initially add each actor to the very first one
459       if(actors.size() > 1)
460       {
461         actors[0].Add(actor);
462       }
463
464       // If mesh, create and add renderer
465       if(node.meshId != 0xffffffff)
466       {
467         const auto& model = mModels[node.meshId].get();
468         auto renderer = Renderer::New( model->geometry, model->shader );
469
470         // if textured, add texture set
471         auto materialId = gltf->GetMeshes()[node.meshId]->material;
472         if( materialId != 0xffffffff )
473         {
474           if( gltf->GetMaterials()[materialId].pbrMetallicRoughness.enabled )
475           {
476             renderer.SetTextures( mTextureSets[materialId] );
477           }
478         }
479
480         actor.AddRenderer( renderer );
481       }
482
483       // Reset and attach main camera
484       if( node.cameraId != 0xffffffff )
485       {
486         mCameraPos = Vector3(node.translation[0], node.translation[1], node.translation[2]);
487         auto quatY = Quaternion( Degree(180.0f), Vector3( 0.0, 1.0, 0.0) );
488         auto cameraActor = CameraActor::DownCast( actor );
489         cameraActor.SetProperty( Actor::Property::ORIENTATION, Quaternion(node.rotationQuaternion[3],
490                                                node.rotationQuaternion[0],
491                                                node.rotationQuaternion[1],
492                                                node.rotationQuaternion[2] )
493                                     * quatY
494                                       );
495         cameraActor.SetProjectionMode( Camera::PERSPECTIVE_PROJECTION );
496
497         const auto camera = gltf->GetCameras()[node.cameraId];
498         cameraActor.SetNearClippingPlane( camera->znear );
499         cameraActor.SetFarClippingPlane( camera->zfar );
500         cameraActor.SetFieldOfView( camera->yfov );
501
502         cameraActor.SetProperty( CameraActor::Property::INVERT_Y_AXIS, true);
503         cameraActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
504         cameraActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
505
506         mCameras.emplace_back( cameraActor );
507       }
508     }
509
510     // Resolve hierarchy dependencies
511     auto i = 0u;
512     for( const auto& node : nodes )
513     {
514       if(!node.children.empty())
515       {
516         for(const auto& childId : node.children)
517         {
518           actors[i].Add( actors[childId+1] );
519         }
520       }
521       ++i;
522     }
523
524     mActors = std::move(actors);
525
526     // Add root actor to the window
527     mLayer3D.Add( mActors[0] );
528
529     for( auto& actor : mActors )
530     {
531       actor.RegisterProperty( "lightDir", mLightDir );
532       actor.RegisterProperty( "eyePos", mCameraPos );
533     }
534
535   }
536
537   /**
538    * Creates models from glTF
539    */
540   void CreateModelsFromGLTF( glTF* gltf )
541   {
542     const auto& meshes = gltf->GetMeshes();
543     for( const auto& mesh : meshes )
544     {
545       // change shader to use texture if material indicates that
546       if(mesh->material != 0xffffffff && gltf->GetMaterials()[mesh->material].pbrMetallicRoughness.enabled)
547       {
548         mModels.emplace_back( CreateModel( *gltf, mesh, VERTEX_SHADER, TEXTURED_FRAGMENT_SHADER ) );
549       }
550       else
551       {
552         mModels.emplace_back( CreateModel( *gltf, mesh, VERTEX_SHADER, FRAGMENT_SHADER ) );
553       }
554     }
555   }
556
557   void CreateTextureSetsFromGLTF( glTF* gltf, const std::string& basePath )
558   {
559     const auto& materials = gltf->GetMaterials();
560     const auto& textures = gltf->GetTextures();
561
562     std::map<std::string, Texture> textureCache{};
563
564     for(const auto& material : materials )
565     {
566       TextureSet textureSet;
567       if(material.pbrMetallicRoughness.enabled)
568       {
569         textureSet = TextureSet::New();
570         std::string filename( basePath );
571         filename += '/';
572         filename += textures[material.pbrMetallicRoughness.baseTextureColor.index].uri;
573         Dali::PixelData pixelData = Dali::Toolkit::SyncImageLoader::Load( filename );
574
575         auto cacheKey = textures[material.pbrMetallicRoughness.baseTextureColor.index].uri;
576         auto iter = textureCache.find(cacheKey);
577         Texture texture;
578         if(iter == textureCache.end())
579         {
580           texture = Texture::New(TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(),
581                                          pixelData.GetHeight());
582           texture.Upload(pixelData);
583           texture.GenerateMipmaps();
584           textureCache[cacheKey] = texture;
585         }
586         else
587         {
588           texture = iter->second;
589         }
590         textureSet.SetTexture( 0, texture );
591         Dali::Sampler sampler = Dali::Sampler::New();
592         sampler.SetWrapMode( Dali::WrapMode::REPEAT, Dali::WrapMode::REPEAT, Dali::WrapMode::REPEAT );
593         sampler.SetFilterMode( Dali::FilterMode::LINEAR_MIPMAP_LINEAR, Dali::FilterMode::LINEAR );
594         textureSet.SetSampler( 0, sampler );
595       }
596       mTextureSets.emplace_back( textureSet );
597     }
598   }
599
600   void OnPan( Actor actor, const PanGesture& panGesture )
601   {
602     auto displacement = panGesture.screenDisplacement;
603     mCenterActor.RotateBy( Degree( displacement.y *0.1f ), Vector3( 0.0, 0.0, 1.0) );
604     mCenterActor.RotateBy( Degree( displacement.x *0.1f ), Vector3( 0.0, 1.0, 0.0) );
605     Quaternion q;
606     mCenterActor.GetProperty( Actor::Property::ORIENTATION ).Get(q);
607     Matrix m = Matrix::IDENTITY;
608     m.SetTransformComponents( Vector3::ONE, q, Vector3::ZERO );
609     auto yAxis = m.GetYAxis() * -1.0f;
610
611     yAxis.Normalize();
612     mReflectionCamera3D.SetProperty( DevelCameraActor::Property::REFLECTION_PLANE, Vector4(yAxis.x, yAxis.y, yAxis.z, 0.0f));
613   }
614
615   void OnKeyEvent( const KeyEvent& event )
616   {
617     if( event.state == KeyEvent::Down )
618     {
619       if ( IsKey( event, Dali::DALI_KEY_ESCAPE ) || IsKey( event, Dali::DALI_KEY_BACK ) )
620       {
621         mApplication.Quit();
622       }
623     }
624   }
625
626   bool TickTimerSignal()
627   {
628     auto root = mLayer3D;
629     static float rotationAngle = 0.0f;
630
631     const auto ROTATION_ANGLE_STEP = 0.05f;
632     const auto FRAME_DELTA_TIME = 0.016f;
633     const auto PLASMA_K_FACTOR = 12.0f; // 'granularity' of plasma effect
634
635     rotationAngle += ROTATION_ANGLE_STEP;
636     mMockTime += FRAME_DELTA_TIME;
637     mKFactor = PLASMA_K_FACTOR;
638
639     auto sun = root.FindChildByName( "sun" );
640     sun.SetProperty( mSunTimeUniformIndex, mMockTime );
641     sun.SetProperty( mSunKFactorUniformIndex, mKFactor );
642     sun.SetProperty( Actor::Property::ORIENTATION, Quaternion( Radian(Degree(rotationAngle)), Vector3(0.0, 1.0, 0.0)));
643     return true;
644   }
645
646 private:
647
648   Application&  mApplication;
649
650   Layer mLayer3D{};
651
652   std::vector<Actor>                      mActors {};
653   std::vector<CameraActor>                mCameras {};
654   std::vector<std::unique_ptr<Model>>     mModels {};
655   std::vector<TextureSet>                 mTextureSets {};
656
657   Animation mAnimation {};
658   float mMockTime { 0.0f };
659   float mKFactor { 0.0f };
660   Property::Index mSunTimeUniformIndex {};
661   Property::Index mSunKFactorUniformIndex {};
662   PanGestureDetector mPanGestureDetector {};
663
664   Vector3 mCameraPos { Vector3::ZERO };
665   Vector3 mLightDir { Vector3::ZERO };
666   Timer mTickTimer {};
667
668   CameraActor mCamera3D {};
669   CameraActor mReflectionCamera3D {};
670   Actor mCenterActor {};
671   Actor mCenterHorizActor {};
672 };
673
674 int DALI_EXPORT_API main( int argc, char **argv )
675 {
676   Application application = Application::New( &argc, &argv );
677   ReflectionExample test( application );
678   application.MainLoop();
679   return 0;
680 }