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