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"
24 using namespace Toolkit;
30 * Vertex shader for a textured cube
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
38 varying mediump vec2 vTexCoord;\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
49 * Fragment shader for a textured cube
51 const char* FRAGMENT_SHADER_CUBE = DALI_COMPOSE_SHADER(
52 uniform sampler2D uTexture;\n
54 varying mediump vec2 vTexCoord;\n
57 mediump vec4 texColor = texture2D( uTexture, vTexCoord );\n
58 gl_FragColor = texColor;\n
63 * Vertex shader for a skybox
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
69 varying mediump vec3 vTexCoord;\n
72 vTexCoord.x = aPosition.x;\n
73 vTexCoord.y = -aPosition.y;\n // convert to GL coords
74 vTexCoord.z = aPosition.z;\n
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.
85 * Fragment shader for a skybox
87 const char* FRAGMENT_SHADER_SKYBOX = DALI_COMPOSE_SHADER(
88 uniform samplerCube uSkyBoxTexture;\n
90 varying mediump vec3 vTexCoord;\n
93 mediump vec4 texColor = textureCube( uSkyBoxTexture, vTexCoord );\n
94 gl_FragColor = texColor;\n
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);
104 const char* TEXTURE_URL = DEMO_IMAGE_DIR "wood.png";
106 const unsigned int SKYBOX_FACE_COUNT = 6;
107 const unsigned int SKYBOX_FACE_WIDTH = 2048;
108 const unsigned int SKYBOX_FACE_HEIGHT = 2048;
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/
116 const char* SKYBOX_FACES[SKYBOX_FACE_COUNT] =
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"};
127 // This example shows how to create a skybox
129 // Recommended screen size on desktop: 1280x720
131 class TexturedCubeController : public ConnectionTracker
134 TexturedCubeController(Application& application)
135 : mApplication(application)
137 // Connect to the Application's Init signal
138 mApplication.InitSignal().Connect(this, &TexturedCubeController::Create);
141 ~TexturedCubeController()
143 // Nothing to do here;
146 // The Init signal is received once (only) during the Application lifetime
147 void Create(Application& application)
149 // Get a handle to the window
150 Window window = application.GetWindow();
151 window.SetBackgroundColor(Color::WHITE);
153 // Step 1. Setup camera
156 // Step 2. Create shaders
159 // Step 3. Create geometry
160 CreateCubeGeometry();
161 CreateSkyboxGeometry();
163 // Step 4. Display first a cube at the world origin.
164 // The write on the depth buffer is enabled.
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.
173 // Step 6. Play animation to rotate the cube
176 // Respond to key events
177 window.KeyEventSignal().Connect(this, &TexturedCubeController::OnKeyEvent);
181 * @brief Called when any key event is received
183 * Will use this to quit the application if Back or the Escape key is received
184 * @param[in] event The key event information
186 void OnKeyEvent(const KeyEvent& event)
188 if(event.GetState() == KeyEvent::DOWN)
190 if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
198 * @brief Setup a perspective camera pointing in the negative Z direction
202 Window window = mApplication.GetWindow();
204 RenderTask renderTask = window.GetRenderTaskList().GetTask(0);
205 renderTask.SetCullMode(false); // avoid frustum culling affecting the skybox
207 mCamera.Initialise(window, CAMERA_DEFAULT_POSITION, CAMERA_DEFAULT_FOV, CAMERA_DEFAULT_NEAR, CAMERA_DEFAULT_FAR);
211 * Creates a shader using inlined variable VERTEX_SHADER and FRAGMENT_SHADER
213 * Shaders are very basic and all they do is transforming vertices and sampling
218 mShaderCube = Shader::New(VERTEX_SHADER_CUBE, FRAGMENT_SHADER_CUBE);
219 mShaderSkybox = Shader::New(VERTEX_SHADER_SKYBOX, FRAGMENT_SHADER_SKYBOX);
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.
227 void CreateCubeGeometry()
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)},
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));
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};
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);
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.
295 void CreateSkyboxGeometry()
302 Vertex skyboxVertices[] = {
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)},
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)},
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)},
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)},
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)},
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)}};
351 VertexBuffer vertexBuffer = VertexBuffer::New(Property::Map()
352 .Add("aPosition", Property::VECTOR3));
353 vertexBuffer.SetData(skyboxVertices, sizeof(skyboxVertices) / sizeof(Vertex));
355 mSkyboxGeometry = Geometry::New();
356 mSkyboxGeometry.AddVertexBuffer(vertexBuffer);
357 mSkyboxGeometry.SetType(Geometry::TRIANGLES);
361 * Display a cube at the world origin
365 // Load image from file
366 PixelData pixels = SyncImageLoader::Load(TEXTURE_URL);
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());
372 mTextureSet = TextureSet::New();
373 mTextureSet.SetTexture(0, texture);
375 mRenderer = Renderer::New(mGeometry, mShaderCube);
376 mRenderer.SetTextures(mTextureSet);
377 mRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, 1.0f);
379 // A further optimization would be to enable debug testing instead
380 mRenderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);
382 // Enables the write on the depth buffer.
383 mRenderer.SetProperty(Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON);
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);
391 mActor.SetProperty(Actor::Property::SIZE, Vector3(10.f, 10.f, 10.f));
393 Window window = mApplication.GetWindow();
398 * Display a skybox surrounding the camera
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++)
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);
411 mSkyboxTextures = TextureSet::New();
412 mSkyboxTextures.SetTexture(0, texture);
414 mSkyboxRenderer = Renderer::New(mSkyboxGeometry, mShaderSkybox);
415 mSkyboxRenderer.SetTextures(mSkyboxTextures);
416 mSkyboxRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, 2.0f);
418 // Enables the depth test.
419 mSkyboxRenderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON);
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);
424 Window window = mApplication.GetWindow();
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);
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));
449 Application& mApplication;
454 Shader mShaderSkybox;
457 TextureSet mTextureSet;
460 Animation mAnimation;
462 Geometry mSkyboxGeometry;
463 TextureSet mSkyboxTextures;
464 Renderer mSkyboxRenderer;
468 int DALI_EXPORT_API main(int argc, char** argv)
470 Application application = Application::New(&argc, &argv);
471 TexturedCubeController test(application);
472 application.MainLoop();