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