2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <dali-toolkit/dali-toolkit.h>
19 #include <dali/dali.h>
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"
29 using namespace Toolkit;
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);
38 const char* TEXTURE_URL = DEMO_IMAGE_DIR "wood.png";
40 const unsigned int SKYBOX_FACE_COUNT = 6;
41 const unsigned int SKYBOX_FACE_WIDTH = 2048;
42 const unsigned int SKYBOX_FACE_HEIGHT = 2048;
45 * @brief The skybox types supported by the application.
49 CUBEMAP, ///< Skybox in cubemap
50 EQUIRECTANGULAR ///< Skybox in equirectangular projection
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/
59 const char* SKYBOX_FACES[SKYBOX_FACE_COUNT] =
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"};
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
74 const char* EQUIRECTANGULAR_TEXTURE_URL = DEMO_IMAGE_DIR "veste_oberhaus_spherical_panoramic.jpg";
77 // This example shows how to create a skybox
79 // Recommended screen size on desktop: 1280x720
81 class TexturedCubeController : public ConnectionTracker
84 TexturedCubeController(Application& application)
85 : mApplication(application),
86 mCurrentSkyboxType(SkyboxType::CUBEMAP)
88 // Connect to the Application's Init signal
89 mApplication.InitSignal().Connect(this, &TexturedCubeController::Create);
92 ~TexturedCubeController()
94 // Nothing to do here;
97 // The Init signal is received once (only) during the Application lifetime
98 void Create(Application& application)
100 // Get a handle to the window
101 Window window = application.GetWindow();
102 window.SetBackgroundColor(Color::WHITE);
104 // Step 1. Setup camera
107 // Step 2. Create shaders
110 // Step 3. Create geometry
111 CreateCubeGeometry();
112 CreateSkyboxGeometry();
114 // Step 4. Display first a cube at the world origin.
115 // The write on the depth buffer is enabled.
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);
124 // Step 6. Play animation to rotate the cube
127 // Respond to key events
128 window.KeyEventSignal().Connect(this, &TexturedCubeController::OnKeyEvent);
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);
137 * @brief Called when any key event is received
139 * Will use this to quit the application if Back or the Escape key is received
140 * @param[in] event The key event information
142 void OnKeyEvent(const KeyEvent& event)
144 if(event.GetState() == KeyEvent::DOWN)
146 if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
153 void OnDoubleTap(Actor /*actor*/, const TapGesture& /*gesture*/)
155 if(mCurrentSkyboxType == SkyboxType::CUBEMAP)
157 mCurrentSkyboxType = SkyboxType::EQUIRECTANGULAR;
161 mCurrentSkyboxType = SkyboxType::CUBEMAP;
164 DisplaySkybox(mCurrentSkyboxType);
168 * @brief Setup a perspective camera pointing in the negative Z direction
172 Window window = mApplication.GetWindow();
174 RenderTask renderTask = window.GetRenderTaskList().GetTask(0);
175 renderTask.SetCullMode(false); // avoid frustum culling affecting the skybox
177 mCamera.Initialise(window, CAMERA_DEFAULT_POSITION, CAMERA_DEFAULT_FOV, CAMERA_DEFAULT_NEAR, CAMERA_DEFAULT_FAR);
181 * Creates a shader using inlined variable VERTEX_SHADER and FRAGMENT_SHADER
183 * Shaders are very basic and all they do is transforming vertices and sampling
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);
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.
198 void CreateCubeGeometry()
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)},
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));
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};
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);
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.
266 void CreateSkyboxGeometry()
273 Vertex skyboxVertices[] = {
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)},
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)},
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)},
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)},
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)},
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)}};
322 VertexBuffer vertexBuffer = VertexBuffer::New(Property::Map()
323 .Add("aPosition", Property::VECTOR3));
324 vertexBuffer.SetData(skyboxVertices, sizeof(skyboxVertices) / sizeof(Vertex));
326 mSkyboxGeometry = Geometry::New();
327 mSkyboxGeometry.AddVertexBuffer(vertexBuffer);
328 mSkyboxGeometry.SetType(Geometry::TRIANGLES);
332 * Display a cube at the world origin
336 // Load image from file
337 PixelData pixels = SyncImageLoader::Load(TEXTURE_URL);
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());
343 mTextureSet = TextureSet::New();
344 mTextureSet.SetTexture(0, texture);
346 mRenderer = Renderer::New(mGeometry, mShaderCube);
347 mRenderer.SetTextures(mTextureSet);
348 mRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, 1.0f);
350 // A further optimization would be to enable debug testing instead
351 mRenderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);
353 // Enables the write on the depth buffer.
354 mRenderer.SetProperty(Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON);
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);
362 mActor.SetProperty(Actor::Property::SIZE, Vector3(10.f, 10.f, 10.f));
364 Window window = mApplication.GetWindow();
369 * Display a skybox surrounding the camera
371 void DisplaySkybox(SkyboxType type)
373 mSkyboxTextures.Reset();
374 mSkyboxRenderer.Reset();
375 mSkyboxActor.Reset();
379 if(type == SkyboxType::CUBEMAP)
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++)
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);
389 mSkyboxRenderer = Renderer::New(mSkyboxGeometry, mShaderSkybox);
393 // Load equirectangular image from file
394 PixelData pixels = SyncImageLoader::Load(EQUIRECTANGULAR_TEXTURE_URL);
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());
399 mSkyboxRenderer = Renderer::New(mSkyboxGeometry, mShaderSkyboxEquirectangular);
403 mSkyboxTextures = TextureSet::New();
404 mSkyboxTextures.SetTexture(0, texture);
406 mSkyboxRenderer.SetTextures(mSkyboxTextures);
407 mSkyboxRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, 2.0f);
409 // Enables the depth test.
410 mSkyboxRenderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON);
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);
415 Window window = mApplication.GetWindow();
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);
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));
440 Application& mApplication;
445 Shader mShaderSkybox;
446 Shader mShaderSkyboxEquirectangular;
449 TextureSet mTextureSet;
452 Animation mAnimation;
454 Geometry mSkyboxGeometry;
455 TextureSet mSkyboxTextures;
456 Renderer mSkyboxRenderer;
459 TapGestureDetector mDoubleTapGesture;
460 SkyboxType mCurrentSkyboxType;
463 int DALI_EXPORT_API main(int argc, char** argv)
465 Application application = Application::New(&argc, &argv);
466 TexturedCubeController test(application);
467 application.MainLoop();