Updated all files to new format
[platform/core/uifw/dali-demo.git] / examples / reflection-demo / reflection-example.cpp
1 /*
2  * Copyright (c) 2021 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 "generated/reflection-frag.h"
25 #include "generated/reflection-plasma-frag.h"
26 #include "generated/reflection-simple-frag.h"
27 #include "generated/reflection-tex-frag.h"
28 #include "generated/reflection-textured-frag.h"
29 #include "generated/reflection-vert.h"
30 #include "gltf-scene.h"
31
32 using namespace Dali;
33
34 namespace
35 {
36 struct Model
37 {
38   Shader   shader;
39   Geometry geometry;
40 };
41 using ModelPtr = std::unique_ptr<Model>;
42
43 using ActorContainer      = std::vector<Actor>;
44 using CameraContainer     = std::vector<CameraActor>;
45 using ModelContainer      = std::vector<ModelPtr>;
46 using TextureSetContainer = std::vector<TextureSet>;
47
48 const Vector3 DEFAULT_LIGHT_DIRECTION(0.5, 0.5, -1);
49
50 template<class T>
51 bool LoadFile(const std::string& filename, std::vector<T>& bytes)
52 {
53   Dali::FileStream fileStream(filename, Dali::FileStream::READ | Dali::FileStream::BINARY);
54   FILE*            fin = fileStream.GetFile();
55
56   if(fin)
57   {
58     if(fseek(fin, 0, SEEK_END))
59     {
60       return false;
61     }
62     bytes.resize(uint32_t(ftell(fin)));
63     std::fill(bytes.begin(), bytes.end(), 0);
64     if(fseek(fin, 0, SEEK_SET))
65     {
66       return false;
67     }
68     size_t result = fread(bytes.data(), 1, bytes.size(), fin);
69     return (result != 0);
70   }
71
72   return false;
73 }
74
75 Shader CreateShader(const std::string& vsh, const std::string& fsh)
76 {
77   std::vector<char> vshShaderSource;
78   std::vector<char> fshShaderSource;
79
80   // VSH
81   if(vsh[0] == '/')
82   {
83     std::string vshPath(DEMO_GAME_DIR);
84     vshPath += '/';
85     vshPath += vsh;
86     LoadFile(vshPath, vshShaderSource);
87   }
88   else
89   {
90     vshShaderSource.insert(vshShaderSource.end(), vsh.begin(), vsh.end());
91   }
92
93   // FSH
94   if(fsh[0] == '/')
95   {
96     std::string fshPath(DEMO_GAME_DIR);
97     fshPath += '/';
98     fshPath += fsh;
99     LoadFile(fshPath, fshShaderSource);
100   }
101   else
102   {
103     fshShaderSource.insert(fshShaderSource.end(), fsh.begin(), fsh.end());
104   }
105
106   vshShaderSource.emplace_back(0);
107   fshShaderSource.emplace_back(0);
108   return Shader::New(std::string(vshShaderSource.data()), std::string(fshShaderSource.data()));
109 }
110
111 ModelPtr CreateModel(
112   glTF&              gltf,
113   const glTF_Mesh*   mesh,
114   const std::string& vertexShaderSource,
115   const std::string& fragmentShaderSource)
116 {
117   /*
118    * Obtain interleaved buffer for first mesh with position and normal attributes
119    */
120   auto positionBuffer = gltf.GetMeshAttributeBuffer(*mesh,
121                                                     {glTFAttributeType::POSITION,
122                                                      glTFAttributeType::NORMAL,
123                                                      glTFAttributeType::TEXCOORD_0});
124
125   auto attributeCount = gltf.GetMeshAttributeCount(mesh);
126   /**
127    * Create matching property buffer
128    */
129   auto vertexBuffer = VertexBuffer::New(Property::Map()
130                                           .Add("aPosition", Property::VECTOR3)
131                                           .Add("aNormal", Property::VECTOR3)
132                                           .Add("aTexCoord", Property::VECTOR2));
133
134   // set vertex data
135   vertexBuffer.SetData(positionBuffer.data(), attributeCount);
136
137   auto geometry = Geometry::New();
138   geometry.AddVertexBuffer(vertexBuffer);
139   auto indexBuffer = gltf.GetMeshIndexBuffer(mesh);
140   geometry.SetIndexBuffer(indexBuffer.data(), indexBuffer.size());
141   geometry.SetType(Geometry::Type::TRIANGLES);
142   ModelPtr retval(new Model());
143   retval->shader   = CreateShader(vertexShaderSource, fragmentShaderSource);
144   retval->geometry = geometry;
145   return retval;
146 }
147
148 void ReplaceShader(Actor& actor, const std::string& vsh, const std::string& fsh)
149 {
150   auto renderer = actor.GetRendererAt(0);
151   auto shader   = CreateShader(vsh, fsh);
152   renderer.SetShader(shader);
153 }
154
155 void CreateTextureSetsFromGLTF(glTF* gltf, const std::string& basePath, TextureSetContainer& textureSets)
156 {
157   const auto& materials = gltf->GetMaterials();
158   const auto& textures  = gltf->GetTextures();
159
160   std::map<std::string, Texture> textureCache{};
161
162   for(const auto& material : materials)
163   {
164     TextureSet textureSet;
165     if(material.pbrMetallicRoughness.enabled)
166     {
167       textureSet = TextureSet::New();
168       std::string filename(basePath);
169       filename += '/';
170       filename += textures[material.pbrMetallicRoughness.baseTextureColor.index].uri;
171       Dali::PixelData pixelData = Dali::Toolkit::SyncImageLoader::Load(filename);
172
173       auto    cacheKey = textures[material.pbrMetallicRoughness.baseTextureColor.index].uri;
174       auto    iter     = textureCache.find(cacheKey);
175       Texture texture;
176       if(iter == textureCache.end())
177       {
178         texture = Texture::New(TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight());
179         texture.Upload(pixelData);
180         texture.GenerateMipmaps();
181         textureCache[cacheKey] = texture;
182       }
183       else
184       {
185         texture = iter->second;
186       }
187       textureSet.SetTexture(0, texture);
188       Dali::Sampler sampler = Dali::Sampler::New();
189       sampler.SetWrapMode(Dali::WrapMode::REPEAT, Dali::WrapMode::REPEAT, Dali::WrapMode::REPEAT);
190       sampler.SetFilterMode(Dali::FilterMode::LINEAR_MIPMAP_LINEAR, Dali::FilterMode::LINEAR);
191       textureSet.SetSampler(0, sampler);
192     }
193     textureSets.emplace_back(textureSet);
194   }
195 }
196
197 /**
198  * Creates models from glTF
199  */
200 void CreateModelsFromGLTF(glTF* gltf, ModelContainer& models)
201 {
202   const auto& meshes = gltf->GetMeshes();
203   for(const auto& mesh : meshes)
204   {
205     // change shader to use texture if material indicates that
206     if(mesh->material != 0xffffffff && gltf->GetMaterials()[mesh->material].pbrMetallicRoughness.enabled)
207     {
208       models.emplace_back(CreateModel(*gltf, mesh, SHADER_REFLECTION_VERT.data(), SHADER_REFLECTION_TEXTURED_FRAG.data()));
209     }
210     else
211     {
212       models.emplace_back(CreateModel(*gltf, mesh, SHADER_REFLECTION_VERT.data(), SHADER_REFLECTION_FRAG.data()));
213     }
214   }
215 }
216
217 Actor CreateSceneFromGLTF(
218   Window               window,
219   glTF*                gltf,
220   ModelContainer&      models,
221   ActorContainer&      actors,
222   CameraContainer&     cameras,
223   TextureSetContainer& textureSets)
224 {
225   const auto& nodes = gltf->GetNodes();
226
227   Vector3 cameraPosition;
228
229   // for each node create nodes and children
230   // resolve parents later
231   actors.reserve(nodes.size());
232   for(const auto& node : nodes)
233   {
234     auto actor = node.cameraId != 0xffffffff ? CameraActor::New(window.GetSize()) : Actor::New();
235
236     actor.SetProperty(Actor::Property::SIZE, Vector3(1, 1, 1));
237     actor.SetProperty(Dali::Actor::Property::NAME, node.name);
238     actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
239     actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
240     actor.SetProperty(Actor::Property::POSITION, Vector3(node.translation[0], node.translation[1], node.translation[2]));
241     actor.SetProperty(Actor::Property::SCALE, Vector3(node.scale[0], node.scale[1], node.scale[2]));
242     actor.SetProperty(Actor::Property::ORIENTATION, Quaternion(node.rotationQuaternion[3], node.rotationQuaternion[0], node.rotationQuaternion[1], node.rotationQuaternion[2]));
243
244     actors.emplace_back(actor);
245
246     // Initially add each actor to the very first one
247     if(actors.size() > 1)
248     {
249       actors[0].Add(actor);
250     }
251
252     // If mesh, create and add renderer
253     if(node.meshId != 0xffffffff)
254     {
255       const auto& model    = models[node.meshId].get();
256       auto        renderer = Renderer::New(model->geometry, model->shader);
257
258       // if textured, add texture set
259       auto materialId = gltf->GetMeshes()[node.meshId]->material;
260       if(materialId != 0xffffffff)
261       {
262         if(gltf->GetMaterials()[materialId].pbrMetallicRoughness.enabled)
263         {
264           renderer.SetTextures(textureSets[materialId]);
265         }
266       }
267
268       actor.AddRenderer(renderer);
269     }
270
271     // Reset and attach main camera
272     if(node.cameraId != 0xffffffff)
273     {
274       cameraPosition   = Vector3(node.translation[0], node.translation[1], node.translation[2]);
275       auto quatY       = Quaternion(Degree(180.0f), Vector3(0.0, 1.0, 0.0));
276       auto cameraActor = CameraActor::DownCast(actor);
277       cameraActor.SetProperty(Actor::Property::ORIENTATION, Quaternion(node.rotationQuaternion[3], node.rotationQuaternion[0], node.rotationQuaternion[1], node.rotationQuaternion[2]) * quatY);
278       cameraActor.SetProjectionMode(Camera::PERSPECTIVE_PROJECTION);
279
280       const auto camera = gltf->GetCameras()[node.cameraId];
281       cameraActor.SetNearClippingPlane(camera->znear);
282       cameraActor.SetFarClippingPlane(camera->zfar);
283       cameraActor.SetFieldOfView(camera->yfov);
284
285       cameraActor.SetProperty(CameraActor::Property::INVERT_Y_AXIS, true);
286       cameraActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
287       cameraActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
288
289       cameras.emplace_back(cameraActor);
290     }
291   }
292
293   // Resolve hierarchy dependencies
294   auto i = 0u;
295   for(const auto& node : nodes)
296   {
297     if(!node.children.empty())
298     {
299       for(const auto& childId : node.children)
300       {
301         actors[i].Add(actors[childId + 1]);
302       }
303     }
304     ++i;
305   }
306
307   for(auto& actor : actors)
308   {
309     actor.RegisterProperty("lightDir", DEFAULT_LIGHT_DIRECTION);
310     actor.RegisterProperty("eyePos", cameraPosition);
311   }
312
313   return actors[0];
314 }
315
316 } // unnamed namespace
317
318 // This example shows how to create and display mirrored reflection using CameraActor
319 //
320 class ReflectionExample : public ConnectionTracker
321 {
322 public:
323   ReflectionExample(Application& application)
324   : mApplication(application)
325   {
326     // Connect to the Application's Init signal
327     mApplication.InitSignal().Connect(this, &ReflectionExample::Create);
328   }
329
330   ~ReflectionExample() = default;
331
332 private:
333   // The Init signal is received once (only) during the Application lifetime
334   void Create(Application& application)
335   {
336     // Get a handle to the window
337     Window   window       = application.GetWindow();
338     uint32_t windowWidth  = uint32_t(window.GetSize().GetWidth());
339     uint32_t windowHeight = uint32_t(window.GetSize().GetHeight());
340
341     window.GetRenderTaskList().GetTask(0).SetClearEnabled(false);
342     mLayer3D = Layer::New();
343     mLayer3D.SetProperty(Actor::Property::SIZE, Vector2(windowWidth, windowHeight));
344     window.Add(mLayer3D);
345
346     mLayer3D.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
347     mLayer3D.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
348     mLayer3D.SetProperty(Layer::Property::BEHAVIOR, Layer::LAYER_3D);
349     mLayer3D.SetProperty(Layer::Property::DEPTH_TEST, true);
350
351     auto gltf = glTF(DEMO_GAME_DIR "/reflection");
352
353     // Define direction of light
354
355     /**
356      * Instantiate texture sets
357      */
358     CreateTextureSetsFromGLTF(&gltf, DEMO_GAME_DIR, mTextureSets);
359
360     /**
361      * Create models
362      */
363     CreateModelsFromGLTF(&gltf, mModels);
364
365     /**
366      * Create scene nodes & add to 3D Layer
367      */
368     mLayer3D.Add(CreateSceneFromGLTF(window, &gltf, mModels, mActors, mCameras, mTextureSets));
369
370     auto planeActor      = mLayer3D.FindChildByName("Plane");
371     auto solarActor      = mLayer3D.FindChildByName("solar_root");
372     auto backgroundActor = mLayer3D.FindChildByName("background");
373     ReplaceShader(backgroundActor, SHADER_REFLECTION_VERT.data(), SHADER_REFLECTION_SIMPLE_FRAG.data());
374     mCenterActor      = mLayer3D.FindChildByName("center");
375     mCenterHorizActor = mLayer3D.FindChildByName("center2");
376
377     // Prepare Sun
378     auto sun = mLayer3D.FindChildByName("sun");
379     ReplaceShader(sun, SHADER_REFLECTION_VERT.data(), SHADER_REFLECTION_PLASMA_FRAG.data());
380     mSunTimeUniformIndex    = sun.RegisterProperty("uTime", 0.0f);
381     mSunKFactorUniformIndex = sun.RegisterProperty("uKFactor", 0.0f);
382
383     mTickTimer = Timer::New(16);
384     mTickTimer.TickSignal().Connect(this, &ReflectionExample::TickTimerSignal);
385
386     // Milkyway
387     auto milkyway = mLayer3D.FindChildByName("milkyway");
388     ReplaceShader(milkyway, SHADER_REFLECTION_VERT.data(), SHADER_REFLECTION_FRAG.data());
389
390     auto renderTaskSourceActor = mLayer3D.FindChildByName("RenderTaskSource");
391
392     /**
393      * Access camera (it's a child of "Camera" node)
394      */
395     auto camera    = mLayer3D.FindChildByName("Camera_Orientation");
396     auto cameraRef = mLayer3D.FindChildByName("CameraReflection_Orientation");
397
398     auto cameraActor = CameraActor::DownCast(camera);
399     mCamera3D        = cameraActor;
400
401     auto cameraRefActor = CameraActor::DownCast(cameraRef);
402     cameraRefActor.SetProperty(DevelCameraActor::Property::REFLECTION_PLANE, Vector4(0.0f, -1.0f, 0.0f, 0.0f));
403     mReflectionCamera3D = cameraRefActor;
404
405     auto task3D = window.GetRenderTaskList().CreateTask();
406     task3D.SetSourceActor(mLayer3D);
407     task3D.SetViewport(Rect<int>(0, 0, windowWidth, windowHeight));
408     task3D.SetCameraActor(cameraActor);
409     task3D.SetClearColor(Color::BLACK);
410     task3D.SetClearEnabled(true);
411     task3D.SetExclusive(false);
412     task3D.SetCameraActor(cameraActor);
413
414     /**
415      * Change shader to textured
416      */
417     Shader texShader = CreateShader(SHADER_REFLECTION_VERT.data(), SHADER_REFLECTION_TEX_FRAG.data());
418     planeActor.RegisterProperty("uScreenSize", Vector2(windowWidth, windowHeight));
419     auto renderer   = planeActor.GetRendererAt(0);
420     auto textureSet = renderer.GetTextures();
421     renderer.SetShader(texShader);
422
423     Texture fbTexture = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGBA8888, windowWidth, windowHeight);
424     textureSet.SetTexture(1u, fbTexture);
425
426     auto fb = FrameBuffer::New(windowWidth, windowHeight, FrameBuffer::Attachment::DEPTH);
427
428     fb.AttachColorTexture(fbTexture);
429
430     auto renderTask = window.GetRenderTaskList().CreateTask();
431     renderTask.SetFrameBuffer(fb);
432     renderTask.SetSourceActor(renderTaskSourceActor);
433     renderTask.SetViewport(Rect<int>(0, 0, windowWidth, windowHeight));
434     renderTask.SetCameraActor(cameraRefActor);
435     renderTask.SetClearColor(Color::BLACK);
436     renderTask.SetClearEnabled(true);
437     renderTask.SetExclusive(false);
438
439     mAnimation = Animation::New(30.0f);
440     mAnimation.AnimateBy(Property(solarActor, Actor::Property::ORIENTATION),
441                          Quaternion(Degree(359), Vector3(0.0, 1.0, 0.0)));
442     mAnimation.AnimateBy(Property(milkyway, Actor::Property::ORIENTATION),
443                          Quaternion(Degree(-359), Vector3(0.0, 1.0, 0.0)));
444     mAnimation.SetLooping(true);
445     mAnimation.Play();
446
447     Actor   panScreen  = Actor::New();
448     Vector2 windowSize = window.GetSize();
449     panScreen.SetProperty(Actor::Property::SIZE, windowSize);
450     panScreen.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
451     panScreen.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
452     auto camera2d = window.GetRenderTaskList().GetTask(0).GetCameraActor();
453     panScreen.SetProperty(Actor::Property::POSITION, Vector3(0, 0, camera2d.GetNearClippingPlane()));
454     camera2d.Add(panScreen);
455     camera2d.RotateBy(Degree(180.0f), Vector3(0.0, 1.0, 0.0));
456     mPanGestureDetector = PanGestureDetector::New();
457     mPanGestureDetector.Attach(panScreen);
458     mPanGestureDetector.DetectedSignal().Connect(this, &ReflectionExample::OnPan);
459
460     // Respond to key events
461     window.KeyEventSignal().Connect(this, &ReflectionExample::OnKeyEvent);
462
463     mTickTimer.Start();
464   }
465
466   void OnPan(Actor actor, const PanGesture& panGesture)
467   {
468     Vector2 displacement = panGesture.GetScreenDisplacement();
469     Vector2 rotation{displacement.y * -0.1f, displacement.x * 0.1f};
470
471     Quaternion q(Degree(0.f), Degree(rotation.y), Degree(rotation.x));
472     Quaternion q0 = mCenterActor.GetProperty(Actor::Property::ORIENTATION).Get<Quaternion>();
473     mCenterActor.SetProperty(Actor::Property::ORIENTATION, q * q0);
474
475     Matrix m = Matrix::IDENTITY;
476     m.SetTransformComponents(Vector3::ONE, q0, Vector3::ZERO);
477     auto yAxis = m.GetYAxis() * -1.0f;
478
479     yAxis.Normalize();
480     mReflectionCamera3D.SetProperty(DevelCameraActor::Property::REFLECTION_PLANE, Vector4(yAxis.x, yAxis.y, yAxis.z, 0.0f));
481   }
482
483   void OnKeyEvent(const KeyEvent& event)
484   {
485     if(event.GetState() == KeyEvent::DOWN)
486     {
487       if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
488       {
489         mApplication.Quit();
490       }
491     }
492   }
493
494   bool TickTimerSignal()
495   {
496     auto         root          = mLayer3D;
497     static float rotationAngle = 0.0f;
498
499     const auto ROTATION_ANGLE_STEP = 0.05f;
500     const auto FRAME_DELTA_TIME    = 0.016f;
501     const auto PLASMA_K_FACTOR     = 12.0f; // 'granularity' of plasma effect
502
503     rotationAngle += ROTATION_ANGLE_STEP;
504     mMockTime += FRAME_DELTA_TIME;
505     mKFactor = PLASMA_K_FACTOR;
506
507     auto sun = root.FindChildByName("sun");
508     sun.SetProperty(mSunTimeUniformIndex, mMockTime);
509     sun.SetProperty(mSunKFactorUniformIndex, mKFactor);
510     sun.SetProperty(Actor::Property::ORIENTATION, Quaternion(Radian(Degree(rotationAngle)), Vector3(0.0, 1.0, 0.0)));
511     return true;
512   }
513
514 private:
515   Application& mApplication;
516
517   Layer mLayer3D{};
518
519   ActorContainer      mActors{};
520   CameraContainer     mCameras{};
521   ModelContainer      mModels{};
522   TextureSetContainer mTextureSets{};
523
524   Animation          mAnimation{};
525   float              mMockTime{0.0f};
526   float              mKFactor{0.0f};
527   Property::Index    mSunTimeUniformIndex{};
528   Property::Index    mSunKFactorUniformIndex{};
529   PanGestureDetector mPanGestureDetector{};
530
531   Timer mTickTimer{};
532
533   CameraActor mCamera3D{};
534   CameraActor mReflectionCamera3D{};
535   Actor       mCenterActor{};
536   Actor       mCenterHorizActor{};
537 };
538
539 int DALI_EXPORT_API main(int argc, char** argv)
540 {
541   Application       application = Application::New(&argc, &argv);
542   ReflectionExample test(application);
543   application.MainLoop();
544   return 0;
545 }