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/dali.h>
19 #include <dali-toolkit/dali-toolkit.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
98 const float CAMERA_DEFAULT_FOV( 60.0f );
99 const float CAMERA_DEFAULT_NEAR( 0.1f );
100 const float CAMERA_DEFAULT_FAR( 1000.0f );
101 const Vector3 CAMERA_DEFAULT_POSITION( 0.0f, 0.0f, 100.0f );
103 const char* TEXTURE_URL = DEMO_IMAGE_DIR "wood.png";
105 const unsigned int SKYBOX_FACE_COUNT = 6;
106 const unsigned int SKYBOX_FACE_WIDTH = 2048;
107 const unsigned int SKYBOX_FACE_HEIGHT = 2048;
110 * Credit to Joey do Vries for the following cubemap images
111 * Take from git://github.com/JoeyDeVries/LearnOpenGL.git
112 * The images are licensed under the terms of the CC BY 4.0 license:
113 * https://creativecommons.org/licenses/by/4.0/
115 const char* SKYBOX_FACES[ SKYBOX_FACE_COUNT ] =
117 DEMO_IMAGE_DIR "lake_right.jpg",
118 DEMO_IMAGE_DIR "lake_left.jpg",
119 DEMO_IMAGE_DIR "lake_top.jpg",
120 DEMO_IMAGE_DIR "lake_bottom.jpg",
121 DEMO_IMAGE_DIR "lake_back.jpg",
122 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
135 TexturedCubeController( Application& application )
136 : mApplication( application )
138 // Connect to the Application's Init signal
139 mApplication.InitSignal().Connect( this, &TexturedCubeController::Create );
142 ~TexturedCubeController()
144 // Nothing to do here;
147 // The Init signal is received once (only) during the Application lifetime
148 void Create( Application& application )
150 // Get a handle to the stage
151 Stage stage = Stage::GetCurrent();
152 stage.SetBackgroundColor( Color::WHITE );
154 // Step 1. Setup camera
157 // Step 2. Create shaders
160 // Step 3. Create geometry
161 CreateCubeGeometry();
162 CreateSkyboxGeometry();
164 // Step 4. Display first a cube at the world origin.
165 // The write on the depth buffer is enabled.
168 // Step 5. Display last the skybox surrounding the camera.
169 // The depth test is enabled, the shader sets 1.0, which is the maximum depth and
170 // the depth function is set to LESS or EQUAL so the fragment shader will run only
171 // in those pixels that any other object has written on them.
174 // Step 6. Play animation to rotate the cube
177 // Respond to key events
178 stage.KeyEventSignal().Connect( this, &TexturedCubeController::OnKeyEvent );
182 * @brief Called when any key event is received
184 * Will use this to quit the application if Back or the Escape key is received
185 * @param[in] event The key event information
187 void OnKeyEvent( const KeyEvent& event )
189 if( event.state == KeyEvent::Down )
191 if ( IsKey( event, Dali::DALI_KEY_ESCAPE ) || IsKey( event, Dali::DALI_KEY_BACK ) )
199 * @brief Setup a perspective camera pointing in the negative Z direction
203 Stage stage = Stage::GetCurrent();
205 RenderTask renderTask = stage.GetRenderTaskList().GetTask( 0 );
206 renderTask.SetCullMode( false ); // avoid frustum culling affecting the skybox
208 mCamera.Initialise( CAMERA_DEFAULT_POSITION, CAMERA_DEFAULT_FOV, CAMERA_DEFAULT_NEAR, CAMERA_DEFAULT_FAR );
212 * Creates a shader using inlined variable VERTEX_SHADER and FRAGMENT_SHADER
214 * Shaders are very basic and all they do is transforming vertices and sampling
219 mShaderCube = Shader::New( VERTEX_SHADER_CUBE, FRAGMENT_SHADER_CUBE );
220 mShaderSkybox = Shader::New( VERTEX_SHADER_SKYBOX, FRAGMENT_SHADER_SKYBOX );
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 CreateCubeGeometry()
236 Vertex vertices[] = {
237 { Vector3( 1.0f,-1.0f,-1.0f ), Vector2( 1.0, 1.0 ) },
238 { Vector3( -1.0f, 1.0f,-1.0f ), Vector2( 0.0, 0.0 ) },
239 { Vector3( 1.0f, 1.0f,-1.0f ), Vector2( 0.0, 1.0 ) },
240 { Vector3( -1.0f, 1.0f, 1.0f ), Vector2( 1.0, 1.0 ) },
241 { Vector3( 1.0f,-1.0f, 1.0f ), Vector2( 0.0, 0.0 ) },
242 { Vector3( 1.0f, 1.0f, 1.0f ), Vector2( 0.0, 1.0 ) },
243 { Vector3( 1.0f, 1.0f, 1.0f ), Vector2( 1.0, 1.0 ) },
244 { Vector3( 1.0f,-1.0f,-1.0f ), Vector2( 0.0, 0.0 ) },
245 { Vector3( 1.0f, 1.0f,-1.0f ), Vector2( 0.0, 1.0 ) },
246 { Vector3( 1.0f,-1.0f, 1.0f ), Vector2( 1.0, 1.0 ) },
247 { Vector3( -1.0f,-1.0f,-1.0f ), Vector2( 0.0, 0.0 ) },
248 { Vector3( 1.0f,-1.0f,-1.0f ), Vector2( 0.0, 1.0 ) },
249 { Vector3( -1.0f,-1.0f,-1.0f ), Vector2( 1.0, 1.0 ) },
250 { Vector3( -1.0f, 1.0f, 1.0f ), Vector2( 0.0, 0.0 ) },
251 { Vector3( -1.0f, 1.0f,-1.0f ), Vector2( 0.0, 1.0 ) },
252 { Vector3( 1.0f, 1.0f,-1.0f ), Vector2( 1.0, 1.0 ) },
253 { Vector3( -1.0f, 1.0f, 1.0f ), Vector2( 0.0, 0.0 ) },
254 { Vector3( 1.0f, 1.0f, 1.0f ), Vector2( 0.0, 1.0 ) },
255 { Vector3( 1.0f,-1.0f,-1.0f ), Vector2( 1.0, 1.0 ) },
256 { Vector3( -1.0f,-1.0f,-1.0f ), Vector2( 1.0, 0.0 ) },
257 { Vector3( -1.0f, 1.0f,-1.0f ), Vector2( 0.0, 0.0 ) },
258 { Vector3( -1.0f, 1.0f, 1.0f ), Vector2( 1.0, 1.0 ) },
259 { Vector3( -1.0f,-1.0f, 1.0f ), Vector2( 1.0, 0.0 ) },
260 { Vector3( 1.0f,-1.0f, 1.0f ), Vector2( 0.0, 0.0 ) },
261 { Vector3( 1.0f, 1.0f, 1.0f ), Vector2( 1.0, 1.0 ) },
262 { Vector3( 1.0f,-1.0f, 1.0f ), Vector2( 1.0, 0.0 ) },
263 { Vector3( 1.0f,-1.0f,-1.0f ), Vector2( 0.0, 0.0 ) },
264 { Vector3( 1.0f,-1.0f, 1.0f ), Vector2( 1.0, 1.0 ) },
265 { Vector3( -1.0f,-1.0f, 1.0f ), Vector2( 1.0, 0.0 ) },
266 { Vector3( -1.0f,-1.0f,-1.0f ), Vector2( 0.0, 0.0 ) },
267 { Vector3( -1.0f,-1.0f,-1.0f ), Vector2( 1.0, 1.0 ) },
268 { Vector3( -1.0f,-1.0f, 1.0f ), Vector2( 1.0, 0.0 ) },
269 { Vector3( -1.0f, 1.0f, 1.0f ), Vector2( 0.0, 0.0 ) },
270 { Vector3( 1.0f, 1.0f,-1.0f ), Vector2( 1.0, 1.0 ) },
271 { Vector3( -1.0f, 1.0f,-1.0f ), Vector2( 1.0, 0.0 ) },
272 { Vector3( -1.0f, 1.0f, 1.0f ), Vector2( 0.0, 0.0 ) },
275 PropertyBuffer vertexBuffer = PropertyBuffer::New( Property::Map()
276 .Add( "aPosition", Property::VECTOR3 )
277 .Add( "aTexCoord", Property::VECTOR2 ) );
278 vertexBuffer.SetData( vertices, sizeof(vertices) / sizeof(Vertex) );
281 const unsigned short INDEX_CUBE[] = {
296 mGeometry = Geometry::New();
297 mGeometry.AddVertexBuffer( vertexBuffer );
298 mGeometry.SetIndexBuffer( INDEX_CUBE,
299 sizeof(INDEX_CUBE)/sizeof(INDEX_CUBE[0]) );
300 mGeometry.SetType( Geometry::TRIANGLES );
304 * @brief CreateCubeGeometry
305 * This function creates a cube geometry including texture coordinates.
306 * Also it demonstrates using the indexed draw feature by setting an index array.
308 void CreateSkyboxGeometry()
315 Vertex skyboxVertices[] = {
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 ) },
321 { Vector3( 1.0f, 1.0f, -1.0f ) },
322 { Vector3( -1.0f, 1.0f, -1.0f ) },
325 { Vector3( -1.0f, -1.0f, 1.0f ) },
326 { Vector3( -1.0f, -1.0f, -1.0f ) },
327 { 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 ) },
333 { Vector3( 1.0f, -1.0f, -1.0f ) },
334 { Vector3( 1.0f, -1.0f, 1.0f ) },
335 { 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 ) },
341 { Vector3( -1.0f, -1.0f, 1.0f ) },
342 { Vector3( -1.0f, 1.0f, 1.0f ) },
343 { 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 ) },
349 { Vector3( -1.0f, 1.0f, -1.0f ) },
350 { Vector3( 1.0f, 1.0f, -1.0f ) },
351 { Vector3( 1.0f, 1.0f, 1.0f ) },
352 { Vector3( 1.0f, 1.0f, 1.0f ) },
353 { Vector3( -1.0f, 1.0f, 1.0f ) },
354 { Vector3( -1.0f, 1.0f, -1.0f ) },
357 { Vector3( -1.0f, -1.0f, -1.0f ) },
358 { Vector3( -1.0f, -1.0f, 1.0f ) },
359 { Vector3( 1.0f, -1.0f, -1.0f ) },
360 { Vector3( 1.0f, -1.0f, -1.0f ) },
361 { Vector3( -1.0f, -1.0f, 1.0f ) },
362 { Vector3( 1.0f, -1.0f, 1.0f ) }
365 PropertyBuffer vertexBuffer = PropertyBuffer::New( Property::Map()
366 .Add( "aPosition", Property::VECTOR3 ) );
367 vertexBuffer.SetData( skyboxVertices, sizeof(skyboxVertices) / sizeof(Vertex) );
369 mSkyboxGeometry = Geometry::New();
370 mSkyboxGeometry.AddVertexBuffer( vertexBuffer );
371 mSkyboxGeometry.SetType( Geometry::TRIANGLES );
375 * Display a cube at the world origin
379 // Load image from file
380 PixelData pixels = SyncImageLoader::Load( TEXTURE_URL );
382 Texture texture = Texture::New( TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight() );
383 texture.Upload( pixels, 0, 0, 0, 0, pixels.GetWidth(), pixels.GetHeight() );
386 mTextureSet = TextureSet::New();
387 mTextureSet.SetTexture( 0, texture );
389 mRenderer = Renderer::New( mGeometry, mShaderCube );
390 mRenderer.SetTextures( mTextureSet );
391 mRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, 1.0f );
393 // A further optimization would be to enable debug testing instead
394 mRenderer.SetProperty( Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK );
396 // Enables the write on the depth buffer.
397 mRenderer.SetProperty( Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON );
399 mActor = Actor::New();
400 mActor.SetProperty( Dali::Actor::Property::NAME, "Cube" );
401 mActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
402 mActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
403 mActor.AddRenderer( mRenderer );
405 mActor.SetProperty( Actor::Property::SIZE, Vector3( 10.f, 10.f, 10.f ) );
407 Stage stage = Stage::GetCurrent();
412 * Display a skybox surrounding the camera
416 // Load skybox faces from file
417 Texture texture = Texture::New( TextureType::TEXTURE_CUBE, Pixel::RGBA8888, SKYBOX_FACE_WIDTH, SKYBOX_FACE_HEIGHT );
418 for (unsigned int i = 0; i < SKYBOX_FACE_COUNT; i++)
420 PixelData pixels = SyncImageLoader::Load( SKYBOX_FACES[i] );
421 texture.Upload( pixels, CubeMapLayer::POSITIVE_X + i, 0, 0, 0, SKYBOX_FACE_WIDTH, SKYBOX_FACE_HEIGHT );
425 mSkyboxTextures = TextureSet::New();
426 mSkyboxTextures.SetTexture( 0, texture );
428 mSkyboxRenderer = Renderer::New( mSkyboxGeometry, mShaderSkybox );
429 mSkyboxRenderer.SetTextures( mSkyboxTextures );
430 mSkyboxRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, 2.0f );
432 // Enables the depth test.
433 mSkyboxRenderer.SetProperty( Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON );
435 // The fragment shader will run only is those pixels that have the max depth value.
436 mSkyboxRenderer.SetProperty( Renderer::Property::DEPTH_FUNCTION, DepthFunction::LESS_EQUAL );
438 Stage stage = Stage::GetCurrent();
440 mSkyboxActor = Actor::New();
441 mSkyboxActor.SetProperty( Dali::Actor::Property::NAME, "SkyBox" );
442 mSkyboxActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
443 mSkyboxActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
444 mSkyboxActor.SetProperty( Actor::Property::POSITION, CAMERA_DEFAULT_POSITION );
445 mSkyboxActor.AddRenderer( mSkyboxRenderer );
446 stage.Add( mSkyboxActor );
454 mAnimation = Animation::New( 5.0f );
455 mAnimation.SetLooping( true );
456 mAnimation.AnimateBy( Property( mActor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree( 360 )), Vector3::ZAXIS ) );
457 mAnimation.AnimateBy( Property( mActor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree( 360 )), Vector3::YAXIS ) );
458 mAnimation.AnimateBy( Property( mActor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree( 360 )), Vector3::XAXIS ) );
463 Application& mApplication;
468 Shader mShaderSkybox;
471 TextureSet mTextureSet;
474 Animation mAnimation;
476 Geometry mSkyboxGeometry;
477 TextureSet mSkyboxTextures;
478 Renderer mSkyboxRenderer;
482 int DALI_EXPORT_API main( int argc, char **argv )
484 Application application = Application::New( &argc, &argv );
485 TexturedCubeController test( application );
486 application.MainLoop();