2 * Copyright (c) 2017 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 // Disable indicator.
151 // It avoids reposition the camera to fit with the indicator height.
152 Dali::Window winHandle = application.GetWindow();
153 winHandle.ShowIndicator( Dali::Window::INVISIBLE );
155 // Get a handle to the stage
156 Stage stage = Stage::GetCurrent();
157 stage.SetBackgroundColor( Color::WHITE );
159 // Step 1. Setup camera
162 // Step 2. Create shaders
165 // Step 3. Create geometry
166 CreateCubeGeometry();
167 CreateSkyboxGeometry();
169 // Step 4. Display first a cube at the world origin.
170 // The write on the depth buffer is enabled.
173 // Step 5. Display last the skybox surrounding the camera.
174 // The depth test is enabled, the shader sets 1.0, which is the maximum depth and
175 // the depth function is set to LESS or EQUAL so the fragment shader will run only
176 // in those pixels that any other object has written on them.
179 // Step 6. Play animation to rotate the cube
182 // Respond to key events
183 stage.KeyEventSignal().Connect( this, &TexturedCubeController::OnKeyEvent );
187 * @brief Called when any key event is received
189 * Will use this to quit the application if Back or the Escape key is received
190 * @param[in] event The key event information
192 void OnKeyEvent( const KeyEvent& event )
194 if( event.state == KeyEvent::Down )
196 if ( IsKey( event, Dali::DALI_KEY_ESCAPE ) || IsKey( event, Dali::DALI_KEY_BACK ) )
204 * @brief Setup a perspective camera pointing in the negative Z direction
208 Stage stage = Stage::GetCurrent();
210 RenderTask renderTask = stage.GetRenderTaskList().GetTask( 0 );
211 renderTask.SetCullMode( false ); // avoid frustum culling affecting the skybox
213 mCamera.Initialise( CAMERA_DEFAULT_POSITION, CAMERA_DEFAULT_FOV, CAMERA_DEFAULT_NEAR, CAMERA_DEFAULT_FAR );
217 * Creates a shader using inlined variable VERTEX_SHADER and FRAGMENT_SHADER
219 * Shaders are very basic and all they do is transforming vertices and sampling
224 mShaderCube = Shader::New( VERTEX_SHADER_CUBE, FRAGMENT_SHADER_CUBE );
225 mShaderSkybox = Shader::New( VERTEX_SHADER_SKYBOX, FRAGMENT_SHADER_SKYBOX );
229 * @brief CreateCubeGeometry
230 * This function creates a cube geometry including texture coordinates.
231 * Also it demonstrates using the indexed draw feature by setting an index array.
233 void CreateCubeGeometry()
241 Vertex vertices[] = {
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( 0.0, 0.0 ) },
256 { Vector3( -1.0f, 1.0f,-1.0f ), Vector2( 0.0, 1.0 ) },
257 { Vector3( 1.0f, 1.0f,-1.0f ), Vector2( 1.0, 1.0 ) },
258 { Vector3( -1.0f, 1.0f, 1.0f ), Vector2( 0.0, 0.0 ) },
259 { Vector3( 1.0f, 1.0f, 1.0f ), Vector2( 0.0, 1.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 ) },
272 { Vector3( -1.0f,-1.0f,-1.0f ), Vector2( 1.0, 1.0 ) },
273 { Vector3( -1.0f,-1.0f, 1.0f ), Vector2( 1.0, 0.0 ) },
274 { Vector3( -1.0f, 1.0f, 1.0f ), Vector2( 0.0, 0.0 ) },
275 { Vector3( 1.0f, 1.0f,-1.0f ), Vector2( 1.0, 1.0 ) },
276 { Vector3( -1.0f, 1.0f,-1.0f ), Vector2( 1.0, 0.0 ) },
277 { Vector3( -1.0f, 1.0f, 1.0f ), Vector2( 0.0, 0.0 ) },
280 PropertyBuffer vertexBuffer = PropertyBuffer::New( Property::Map()
281 .Add( "aPosition", Property::VECTOR3 )
282 .Add( "aTexCoord", Property::VECTOR2 ) );
283 vertexBuffer.SetData( vertices, sizeof(vertices) / sizeof(Vertex) );
286 const unsigned short INDEX_CUBE[] = {
301 mGeometry = Geometry::New();
302 mGeometry.AddVertexBuffer( vertexBuffer );
303 mGeometry.SetIndexBuffer( INDEX_CUBE,
304 sizeof(INDEX_CUBE)/sizeof(INDEX_CUBE[0]) );
305 mGeometry.SetType( Geometry::TRIANGLES );
309 * @brief CreateCubeGeometry
310 * This function creates a cube geometry including texture coordinates.
311 * Also it demonstrates using the indexed draw feature by setting an index array.
313 void CreateSkyboxGeometry()
320 Vertex skyboxVertices[] = {
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 ) },
326 { Vector3( 1.0f, 1.0f, -1.0f ) },
327 { 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 ) },
334 { Vector3( -1.0f, 1.0f, 1.0f ) },
335 { 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 ) },
342 { Vector3( 1.0f, 1.0f, -1.0f ) },
343 { 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 ) },
350 { Vector3( 1.0f, -1.0f, 1.0f ) },
351 { Vector3( -1.0f, -1.0f, 1.0f ) },
354 { Vector3( -1.0f, 1.0f, -1.0f ) },
355 { Vector3( 1.0f, 1.0f, -1.0f ) },
356 { 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 ) },
362 { Vector3( -1.0f, -1.0f, -1.0f ) },
363 { Vector3( -1.0f, -1.0f, 1.0f ) },
364 { Vector3( 1.0f, -1.0f, -1.0f ) },
365 { Vector3( 1.0f, -1.0f, -1.0f ) },
366 { Vector3( -1.0f, -1.0f, 1.0f ) },
367 { Vector3( 1.0f, -1.0f, 1.0f ) }
370 PropertyBuffer vertexBuffer = PropertyBuffer::New( Property::Map()
371 .Add( "aPosition", Property::VECTOR3 ) );
372 vertexBuffer.SetData( skyboxVertices, sizeof(skyboxVertices) / sizeof(Vertex) );
374 mSkyboxGeometry = Geometry::New();
375 mSkyboxGeometry.AddVertexBuffer( vertexBuffer );
376 mSkyboxGeometry.SetType( Geometry::TRIANGLES );
380 * Display a cube at the world origin
384 // Load image from file
385 PixelData pixels = SyncImageLoader::Load( TEXTURE_URL );
387 Texture texture = Texture::New( TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight() );
388 texture.Upload( pixels, 0, 0, 0, 0, pixels.GetWidth(), pixels.GetHeight() );
391 mTextureSet = TextureSet::New();
392 mTextureSet.SetTexture( 0, texture );
394 mRenderer = Renderer::New( mGeometry, mShaderCube );
395 mRenderer.SetTextures( mTextureSet );
396 mRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, 1.0f );
398 // A further optimization would be to enable debug testing instead
399 mRenderer.SetProperty( Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK );
401 // Enables the write on the depth buffer.
402 mRenderer.SetProperty( Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON );
404 mActor = Actor::New();
405 mActor.SetName( "Cube" );
406 mActor.SetAnchorPoint( AnchorPoint::CENTER );
407 mActor.SetParentOrigin( ParentOrigin::CENTER );
408 mActor.AddRenderer( mRenderer );
410 mActor.SetSize( 10.f, 10.f, 10.f );
412 Stage stage = Stage::GetCurrent();
417 * Display a skybox surrounding the camera
421 // Load skybox faces from file
422 Texture texture = Texture::New( TextureType::TEXTURE_CUBE, Pixel::RGBA8888, SKYBOX_FACE_WIDTH, SKYBOX_FACE_HEIGHT );
423 for (unsigned int i = 0; i < SKYBOX_FACE_COUNT; i++)
425 PixelData pixels = SyncImageLoader::Load( SKYBOX_FACES[i] );
426 texture.Upload( pixels, CubeMapLayer::POSITIVE_X + i, 0, 0, 0, SKYBOX_FACE_WIDTH, SKYBOX_FACE_HEIGHT );
430 mSkyboxTextures = TextureSet::New();
431 mSkyboxTextures.SetTexture( 0, texture );
433 mSkyboxRenderer = Renderer::New( mSkyboxGeometry, mShaderSkybox );
434 mSkyboxRenderer.SetTextures( mSkyboxTextures );
435 mSkyboxRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, 2.0f );
437 // Enables the depth test.
438 mSkyboxRenderer.SetProperty( Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON );
440 // The fragment shader will run only is those pixels that have the max depth value.
441 mSkyboxRenderer.SetProperty( Renderer::Property::DEPTH_FUNCTION, DepthFunction::LESS_EQUAL );
443 Stage stage = Stage::GetCurrent();
445 mSkyboxActor = Actor::New();
446 mSkyboxActor.SetName( "SkyBox" );
447 mSkyboxActor.SetAnchorPoint( AnchorPoint::CENTER );
448 mSkyboxActor.SetParentOrigin( ParentOrigin::CENTER );
449 mSkyboxActor.SetPosition( CAMERA_DEFAULT_POSITION );
450 mSkyboxActor.AddRenderer( mSkyboxRenderer );
451 stage.Add( mSkyboxActor );
459 mAnimation = Animation::New( 5.0f );
460 mAnimation.SetLooping( true );
461 mAnimation.AnimateBy( Property( mActor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree( 360 )), Vector3::ZAXIS ) );
462 mAnimation.AnimateBy( Property( mActor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree( 360 )), Vector3::YAXIS ) );
463 mAnimation.AnimateBy( Property( mActor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree( 360 )), Vector3::XAXIS ) );
468 Application& mApplication;
473 Shader mShaderSkybox;
476 TextureSet mTextureSet;
479 Animation mAnimation;
481 Geometry mSkyboxGeometry;
482 TextureSet mSkyboxTextures;
483 Renderer mSkyboxRenderer;
487 void RunTest( Application& application )
489 TexturedCubeController test( application );
491 application.MainLoop();
494 // Entry point for Linux & Tizen applications
496 int DALI_EXPORT_API main( int argc, char **argv )
498 Application application = Application::New( &argc, &argv );
500 RunTest( application );