Updated demos to use DALi clang-format
[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
28 namespace
29 {
30 // clang-format off
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 // clang-format on
165
166 struct Model
167 {
168   Shader   shader;
169   Geometry geometry;
170 };
171 using ModelPtr = std::unique_ptr<Model>;
172
173 using ActorContainer      = std::vector<Actor>;
174 using CameraContainer     = std::vector<CameraActor>;
175 using ModelContainer      = std::vector<ModelPtr>;
176 using TextureSetContainer = std::vector<TextureSet>;
177
178 const Vector3 DEFAULT_LIGHT_DIRECTION(0.5, 0.5, -1);
179
180 template<class T>
181 bool LoadFile(const std::string& filename, std::vector<T>& bytes)
182 {
183   Dali::FileStream fileStream(filename, Dali::FileStream::READ | Dali::FileStream::BINARY);
184   FILE*            fin = fileStream.GetFile();
185
186   if(fin)
187   {
188     if(fseek(fin, 0, SEEK_END))
189     {
190       return false;
191     }
192     bytes.resize(uint32_t(ftell(fin)));
193     std::fill(bytes.begin(), bytes.end(), 0);
194     if(fseek(fin, 0, SEEK_SET))
195     {
196       return false;
197     }
198     size_t result = fread(bytes.data(), 1, bytes.size(), fin);
199     return (result != 0);
200   }
201
202   return false;
203 }
204
205 Shader CreateShader(const std::string& vsh, const std::string& fsh)
206 {
207   std::vector<char> vshShaderSource;
208   std::vector<char> fshShaderSource;
209
210   // VSH
211   if(vsh[0] == '/')
212   {
213     std::string vshPath(DEMO_GAME_DIR);
214     vshPath += '/';
215     vshPath += vsh;
216     LoadFile(vshPath, vshShaderSource);
217   }
218   else
219   {
220     vshShaderSource.insert(vshShaderSource.end(), vsh.begin(), vsh.end());
221   }
222
223   // FSH
224   if(fsh[0] == '/')
225   {
226     std::string fshPath(DEMO_GAME_DIR);
227     fshPath += '/';
228     fshPath += fsh;
229     LoadFile(fshPath, fshShaderSource);
230   }
231   else
232   {
233     fshShaderSource.insert(fshShaderSource.end(), fsh.begin(), fsh.end());
234   }
235
236   vshShaderSource.emplace_back(0);
237   fshShaderSource.emplace_back(0);
238   return Shader::New(std::string(vshShaderSource.data()), std::string(fshShaderSource.data()));
239 }
240
241 ModelPtr CreateModel(
242   glTF&              gltf,
243   const glTF_Mesh*   mesh,
244   const std::string& vertexShaderSource,
245   const std::string& fragmentShaderSource)
246 {
247   /*
248    * Obtain interleaved buffer for first mesh with position and normal attributes
249    */
250   auto positionBuffer = gltf.GetMeshAttributeBuffer(*mesh,
251                                                     {glTFAttributeType::POSITION,
252                                                      glTFAttributeType::NORMAL,
253                                                      glTFAttributeType::TEXCOORD_0});
254
255   auto attributeCount = gltf.GetMeshAttributeCount(mesh);
256   /**
257    * Create matching property buffer
258    */
259   auto vertexBuffer = VertexBuffer::New(Property::Map()
260                                           .Add("aPosition", Property::VECTOR3)
261                                           .Add("aNormal", Property::VECTOR3)
262                                           .Add("aTexCoord", Property::VECTOR2));
263
264   // set vertex data
265   vertexBuffer.SetData(positionBuffer.data(), attributeCount);
266
267   auto geometry = Geometry::New();
268   geometry.AddVertexBuffer(vertexBuffer);
269   auto indexBuffer = gltf.GetMeshIndexBuffer(mesh);
270   geometry.SetIndexBuffer(indexBuffer.data(), indexBuffer.size());
271   geometry.SetType(Geometry::Type::TRIANGLES);
272   ModelPtr retval(new Model());
273   retval->shader   = CreateShader(vertexShaderSource, fragmentShaderSource);
274   retval->geometry = geometry;
275   return retval;
276 }
277
278 void ReplaceShader(Actor& actor, const std::string& vsh, const std::string& fsh)
279 {
280   auto renderer = actor.GetRendererAt(0);
281   auto shader   = CreateShader(vsh, fsh);
282   renderer.SetShader(shader);
283 }
284
285 void CreateTextureSetsFromGLTF(glTF* gltf, const std::string& basePath, TextureSetContainer& textureSets)
286 {
287   const auto& materials = gltf->GetMaterials();
288   const auto& textures  = gltf->GetTextures();
289
290   std::map<std::string, Texture> textureCache{};
291
292   for(const auto& material : materials)
293   {
294     TextureSet textureSet;
295     if(material.pbrMetallicRoughness.enabled)
296     {
297       textureSet = TextureSet::New();
298       std::string filename(basePath);
299       filename += '/';
300       filename += textures[material.pbrMetallicRoughness.baseTextureColor.index].uri;
301       Dali::PixelData pixelData = Dali::Toolkit::SyncImageLoader::Load(filename);
302
303       auto    cacheKey = textures[material.pbrMetallicRoughness.baseTextureColor.index].uri;
304       auto    iter     = textureCache.find(cacheKey);
305       Texture texture;
306       if(iter == textureCache.end())
307       {
308         texture = Texture::New(TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight());
309         texture.Upload(pixelData);
310         texture.GenerateMipmaps();
311         textureCache[cacheKey] = texture;
312       }
313       else
314       {
315         texture = iter->second;
316       }
317       textureSet.SetTexture(0, texture);
318       Dali::Sampler sampler = Dali::Sampler::New();
319       sampler.SetWrapMode(Dali::WrapMode::REPEAT, Dali::WrapMode::REPEAT, Dali::WrapMode::REPEAT);
320       sampler.SetFilterMode(Dali::FilterMode::LINEAR_MIPMAP_LINEAR, Dali::FilterMode::LINEAR);
321       textureSet.SetSampler(0, sampler);
322     }
323     textureSets.emplace_back(textureSet);
324   }
325 }
326
327 /**
328  * Creates models from glTF
329  */
330 void CreateModelsFromGLTF(glTF* gltf, ModelContainer& models)
331 {
332   const auto& meshes = gltf->GetMeshes();
333   for(const auto& mesh : meshes)
334   {
335     // change shader to use texture if material indicates that
336     if(mesh->material != 0xffffffff && gltf->GetMaterials()[mesh->material].pbrMetallicRoughness.enabled)
337     {
338       models.emplace_back(CreateModel(*gltf, mesh, VERTEX_SHADER, TEXTURED_FRAGMENT_SHADER));
339     }
340     else
341     {
342       models.emplace_back(CreateModel(*gltf, mesh, VERTEX_SHADER, FRAGMENT_SHADER));
343     }
344   }
345 }
346
347 Actor CreateSceneFromGLTF(
348   Window               window,
349   glTF*                gltf,
350   ModelContainer&      models,
351   ActorContainer&      actors,
352   CameraContainer&     cameras,
353   TextureSetContainer& textureSets)
354 {
355   const auto& nodes = gltf->GetNodes();
356
357   Vector3 cameraPosition;
358
359   // for each node create nodes and children
360   // resolve parents later
361   actors.reserve(nodes.size());
362   for(const auto& node : nodes)
363   {
364     auto actor = node.cameraId != 0xffffffff ? CameraActor::New(window.GetSize()) : Actor::New();
365
366     actor.SetProperty(Actor::Property::SIZE, Vector3(1, 1, 1));
367     actor.SetProperty(Dali::Actor::Property::NAME, node.name);
368     actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
369     actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
370     actor.SetProperty(Actor::Property::POSITION, Vector3(node.translation[0], node.translation[1], node.translation[2]));
371     actor.SetProperty(Actor::Property::SCALE, Vector3(node.scale[0], node.scale[1], node.scale[2]));
372     actor.SetProperty(Actor::Property::ORIENTATION, Quaternion(node.rotationQuaternion[3], node.rotationQuaternion[0], node.rotationQuaternion[1], node.rotationQuaternion[2]));
373
374     actors.emplace_back(actor);
375
376     // Initially add each actor to the very first one
377     if(actors.size() > 1)
378     {
379       actors[0].Add(actor);
380     }
381
382     // If mesh, create and add renderer
383     if(node.meshId != 0xffffffff)
384     {
385       const auto& model    = models[node.meshId].get();
386       auto        renderer = Renderer::New(model->geometry, model->shader);
387
388       // if textured, add texture set
389       auto materialId = gltf->GetMeshes()[node.meshId]->material;
390       if(materialId != 0xffffffff)
391       {
392         if(gltf->GetMaterials()[materialId].pbrMetallicRoughness.enabled)
393         {
394           renderer.SetTextures(textureSets[materialId]);
395         }
396       }
397
398       actor.AddRenderer(renderer);
399     }
400
401     // Reset and attach main camera
402     if(node.cameraId != 0xffffffff)
403     {
404       cameraPosition   = Vector3(node.translation[0], node.translation[1], node.translation[2]);
405       auto quatY       = Quaternion(Degree(180.0f), Vector3(0.0, 1.0, 0.0));
406       auto cameraActor = CameraActor::DownCast(actor);
407       cameraActor.SetProperty(Actor::Property::ORIENTATION, Quaternion(node.rotationQuaternion[3], node.rotationQuaternion[0], node.rotationQuaternion[1], node.rotationQuaternion[2]) * quatY);
408       cameraActor.SetProjectionMode(Camera::PERSPECTIVE_PROJECTION);
409
410       const auto camera = gltf->GetCameras()[node.cameraId];
411       cameraActor.SetNearClippingPlane(camera->znear);
412       cameraActor.SetFarClippingPlane(camera->zfar);
413       cameraActor.SetFieldOfView(camera->yfov);
414
415       cameraActor.SetProperty(CameraActor::Property::INVERT_Y_AXIS, true);
416       cameraActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
417       cameraActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
418
419       cameras.emplace_back(cameraActor);
420     }
421   }
422
423   // Resolve hierarchy dependencies
424   auto i = 0u;
425   for(const auto& node : nodes)
426   {
427     if(!node.children.empty())
428     {
429       for(const auto& childId : node.children)
430       {
431         actors[i].Add(actors[childId + 1]);
432       }
433     }
434     ++i;
435   }
436
437   for(auto& actor : actors)
438   {
439     actor.RegisterProperty("lightDir", DEFAULT_LIGHT_DIRECTION);
440     actor.RegisterProperty("eyePos", cameraPosition);
441   }
442
443   return actors[0];
444 }
445
446 } // unnamed namespace
447
448 // This example shows how to create and display mirrored reflection using CameraActor
449 //
450 class ReflectionExample : public ConnectionTracker
451 {
452 public:
453   ReflectionExample(Application& application)
454   : mApplication(application)
455   {
456     // Connect to the Application's Init signal
457     mApplication.InitSignal().Connect(this, &ReflectionExample::Create);
458   }
459
460   ~ReflectionExample() = default;
461
462 private:
463   // The Init signal is received once (only) during the Application lifetime
464   void Create(Application& application)
465   {
466     // Get a handle to the window
467     Window   window       = application.GetWindow();
468     uint32_t windowWidth  = uint32_t(window.GetSize().GetWidth());
469     uint32_t windowHeight = uint32_t(window.GetSize().GetHeight());
470
471     window.GetRenderTaskList().GetTask(0).SetClearEnabled(false);
472     mLayer3D = Layer::New();
473     mLayer3D.SetProperty(Actor::Property::SIZE, Vector2(windowWidth, windowHeight));
474     window.Add(mLayer3D);
475
476     mLayer3D.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
477     mLayer3D.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
478     mLayer3D.SetProperty(Layer::Property::BEHAVIOR, Layer::LAYER_3D);
479     mLayer3D.SetProperty(Layer::Property::DEPTH_TEST, true);
480
481     auto gltf = glTF(DEMO_GAME_DIR "/reflection");
482
483     // Define direction of light
484
485     /**
486      * Instantiate texture sets
487      */
488     CreateTextureSetsFromGLTF(&gltf, DEMO_GAME_DIR, mTextureSets);
489
490     /**
491      * Create models
492      */
493     CreateModelsFromGLTF(&gltf, mModels);
494
495     /**
496      * Create scene nodes & add to 3D Layer
497      */
498     mLayer3D.Add(CreateSceneFromGLTF(window, &gltf, mModels, mActors, mCameras, mTextureSets));
499
500     auto planeActor      = mLayer3D.FindChildByName("Plane");
501     auto solarActor      = mLayer3D.FindChildByName("solar_root");
502     auto backgroundActor = mLayer3D.FindChildByName("background");
503     ReplaceShader(backgroundActor, VERTEX_SHADER, FRAGMENT_SIMPLE_SHADER);
504     mCenterActor      = mLayer3D.FindChildByName("center");
505     mCenterHorizActor = mLayer3D.FindChildByName("center2");
506
507     // Prepare Sun
508     auto sun = mLayer3D.FindChildByName("sun");
509     ReplaceShader(sun, VERTEX_SHADER, PLASMA_FRAGMENT_SHADER);
510     mSunTimeUniformIndex    = sun.RegisterProperty("uTime", 0.0f);
511     mSunKFactorUniformIndex = sun.RegisterProperty("uKFactor", 0.0f);
512
513     mTickTimer = Timer::New(16);
514     mTickTimer.TickSignal().Connect(this, &ReflectionExample::TickTimerSignal);
515
516     // Milkyway
517     auto milkyway = mLayer3D.FindChildByName("milkyway");
518     ReplaceShader(milkyway, VERTEX_SHADER, FRAGMENT_SHADER);
519
520     auto renderTaskSourceActor = mLayer3D.FindChildByName("RenderTaskSource");
521
522     /**
523      * Access camera (it's a child of "Camera" node)
524      */
525     auto camera    = mLayer3D.FindChildByName("Camera_Orientation");
526     auto cameraRef = mLayer3D.FindChildByName("CameraReflection_Orientation");
527
528     auto cameraActor = CameraActor::DownCast(camera);
529     mCamera3D        = cameraActor;
530
531     auto cameraRefActor = CameraActor::DownCast(cameraRef);
532     cameraRefActor.SetProperty(DevelCameraActor::Property::REFLECTION_PLANE, Vector4(0.0f, -1.0f, 0.0f, 0.0f));
533     mReflectionCamera3D = cameraRefActor;
534
535     auto task3D = window.GetRenderTaskList().CreateTask();
536     task3D.SetSourceActor(mLayer3D);
537     task3D.SetViewport(Rect<int>(0, 0, windowWidth, windowHeight));
538     task3D.SetCameraActor(cameraActor);
539     task3D.SetClearColor(Color::BLACK);
540     task3D.SetClearEnabled(true);
541     task3D.SetExclusive(false);
542     task3D.SetCameraActor(cameraActor);
543
544     /**
545      * Change shader to textured
546      */
547     Shader texShader = CreateShader(VERTEX_SHADER, TEX_FRAGMENT_SHADER);
548     planeActor.RegisterProperty("uScreenSize", Vector2(windowWidth, windowHeight));
549     auto renderer   = planeActor.GetRendererAt(0);
550     auto textureSet = renderer.GetTextures();
551     renderer.SetShader(texShader);
552
553     Texture fbTexture = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGBA8888, windowWidth, windowHeight);
554     textureSet.SetTexture(1u, fbTexture);
555
556     auto fb = FrameBuffer::New(windowWidth, windowHeight, FrameBuffer::Attachment::DEPTH);
557
558     fb.AttachColorTexture(fbTexture);
559
560     auto renderTask = window.GetRenderTaskList().CreateTask();
561     renderTask.SetFrameBuffer(fb);
562     renderTask.SetSourceActor(renderTaskSourceActor);
563     renderTask.SetViewport(Rect<int>(0, 0, windowWidth, windowHeight));
564     renderTask.SetCameraActor(cameraRefActor);
565     renderTask.SetClearColor(Color::BLACK);
566     renderTask.SetClearEnabled(true);
567     renderTask.SetExclusive(false);
568
569     mAnimation = Animation::New(30.0f);
570     mAnimation.AnimateBy(Property(solarActor, Actor::Property::ORIENTATION),
571                          Quaternion(Degree(359), Vector3(0.0, 1.0, 0.0)));
572     mAnimation.AnimateBy(Property(milkyway, Actor::Property::ORIENTATION),
573                          Quaternion(Degree(-359), Vector3(0.0, 1.0, 0.0)));
574     mAnimation.SetLooping(true);
575     mAnimation.Play();
576
577     Actor   panScreen  = Actor::New();
578     Vector2 windowSize = window.GetSize();
579     panScreen.SetProperty(Actor::Property::SIZE, windowSize);
580     panScreen.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
581     panScreen.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
582     auto camera2d = window.GetRenderTaskList().GetTask(0).GetCameraActor();
583     panScreen.SetProperty(Actor::Property::POSITION, Vector3(0, 0, camera2d.GetNearClippingPlane()));
584     camera2d.Add(panScreen);
585     camera2d.RotateBy(Degree(180.0f), Vector3(0.0, 1.0, 0.0));
586     mPanGestureDetector = PanGestureDetector::New();
587     mPanGestureDetector.Attach(panScreen);
588     mPanGestureDetector.DetectedSignal().Connect(this, &ReflectionExample::OnPan);
589
590     // Respond to key events
591     window.KeyEventSignal().Connect(this, &ReflectionExample::OnKeyEvent);
592
593     mTickTimer.Start();
594   }
595
596   void OnPan(Actor actor, const PanGesture& panGesture)
597   {
598     const auto& displacement = panGesture.GetScreenDisplacement();
599     mCenterActor.RotateBy(Degree(displacement.y * 0.1f), Vector3(0.0, 0.0, 1.0));
600     mCenterActor.RotateBy(Degree(displacement.x * 0.1f), Vector3(0.0, 1.0, 0.0));
601     Quaternion q;
602     mCenterActor.GetProperty(Actor::Property::ORIENTATION).Get(q);
603     Matrix m = Matrix::IDENTITY;
604     m.SetTransformComponents(Vector3::ONE, q, Vector3::ZERO);
605     auto yAxis = m.GetYAxis() * -1.0f;
606
607     yAxis.Normalize();
608     mReflectionCamera3D.SetProperty(DevelCameraActor::Property::REFLECTION_PLANE, Vector4(yAxis.x, yAxis.y, yAxis.z, 0.0f));
609   }
610
611   void OnKeyEvent(const KeyEvent& event)
612   {
613     if(event.GetState() == KeyEvent::DOWN)
614     {
615       if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
616       {
617         mApplication.Quit();
618       }
619     }
620   }
621
622   bool TickTimerSignal()
623   {
624     auto         root          = mLayer3D;
625     static float rotationAngle = 0.0f;
626
627     const auto ROTATION_ANGLE_STEP = 0.05f;
628     const auto FRAME_DELTA_TIME    = 0.016f;
629     const auto PLASMA_K_FACTOR     = 12.0f; // 'granularity' of plasma effect
630
631     rotationAngle += ROTATION_ANGLE_STEP;
632     mMockTime += FRAME_DELTA_TIME;
633     mKFactor = PLASMA_K_FACTOR;
634
635     auto sun = root.FindChildByName("sun");
636     sun.SetProperty(mSunTimeUniformIndex, mMockTime);
637     sun.SetProperty(mSunKFactorUniformIndex, mKFactor);
638     sun.SetProperty(Actor::Property::ORIENTATION, Quaternion(Radian(Degree(rotationAngle)), Vector3(0.0, 1.0, 0.0)));
639     return true;
640   }
641
642 private:
643   Application& mApplication;
644
645   Layer mLayer3D{};
646
647   ActorContainer      mActors{};
648   CameraContainer     mCameras{};
649   ModelContainer      mModels{};
650   TextureSetContainer 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   Timer mTickTimer{};
660
661   CameraActor mCamera3D{};
662   CameraActor mReflectionCamera3D{};
663   Actor       mCenterActor{};
664   Actor       mCenterHorizActor{};
665 };
666
667 int DALI_EXPORT_API main(int argc, char** argv)
668 {
669   Application       application = Application::New(&argc, &argv);
670   ReflectionExample test(application);
671   application.MainLoop();
672   return 0;
673 }