2 * Copyright (c) 2020 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 "look-camera.h"
22 #include "generated/rendering-skybox-vert.h"
23 #include "generated/rendering-skybox-frag.h"
24 #include "generated/rendering-skybox-cube-vert.h"
25 #include "generated/rendering-skybox-cube-frag.h"
28 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 * Credit to Joey do Vries for the following cubemap images
46 * Take from git://github.com/JoeyDeVries/LearnOpenGL.git
47 * The images are licensed under the terms of the CC BY 4.0 license:
48 * https://creativecommons.org/licenses/by/4.0/
50 const char* SKYBOX_FACES[SKYBOX_FACE_COUNT] =
52 DEMO_IMAGE_DIR "lake_right.jpg",
53 DEMO_IMAGE_DIR "lake_left.jpg",
54 DEMO_IMAGE_DIR "lake_top.jpg",
55 DEMO_IMAGE_DIR "lake_bottom.jpg",
56 DEMO_IMAGE_DIR "lake_back.jpg",
57 DEMO_IMAGE_DIR "lake_front.jpg"};
61 // This example shows how to create a skybox
63 // Recommended screen size on desktop: 1280x720
65 class TexturedCubeController : public ConnectionTracker
68 TexturedCubeController(Application& application)
69 : mApplication(application)
71 // Connect to the Application's Init signal
72 mApplication.InitSignal().Connect(this, &TexturedCubeController::Create);
75 ~TexturedCubeController()
77 // Nothing to do here;
80 // The Init signal is received once (only) during the Application lifetime
81 void Create(Application& application)
83 // Get a handle to the window
84 Window window = application.GetWindow();
85 window.SetBackgroundColor(Color::WHITE);
87 // Step 1. Setup camera
90 // Step 2. Create shaders
93 // Step 3. Create geometry
95 CreateSkyboxGeometry();
97 // Step 4. Display first a cube at the world origin.
98 // The write on the depth buffer is enabled.
101 // Step 5. Display last the skybox surrounding the camera.
102 // The depth test is enabled, the shader sets 1.0, which is the maximum depth and
103 // the depth function is set to LESS or EQUAL so the fragment shader will run only
104 // in those pixels that any other object has written on them.
107 // Step 6. Play animation to rotate the cube
110 // Respond to key events
111 window.KeyEventSignal().Connect(this, &TexturedCubeController::OnKeyEvent);
115 * @brief Called when any key event is received
117 * Will use this to quit the application if Back or the Escape key is received
118 * @param[in] event The key event information
120 void OnKeyEvent(const KeyEvent& event)
122 if(event.GetState() == KeyEvent::DOWN)
124 if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
132 * @brief Setup a perspective camera pointing in the negative Z direction
136 Window window = mApplication.GetWindow();
138 RenderTask renderTask = window.GetRenderTaskList().GetTask(0);
139 renderTask.SetCullMode(false); // avoid frustum culling affecting the skybox
141 mCamera.Initialise(window, CAMERA_DEFAULT_POSITION, CAMERA_DEFAULT_FOV, CAMERA_DEFAULT_NEAR, CAMERA_DEFAULT_FAR);
145 * Creates a shader using inlined variable VERTEX_SHADER and FRAGMENT_SHADER
147 * Shaders are very basic and all they do is transforming vertices and sampling
152 mShaderCube = Shader::New(SHADER_RENDERING_SKYBOX_CUBE_VERT, SHADER_RENDERING_SKYBOX_CUBE_FRAG);
153 mShaderSkybox = Shader::New(SHADER_RENDERING_SKYBOX_VERT, SHADER_RENDERING_SKYBOX_FRAG);
157 * @brief CreateCubeGeometry
158 * This function creates a cube geometry including texture coordinates.
159 * Also it demonstrates using the indexed draw feature by setting an index array.
161 void CreateCubeGeometry()
169 Vertex vertices[] = {
170 {Vector3(1.0f, -1.0f, -1.0f), Vector2(1.0, 1.0)},
171 {Vector3(-1.0f, 1.0f, -1.0f), Vector2(0.0, 0.0)},
172 {Vector3(1.0f, 1.0f, -1.0f), Vector2(0.0, 1.0)},
173 {Vector3(-1.0f, 1.0f, 1.0f), Vector2(1.0, 1.0)},
174 {Vector3(1.0f, -1.0f, 1.0f), Vector2(0.0, 0.0)},
175 {Vector3(1.0f, 1.0f, 1.0f), Vector2(0.0, 1.0)},
176 {Vector3(1.0f, 1.0f, 1.0f), Vector2(1.0, 1.0)},
177 {Vector3(1.0f, -1.0f, -1.0f), Vector2(0.0, 0.0)},
178 {Vector3(1.0f, 1.0f, -1.0f), Vector2(0.0, 1.0)},
179 {Vector3(1.0f, -1.0f, 1.0f), Vector2(1.0, 1.0)},
180 {Vector3(-1.0f, -1.0f, -1.0f), Vector2(0.0, 0.0)},
181 {Vector3(1.0f, -1.0f, -1.0f), Vector2(0.0, 1.0)},
182 {Vector3(-1.0f, -1.0f, -1.0f), Vector2(1.0, 1.0)},
183 {Vector3(-1.0f, 1.0f, 1.0f), Vector2(0.0, 0.0)},
184 {Vector3(-1.0f, 1.0f, -1.0f), Vector2(0.0, 1.0)},
185 {Vector3(1.0f, 1.0f, -1.0f), Vector2(1.0, 1.0)},
186 {Vector3(-1.0f, 1.0f, 1.0f), Vector2(0.0, 0.0)},
187 {Vector3(1.0f, 1.0f, 1.0f), Vector2(0.0, 1.0)},
188 {Vector3(1.0f, -1.0f, -1.0f), Vector2(1.0, 1.0)},
189 {Vector3(-1.0f, -1.0f, -1.0f), Vector2(1.0, 0.0)},
190 {Vector3(-1.0f, 1.0f, -1.0f), Vector2(0.0, 0.0)},
191 {Vector3(-1.0f, 1.0f, 1.0f), Vector2(1.0, 1.0)},
192 {Vector3(-1.0f, -1.0f, 1.0f), Vector2(1.0, 0.0)},
193 {Vector3(1.0f, -1.0f, 1.0f), Vector2(0.0, 0.0)},
194 {Vector3(1.0f, 1.0f, 1.0f), Vector2(1.0, 1.0)},
195 {Vector3(1.0f, -1.0f, 1.0f), Vector2(1.0, 0.0)},
196 {Vector3(1.0f, -1.0f, -1.0f), Vector2(0.0, 0.0)},
197 {Vector3(1.0f, -1.0f, 1.0f), Vector2(1.0, 1.0)},
198 {Vector3(-1.0f, -1.0f, 1.0f), Vector2(1.0, 0.0)},
199 {Vector3(-1.0f, -1.0f, -1.0f), Vector2(0.0, 0.0)},
200 {Vector3(-1.0f, -1.0f, -1.0f), Vector2(1.0, 1.0)},
201 {Vector3(-1.0f, -1.0f, 1.0f), Vector2(1.0, 0.0)},
202 {Vector3(-1.0f, 1.0f, 1.0f), Vector2(0.0, 0.0)},
203 {Vector3(1.0f, 1.0f, -1.0f), Vector2(1.0, 1.0)},
204 {Vector3(-1.0f, 1.0f, -1.0f), Vector2(1.0, 0.0)},
205 {Vector3(-1.0f, 1.0f, 1.0f), Vector2(0.0, 0.0)},
208 VertexBuffer vertexBuffer = VertexBuffer::New(Property::Map()
209 .Add("aPosition", Property::VECTOR3)
210 .Add("aTexCoord", Property::VECTOR2));
211 vertexBuffer.SetData(vertices, sizeof(vertices) / sizeof(Vertex));
214 const unsigned short INDEX_CUBE[] = {
215 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};
217 mGeometry = Geometry::New();
218 mGeometry.AddVertexBuffer(vertexBuffer);
219 mGeometry.SetIndexBuffer(INDEX_CUBE,
220 sizeof(INDEX_CUBE) / sizeof(INDEX_CUBE[0]));
221 mGeometry.SetType(Geometry::TRIANGLES);
225 * @brief CreateCubeGeometry
226 * This function creates a cube geometry including texture coordinates.
227 * Also it demonstrates using the indexed draw feature by setting an index array.
229 void CreateSkyboxGeometry()
236 Vertex skyboxVertices[] = {
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 {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 {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 {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 {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 {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 {Vector3(1.0f, -1.0f, 1.0f)}};
285 VertexBuffer vertexBuffer = VertexBuffer::New(Property::Map()
286 .Add("aPosition", Property::VECTOR3));
287 vertexBuffer.SetData(skyboxVertices, sizeof(skyboxVertices) / sizeof(Vertex));
289 mSkyboxGeometry = Geometry::New();
290 mSkyboxGeometry.AddVertexBuffer(vertexBuffer);
291 mSkyboxGeometry.SetType(Geometry::TRIANGLES);
295 * Display a cube at the world origin
299 // Load image from file
300 PixelData pixels = SyncImageLoader::Load(TEXTURE_URL);
302 Texture texture = Texture::New(TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight());
303 texture.Upload(pixels, 0, 0, 0, 0, pixels.GetWidth(), pixels.GetHeight());
306 mTextureSet = TextureSet::New();
307 mTextureSet.SetTexture(0, texture);
309 mRenderer = Renderer::New(mGeometry, mShaderCube);
310 mRenderer.SetTextures(mTextureSet);
311 mRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, 1.0f);
313 // A further optimization would be to enable debug testing instead
314 mRenderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);
316 // Enables the write on the depth buffer.
317 mRenderer.SetProperty(Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON);
319 mActor = Actor::New();
320 mActor.SetProperty(Dali::Actor::Property::NAME, "Cube");
321 mActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
322 mActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
323 mActor.AddRenderer(mRenderer);
325 mActor.SetProperty(Actor::Property::SIZE, Vector3(10.f, 10.f, 10.f));
327 Window window = mApplication.GetWindow();
332 * Display a skybox surrounding the camera
336 // Load skybox faces from file
337 Texture texture = Texture::New(TextureType::TEXTURE_CUBE, Pixel::RGBA8888, SKYBOX_FACE_WIDTH, SKYBOX_FACE_HEIGHT);
338 for(unsigned int i = 0; i < SKYBOX_FACE_COUNT; i++)
340 PixelData pixels = SyncImageLoader::Load(SKYBOX_FACES[i]);
341 texture.Upload(pixels, CubeMapLayer::POSITIVE_X + i, 0, 0, 0, SKYBOX_FACE_WIDTH, SKYBOX_FACE_HEIGHT);
345 mSkyboxTextures = TextureSet::New();
346 mSkyboxTextures.SetTexture(0, texture);
348 mSkyboxRenderer = Renderer::New(mSkyboxGeometry, mShaderSkybox);
349 mSkyboxRenderer.SetTextures(mSkyboxTextures);
350 mSkyboxRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, 2.0f);
352 // Enables the depth test.
353 mSkyboxRenderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON);
355 // The fragment shader will run only is those pixels that have the max depth value.
356 mSkyboxRenderer.SetProperty(Renderer::Property::DEPTH_FUNCTION, DepthFunction::LESS_EQUAL);
358 Window window = mApplication.GetWindow();
360 mSkyboxActor = Actor::New();
361 mSkyboxActor.SetProperty(Dali::Actor::Property::NAME, "SkyBox");
362 mSkyboxActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
363 mSkyboxActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
364 mSkyboxActor.SetProperty(Actor::Property::POSITION, CAMERA_DEFAULT_POSITION);
365 mSkyboxActor.AddRenderer(mSkyboxRenderer);
366 window.Add(mSkyboxActor);
374 mAnimation = Animation::New(5.0f);
375 mAnimation.SetLooping(true);
376 mAnimation.AnimateBy(Property(mActor, Actor::Property::ORIENTATION), Quaternion(Radian(Degree(360)), Vector3::ZAXIS));
377 mAnimation.AnimateBy(Property(mActor, Actor::Property::ORIENTATION), Quaternion(Radian(Degree(360)), Vector3::YAXIS));
378 mAnimation.AnimateBy(Property(mActor, Actor::Property::ORIENTATION), Quaternion(Radian(Degree(360)), Vector3::XAXIS));
383 Application& mApplication;
388 Shader mShaderSkybox;
391 TextureSet mTextureSet;
394 Animation mAnimation;
396 Geometry mSkyboxGeometry;
397 TextureSet mSkyboxTextures;
398 Renderer mSkyboxRenderer;
402 int DALI_EXPORT_API main(int argc, char** argv)
404 Application application = Application::New(&argc, &argv);
405 TexturedCubeController test(application);
406 application.MainLoop();