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