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