[dali_2.2.6] Merge branch 'devel/master'
[platform/core/uifw/dali-demo.git] / examples / rendering-skybox / rendering-skybox.cpp
1 /*
2  * Copyright (c) 2022 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/dali.h>
20
21 #include "generated/rendering-skybox-cube-frag.h"
22 #include "generated/rendering-skybox-cube-vert.h"
23 #include "generated/rendering-skybox-equirectangular-frag.h"
24 #include "generated/rendering-skybox-frag.h"
25 #include "generated/rendering-skybox-vert.h"
26 #include "look-camera.h"
27
28 using namespace Dali;
29 using namespace Toolkit;
30
31 namespace
32 {
33 const float   CAMERA_DEFAULT_FOV(60.0f);
34 const float   CAMERA_DEFAULT_NEAR(0.1f);
35 const float   CAMERA_DEFAULT_FAR(1000.0f);
36 const Vector3 CAMERA_DEFAULT_POSITION(0.0f, 0.0f, 100.0f);
37
38 const char* TEXTURE_URL = DEMO_IMAGE_DIR "wood.png";
39
40 const unsigned int SKYBOX_FACE_COUNT  = 6;
41 const unsigned int SKYBOX_FACE_WIDTH  = 2048;
42 const unsigned int SKYBOX_FACE_HEIGHT = 2048;
43
44 /**
45  * @brief The skybox types supported by the application.
46  */
47 enum class SkyboxType
48 {
49   CUBEMAP,        ///< Skybox in cubemap
50   EQUIRECTANGULAR ///< Skybox in equirectangular projection
51 };
52
53 /*
54  * Credit to Joey do Vries for the following cubemap images
55  * Take from git://github.com/JoeyDeVries/LearnOpenGL.git
56  * The images are licensed under the terms of the CC BY 4.0 license:
57  * https://creativecommons.org/licenses/by/4.0/
58  */
59 const char* SKYBOX_FACES[SKYBOX_FACE_COUNT] =
60   {
61     DEMO_IMAGE_DIR "lake_right.jpg",
62     DEMO_IMAGE_DIR "lake_left.jpg",
63     DEMO_IMAGE_DIR "lake_top.jpg",
64     DEMO_IMAGE_DIR "lake_bottom.jpg",
65     DEMO_IMAGE_DIR "lake_back.jpg",
66     DEMO_IMAGE_DIR "lake_front.jpg"};
67
68 /*
69  * Take from Wikimedia Commons, the free media repository
70  * https://commons.wikimedia.org
71  * The image is licensed under the terms of the CC BY-SA 4.0 license:
72  * https://creativecommons.org/licenses/by-sa/4.0
73  */
74 const char* EQUIRECTANGULAR_TEXTURE_URL = DEMO_IMAGE_DIR "veste_oberhaus_spherical_panoramic.jpg";
75 } // namespace
76
77 // This example shows how to create a skybox
78 //
79 // Recommended screen size on desktop: 1280x720
80 //
81 class TexturedCubeController : public ConnectionTracker
82 {
83 public:
84   TexturedCubeController(Application& application)
85   : mApplication(application),
86     mCurrentSkyboxType(SkyboxType::CUBEMAP)
87   {
88     // Connect to the Application's Init signal
89     mApplication.InitSignal().Connect(this, &TexturedCubeController::Create);
90   }
91
92   ~TexturedCubeController()
93   {
94     // Nothing to do here;
95   }
96
97   // The Init signal is received once (only) during the Application lifetime
98   void Create(Application& application)
99   {
100     // Get a handle to the window
101     Window window = application.GetWindow();
102     window.SetBackgroundColor(Color::WHITE);
103
104     // Step 1. Setup camera
105     SetupCamera();
106
107     // Step 2. Create shaders
108     CreateShaders();
109
110     // Step 3. Create geometry
111     CreateCubeGeometry();
112     CreateSkyboxGeometry();
113
114     // Step 4. Display first a cube at the world origin.
115     //         The write on the depth buffer is enabled.
116     DisplayCube();
117
118     // Step 5. Display last the skybox surrounding the camera.
119     //         The depth test is enabled, the shader sets 1.0, which is the maximum depth and
120     //         the depth function is set to LESS or EQUAL so the fragment shader will run only
121     //         in those pixels that any other object has written on them.
122     DisplaySkybox(mCurrentSkyboxType);
123
124     // Step 6. Play animation to rotate the cube
125     PlayAnimation();
126
127     // Respond to key events
128     window.KeyEventSignal().Connect(this, &TexturedCubeController::OnKeyEvent);
129
130     // Create a tap gesture detector to detect double tap
131     mDoubleTapGesture = TapGestureDetector::New(2);
132     mDoubleTapGesture.Attach(window.GetRootLayer());
133     mDoubleTapGesture.DetectedSignal().Connect(this, &TexturedCubeController::OnDoubleTap);
134   }
135
136   /**
137    * @brief Called when any key event is received
138    *
139    * Will use this to quit the application if Back or the Escape key is received
140    * @param[in] event The key event information
141    */
142   void OnKeyEvent(const KeyEvent& event)
143   {
144     if(event.GetState() == KeyEvent::DOWN)
145     {
146       if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
147       {
148         mApplication.Quit();
149       }
150     }
151   }
152
153   void OnDoubleTap(Actor /*actor*/, const TapGesture& /*gesture*/)
154   {
155     if(mCurrentSkyboxType == SkyboxType::CUBEMAP)
156     {
157       mCurrentSkyboxType = SkyboxType::EQUIRECTANGULAR;
158     }
159     else
160     {
161       mCurrentSkyboxType = SkyboxType::CUBEMAP;
162     }
163
164     DisplaySkybox(mCurrentSkyboxType);
165   }
166
167   /**
168    * @brief Setup a perspective camera pointing in the negative Z direction
169    */
170   void SetupCamera()
171   {
172     Window window = mApplication.GetWindow();
173
174     RenderTask renderTask = window.GetRenderTaskList().GetTask(0);
175     renderTask.SetCullMode(false); // avoid frustum culling affecting the skybox
176
177     mCamera.Initialise(window, CAMERA_DEFAULT_POSITION, CAMERA_DEFAULT_FOV, CAMERA_DEFAULT_NEAR, CAMERA_DEFAULT_FAR);
178   }
179
180   /**
181    * Creates a shader using inlined variable VERTEX_SHADER and FRAGMENT_SHADER
182    *
183    * Shaders are very basic and all they do is transforming vertices and sampling
184    * a texture.
185    */
186   void CreateShaders()
187   {
188     mShaderCube                  = Shader::New(SHADER_RENDERING_SKYBOX_CUBE_VERT, SHADER_RENDERING_SKYBOX_CUBE_FRAG);
189     mShaderSkybox                = Shader::New(SHADER_RENDERING_SKYBOX_VERT, SHADER_RENDERING_SKYBOX_FRAG);
190     mShaderSkyboxEquirectangular = Shader::New(SHADER_RENDERING_SKYBOX_VERT, SHADER_RENDERING_SKYBOX_EQUIRECTANGULAR_FRAG);
191   }
192
193   /**
194    * @brief CreateCubeGeometry
195    * This function creates a cube geometry including texture coordinates.
196    * Also it demonstrates using the indexed draw feature by setting an index array.
197    */
198   void CreateCubeGeometry()
199   {
200     struct Vertex
201     {
202       Vector3 aPosition;
203       Vector2 aTexCoord;
204     };
205
206     Vertex vertices[] = {
207       {Vector3(1.0f, -1.0f, -1.0f), Vector2(1.0, 1.0)},
208       {Vector3(-1.0f, 1.0f, -1.0f), Vector2(0.0, 0.0)},
209       {Vector3(1.0f, 1.0f, -1.0f), Vector2(0.0, 1.0)},
210       {Vector3(-1.0f, 1.0f, 1.0f), Vector2(1.0, 1.0)},
211       {Vector3(1.0f, -1.0f, 1.0f), Vector2(0.0, 0.0)},
212       {Vector3(1.0f, 1.0f, 1.0f), Vector2(0.0, 1.0)},
213       {Vector3(1.0f, 1.0f, 1.0f), Vector2(1.0, 1.0)},
214       {Vector3(1.0f, -1.0f, -1.0f), Vector2(0.0, 0.0)},
215       {Vector3(1.0f, 1.0f, -1.0f), Vector2(0.0, 1.0)},
216       {Vector3(1.0f, -1.0f, 1.0f), Vector2(1.0, 1.0)},
217       {Vector3(-1.0f, -1.0f, -1.0f), Vector2(0.0, 0.0)},
218       {Vector3(1.0f, -1.0f, -1.0f), Vector2(0.0, 1.0)},
219       {Vector3(-1.0f, -1.0f, -1.0f), Vector2(1.0, 1.0)},
220       {Vector3(-1.0f, 1.0f, 1.0f), Vector2(0.0, 0.0)},
221       {Vector3(-1.0f, 1.0f, -1.0f), Vector2(0.0, 1.0)},
222       {Vector3(1.0f, 1.0f, -1.0f), Vector2(1.0, 1.0)},
223       {Vector3(-1.0f, 1.0f, 1.0f), Vector2(0.0, 0.0)},
224       {Vector3(1.0f, 1.0f, 1.0f), Vector2(0.0, 1.0)},
225       {Vector3(1.0f, -1.0f, -1.0f), Vector2(1.0, 1.0)},
226       {Vector3(-1.0f, -1.0f, -1.0f), Vector2(1.0, 0.0)},
227       {Vector3(-1.0f, 1.0f, -1.0f), Vector2(0.0, 0.0)},
228       {Vector3(-1.0f, 1.0f, 1.0f), Vector2(1.0, 1.0)},
229       {Vector3(-1.0f, -1.0f, 1.0f), Vector2(1.0, 0.0)},
230       {Vector3(1.0f, -1.0f, 1.0f), Vector2(0.0, 0.0)},
231       {Vector3(1.0f, 1.0f, 1.0f), Vector2(1.0, 1.0)},
232       {Vector3(1.0f, -1.0f, 1.0f), Vector2(1.0, 0.0)},
233       {Vector3(1.0f, -1.0f, -1.0f), Vector2(0.0, 0.0)},
234       {Vector3(1.0f, -1.0f, 1.0f), Vector2(1.0, 1.0)},
235       {Vector3(-1.0f, -1.0f, 1.0f), Vector2(1.0, 0.0)},
236       {Vector3(-1.0f, -1.0f, -1.0f), Vector2(0.0, 0.0)},
237       {Vector3(-1.0f, -1.0f, -1.0f), Vector2(1.0, 1.0)},
238       {Vector3(-1.0f, -1.0f, 1.0f), Vector2(1.0, 0.0)},
239       {Vector3(-1.0f, 1.0f, 1.0f), Vector2(0.0, 0.0)},
240       {Vector3(1.0f, 1.0f, -1.0f), Vector2(1.0, 1.0)},
241       {Vector3(-1.0f, 1.0f, -1.0f), Vector2(1.0, 0.0)},
242       {Vector3(-1.0f, 1.0f, 1.0f), Vector2(0.0, 0.0)},
243     };
244
245     VertexBuffer vertexBuffer = VertexBuffer::New(Property::Map()
246                                                     .Add("aPosition", Property::VECTOR3)
247                                                     .Add("aTexCoord", Property::VECTOR2));
248     vertexBuffer.SetData(vertices, sizeof(vertices) / sizeof(Vertex));
249
250     // create indices
251     const unsigned short INDEX_CUBE[] = {
252       2, 1, 0, 5, 4, 3, 8, 7, 6, 11, 10, 9, 14, 13, 12, 17, 16, 15, 20, 19, 18, 23, 22, 21, 26, 25, 24, 29, 28, 27, 32, 31, 30, 35, 34, 33};
253
254     mGeometry = Geometry::New();
255     mGeometry.AddVertexBuffer(vertexBuffer);
256     mGeometry.SetIndexBuffer(INDEX_CUBE,
257                              sizeof(INDEX_CUBE) / sizeof(INDEX_CUBE[0]));
258     mGeometry.SetType(Geometry::TRIANGLES);
259   }
260
261   /**
262    * @brief CreateCubeGeometry
263    * This function creates a cube geometry including texture coordinates.
264    * Also it demonstrates using the indexed draw feature by setting an index array.
265    */
266   void CreateSkyboxGeometry()
267   {
268     struct Vertex
269     {
270       Vector3 aPosition;
271     };
272
273     Vertex skyboxVertices[] = {
274       // back
275       {Vector3(-1.0f, 1.0f, -1.0f)},
276       {Vector3(-1.0f, -1.0f, -1.0f)},
277       {Vector3(1.0f, -1.0f, -1.0f)},
278       {Vector3(1.0f, -1.0f, -1.0f)},
279       {Vector3(1.0f, 1.0f, -1.0f)},
280       {Vector3(-1.0f, 1.0f, -1.0f)},
281
282       // left
283       {Vector3(-1.0f, -1.0f, 1.0f)},
284       {Vector3(-1.0f, -1.0f, -1.0f)},
285       {Vector3(-1.0f, 1.0f, -1.0f)},
286       {Vector3(-1.0f, 1.0f, -1.0f)},
287       {Vector3(-1.0f, 1.0f, 1.0f)},
288       {Vector3(-1.0f, -1.0f, 1.0f)},
289
290       // right
291       {Vector3(1.0f, -1.0f, -1.0f)},
292       {Vector3(1.0f, -1.0f, 1.0f)},
293       {Vector3(1.0f, 1.0f, 1.0f)},
294       {Vector3(1.0f, 1.0f, 1.0f)},
295       {Vector3(1.0f, 1.0f, -1.0f)},
296       {Vector3(1.0f, -1.0f, -1.0f)},
297
298       // front
299       {Vector3(-1.0f, -1.0f, 1.0f)},
300       {Vector3(-1.0f, 1.0f, 1.0f)},
301       {Vector3(1.0f, 1.0f, 1.0f)},
302       {Vector3(1.0f, 1.0f, 1.0f)},
303       {Vector3(1.0f, -1.0f, 1.0f)},
304       {Vector3(-1.0f, -1.0f, 1.0f)},
305
306       // botton
307       {Vector3(-1.0f, 1.0f, -1.0f)},
308       {Vector3(1.0f, 1.0f, -1.0f)},
309       {Vector3(1.0f, 1.0f, 1.0f)},
310       {Vector3(1.0f, 1.0f, 1.0f)},
311       {Vector3(-1.0f, 1.0f, 1.0f)},
312       {Vector3(-1.0f, 1.0f, -1.0f)},
313
314       // top
315       {Vector3(-1.0f, -1.0f, -1.0f)},
316       {Vector3(-1.0f, -1.0f, 1.0f)},
317       {Vector3(1.0f, -1.0f, -1.0f)},
318       {Vector3(1.0f, -1.0f, -1.0f)},
319       {Vector3(-1.0f, -1.0f, 1.0f)},
320       {Vector3(1.0f, -1.0f, 1.0f)}};
321
322     VertexBuffer vertexBuffer = VertexBuffer::New(Property::Map()
323                                                     .Add("aPosition", Property::VECTOR3));
324     vertexBuffer.SetData(skyboxVertices, sizeof(skyboxVertices) / sizeof(Vertex));
325
326     mSkyboxGeometry = Geometry::New();
327     mSkyboxGeometry.AddVertexBuffer(vertexBuffer);
328     mSkyboxGeometry.SetType(Geometry::TRIANGLES);
329   }
330
331   /**
332    * Display a cube at the world origin
333    */
334   void DisplayCube()
335   {
336     // Load image from file
337     PixelData pixels = SyncImageLoader::Load(TEXTURE_URL);
338
339     Texture texture = Texture::New(TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight());
340     texture.Upload(pixels, 0, 0, 0, 0, pixels.GetWidth(), pixels.GetHeight());
341
342     // create TextureSet
343     mTextureSet = TextureSet::New();
344     mTextureSet.SetTexture(0, texture);
345
346     mRenderer = Renderer::New(mGeometry, mShaderCube);
347     mRenderer.SetTextures(mTextureSet);
348     mRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, 1.0f);
349
350     // A further optimization would be to enable debug testing instead
351     mRenderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);
352
353     // Enables the write on the depth buffer.
354     mRenderer.SetProperty(Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON);
355
356     mActor = Actor::New();
357     mActor.SetProperty(Dali::Actor::Property::NAME, "Cube");
358     mActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
359     mActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
360     mActor.AddRenderer(mRenderer);
361
362     mActor.SetProperty(Actor::Property::SIZE, Vector3(10.f, 10.f, 10.f));
363
364     Window window = mApplication.GetWindow();
365     window.Add(mActor);
366   }
367
368   /**
369    * Display a skybox surrounding the camera
370    */
371   void DisplaySkybox(SkyboxType type)
372   {
373     mSkyboxTextures.Reset();
374     mSkyboxRenderer.Reset();
375     mSkyboxActor.Reset();
376
377     Texture texture;
378
379     if(type == SkyboxType::CUBEMAP)
380     {
381       // Load skybox faces from file
382       texture = Texture::New(TextureType::TEXTURE_CUBE, Pixel::RGBA8888, SKYBOX_FACE_WIDTH, SKYBOX_FACE_HEIGHT);
383       for(unsigned int i = 0; i < SKYBOX_FACE_COUNT; i++)
384       {
385         PixelData pixels = SyncImageLoader::Load(SKYBOX_FACES[i]);
386         texture.Upload(pixels, CubeMapLayer::POSITIVE_X + i, 0, 0, 0, SKYBOX_FACE_WIDTH, SKYBOX_FACE_HEIGHT);
387       }
388
389       mSkyboxRenderer = Renderer::New(mSkyboxGeometry, mShaderSkybox);
390     }
391     else
392     {
393       // Load equirectangular image from file
394       PixelData pixels = SyncImageLoader::Load(EQUIRECTANGULAR_TEXTURE_URL);
395
396       texture = Texture::New(TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight());
397       texture.Upload(pixels, 0, 0, 0, 0, pixels.GetWidth(), pixels.GetHeight());
398
399       mSkyboxRenderer = Renderer::New(mSkyboxGeometry, mShaderSkyboxEquirectangular);
400     }
401
402     // create TextureSet
403     mSkyboxTextures = TextureSet::New();
404     mSkyboxTextures.SetTexture(0, texture);
405
406     mSkyboxRenderer.SetTextures(mSkyboxTextures);
407     mSkyboxRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, 2.0f);
408
409     // Enables the depth test.
410     mSkyboxRenderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON);
411
412     // The fragment shader will run only is those pixels that have the max depth value.
413     mSkyboxRenderer.SetProperty(Renderer::Property::DEPTH_FUNCTION, DepthFunction::LESS_EQUAL);
414
415     Window window = mApplication.GetWindow();
416
417     mSkyboxActor = Actor::New();
418     mSkyboxActor.SetProperty(Dali::Actor::Property::NAME, "SkyBox");
419     mSkyboxActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
420     mSkyboxActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
421     mSkyboxActor.SetProperty(Actor::Property::POSITION, CAMERA_DEFAULT_POSITION);
422     mSkyboxActor.AddRenderer(mSkyboxRenderer);
423     window.Add(mSkyboxActor);
424   }
425
426   /**
427    * Plays animation
428    */
429   void PlayAnimation()
430   {
431     mAnimation = Animation::New(5.0f);
432     mAnimation.SetLooping(true);
433     mAnimation.AnimateBy(Property(mActor, Actor::Property::ORIENTATION), Quaternion(Radian(Degree(360)), Vector3::ZAXIS));
434     mAnimation.AnimateBy(Property(mActor, Actor::Property::ORIENTATION), Quaternion(Radian(Degree(360)), Vector3::YAXIS));
435     mAnimation.AnimateBy(Property(mActor, Actor::Property::ORIENTATION), Quaternion(Radian(Degree(360)), Vector3::XAXIS));
436     mAnimation.Play();
437   }
438
439 private:
440   Application& mApplication;
441
442   LookCamera mCamera;
443
444   Shader mShaderCube;
445   Shader mShaderSkybox;
446   Shader mShaderSkyboxEquirectangular;
447
448   Geometry   mGeometry;
449   TextureSet mTextureSet;
450   Renderer   mRenderer;
451   Actor      mActor;
452   Animation  mAnimation;
453
454   Geometry   mSkyboxGeometry;
455   TextureSet mSkyboxTextures;
456   Renderer   mSkyboxRenderer;
457   Actor      mSkyboxActor;
458
459   TapGestureDetector mDoubleTapGesture;
460   SkyboxType         mCurrentSkyboxType;
461 };
462
463 int DALI_EXPORT_API main(int argc, char** argv)
464 {
465   Application            application = Application::New(&argc, &argv);
466   TexturedCubeController test(application);
467   application.MainLoop();
468   return 0;
469 }