2 * Copyright (c) 2021 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-frag.h"
24 #include "generated/rendering-skybox-vert.h"
25 #include "look-camera.h"
28 using namespace Toolkit;
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);
37 const char* TEXTURE_URL = DEMO_IMAGE_DIR "wood.png";
39 const unsigned int SKYBOX_FACE_COUNT = 6;
40 const unsigned int SKYBOX_FACE_WIDTH = 2048;
41 const unsigned int SKYBOX_FACE_HEIGHT = 2048;
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/
49 const char* SKYBOX_FACES[SKYBOX_FACE_COUNT] =
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"};
60 // This example shows how to create a skybox
62 // Recommended screen size on desktop: 1280x720
64 class TexturedCubeController : public ConnectionTracker
67 TexturedCubeController(Application& application)
68 : mApplication(application)
70 // Connect to the Application's Init signal
71 mApplication.InitSignal().Connect(this, &TexturedCubeController::Create);
74 ~TexturedCubeController()
76 // Nothing to do here;
79 // The Init signal is received once (only) during the Application lifetime
80 void Create(Application& application)
82 // Get a handle to the window
83 Window window = application.GetWindow();
84 window.SetBackgroundColor(Color::WHITE);
86 // Step 1. Setup camera
89 // Step 2. Create shaders
92 // Step 3. Create geometry
94 CreateSkyboxGeometry();
96 // Step 4. Display first a cube at the world origin.
97 // The write on the depth buffer is enabled.
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.
106 // Step 6. Play animation to rotate the cube
109 // Respond to key events
110 window.KeyEventSignal().Connect(this, &TexturedCubeController::OnKeyEvent);
114 * @brief Called when any key event is received
116 * Will use this to quit the application if Back or the Escape key is received
117 * @param[in] event The key event information
119 void OnKeyEvent(const KeyEvent& event)
121 if(event.GetState() == KeyEvent::DOWN)
123 if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
131 * @brief Setup a perspective camera pointing in the negative Z direction
135 Window window = mApplication.GetWindow();
137 RenderTask renderTask = window.GetRenderTaskList().GetTask(0);
138 renderTask.SetCullMode(false); // avoid frustum culling affecting the skybox
140 mCamera.Initialise(window, CAMERA_DEFAULT_POSITION, CAMERA_DEFAULT_FOV, CAMERA_DEFAULT_NEAR, CAMERA_DEFAULT_FAR);
144 * Creates a shader using inlined variable VERTEX_SHADER and FRAGMENT_SHADER
146 * Shaders are very basic and all they do is transforming vertices and sampling
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);
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.
160 void CreateCubeGeometry()
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)},
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));
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};
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);
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.
228 void CreateSkyboxGeometry()
235 Vertex skyboxVertices[] = {
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)},
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)},
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)},
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)},
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)},
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)}};
284 VertexBuffer vertexBuffer = VertexBuffer::New(Property::Map()
285 .Add("aPosition", Property::VECTOR3));
286 vertexBuffer.SetData(skyboxVertices, sizeof(skyboxVertices) / sizeof(Vertex));
288 mSkyboxGeometry = Geometry::New();
289 mSkyboxGeometry.AddVertexBuffer(vertexBuffer);
290 mSkyboxGeometry.SetType(Geometry::TRIANGLES);
294 * Display a cube at the world origin
298 // Load image from file
299 PixelData pixels = SyncImageLoader::Load(TEXTURE_URL);
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());
305 mTextureSet = TextureSet::New();
306 mTextureSet.SetTexture(0, texture);
308 mRenderer = Renderer::New(mGeometry, mShaderCube);
309 mRenderer.SetTextures(mTextureSet);
310 mRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, 1.0f);
312 // A further optimization would be to enable debug testing instead
313 mRenderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);
315 // Enables the write on the depth buffer.
316 mRenderer.SetProperty(Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON);
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);
324 mActor.SetProperty(Actor::Property::SIZE, Vector3(10.f, 10.f, 10.f));
326 Window window = mApplication.GetWindow();
331 * Display a skybox surrounding the camera
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++)
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);
344 mSkyboxTextures = TextureSet::New();
345 mSkyboxTextures.SetTexture(0, texture);
347 mSkyboxRenderer = Renderer::New(mSkyboxGeometry, mShaderSkybox);
348 mSkyboxRenderer.SetTextures(mSkyboxTextures);
349 mSkyboxRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, 2.0f);
351 // Enables the depth test.
352 mSkyboxRenderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON);
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);
357 Window window = mApplication.GetWindow();
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);
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));
382 Application& mApplication;
387 Shader mShaderSkybox;
390 TextureSet mTextureSet;
393 Animation mAnimation;
395 Geometry mSkyboxGeometry;
396 TextureSet mSkyboxTextures;
397 Renderer mSkyboxRenderer;
401 int DALI_EXPORT_API main(int argc, char** argv)
403 Application application = Application::New(&argc, &argv);
404 TexturedCubeController test(application);
405 application.MainLoop();