Further Setter/Getter public API removal from Dali::Actor
[platform/core/uifw/dali-demo.git] / examples / rendering-skybox / rendering-skybox.cpp
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 #include <dali/dali.h>
19 #include <dali-toolkit/dali-toolkit.h>
20
21 #include "look-camera.h"
22
23 using namespace Dali;
24 using namespace Toolkit;
25
26 namespace
27 {
28
29 /*
30  * Vertex shader for a textured cube
31  */
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
37 \n
38 varying mediump vec2 vTexCoord;\n
39 void main()\n
40 {\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
45 }\n
46 );
47
48 /*
49  * Fragment shader for a textured cube
50  */
51 const char* FRAGMENT_SHADER_CUBE = DALI_COMPOSE_SHADER(
52 uniform sampler2D uTexture;\n
53 \n
54 varying mediump vec2 vTexCoord;\n
55 void main()\n
56 {\n
57   mediump vec4 texColor = texture2D( uTexture, vTexCoord );\n
58   gl_FragColor = texColor;\n
59 }\n
60 );
61
62 /*
63  * Vertex shader for a skybox
64  */
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
68 \n
69 varying mediump vec3 vTexCoord;\n
70 void main()\n
71 {\n
72   vTexCoord.x =  aPosition.x;\n
73   vTexCoord.y = -aPosition.y;\n // convert to GL coords
74   vTexCoord.z =  aPosition.z;\n
75
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.
81 }\n
82 );
83
84 /*
85  * Fragment shader for a skybox
86  */
87 const char* FRAGMENT_SHADER_SKYBOX = DALI_COMPOSE_SHADER(
88 uniform samplerCube uSkyBoxTexture;\n
89 \n
90 varying mediump vec3 vTexCoord;\n
91 void main()\n
92 {\n
93   mediump vec4 texColor = textureCube( uSkyBoxTexture, vTexCoord );\n
94   gl_FragColor = texColor;\n
95 }\n
96 );
97
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 );
102
103 const char* TEXTURE_URL = DEMO_IMAGE_DIR "wood.png";
104
105 const unsigned int SKYBOX_FACE_COUNT = 6;
106 const unsigned int SKYBOX_FACE_WIDTH  = 2048;
107 const unsigned int SKYBOX_FACE_HEIGHT = 2048;
108
109 /*
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/
114  */
115 const char* SKYBOX_FACES[ SKYBOX_FACE_COUNT ] =
116 {
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"
123 };
124
125 }
126
127 // This example shows how to create a skybox
128 //
129 // Recommended screen size on desktop: 1280x720
130 //
131 class TexturedCubeController : public ConnectionTracker
132 {
133 public:
134
135   TexturedCubeController( Application& application )
136   : mApplication( application )
137   {
138     // Connect to the Application's Init signal
139     mApplication.InitSignal().Connect( this, &TexturedCubeController::Create );
140   }
141
142   ~TexturedCubeController()
143   {
144     // Nothing to do here;
145   }
146
147   // The Init signal is received once (only) during the Application lifetime
148   void Create( Application& application )
149   {
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 );
154
155     // Get a handle to the stage
156     Stage stage = Stage::GetCurrent();
157     stage.SetBackgroundColor( Color::WHITE );
158
159     // Step 1. Setup camera
160     SetupCamera();
161
162     // Step 2. Create shaders
163     CreateShaders();
164
165     // Step 3. Create geometry
166     CreateCubeGeometry();
167     CreateSkyboxGeometry();
168
169     // Step 4. Display first a cube at the world origin.
170     //         The write on the depth buffer is enabled.
171     DisplayCube();
172
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.
177     DisplaySkybox();
178
179     // Step 6. Play animation to rotate the cube
180     PlayAnimation();
181
182     // Respond to key events
183     stage.KeyEventSignal().Connect( this, &TexturedCubeController::OnKeyEvent );
184   }
185
186   /**
187    * @brief Called when any key event is received
188    *
189    * Will use this to quit the application if Back or the Escape key is received
190    * @param[in] event The key event information
191    */
192   void OnKeyEvent( const KeyEvent& event )
193   {
194     if( event.state == KeyEvent::Down )
195     {
196       if ( IsKey( event, Dali::DALI_KEY_ESCAPE ) || IsKey( event, Dali::DALI_KEY_BACK ) )
197       {
198         mApplication.Quit();
199       }
200     }
201   }
202
203   /**
204    * @brief Setup a perspective camera pointing in the negative Z direction
205    */
206   void SetupCamera()
207   {
208     Stage stage = Stage::GetCurrent();
209
210     RenderTask renderTask = stage.GetRenderTaskList().GetTask( 0 );
211     renderTask.SetCullMode( false ); // avoid frustum culling affecting the skybox
212
213     mCamera.Initialise( CAMERA_DEFAULT_POSITION, CAMERA_DEFAULT_FOV, CAMERA_DEFAULT_NEAR, CAMERA_DEFAULT_FAR );
214   }
215
216   /**
217    * Creates a shader using inlined variable VERTEX_SHADER and FRAGMENT_SHADER
218    *
219    * Shaders are very basic and all they do is transforming vertices and sampling
220    * a texture.
221    */
222   void CreateShaders()
223   {
224     mShaderCube   = Shader::New( VERTEX_SHADER_CUBE, FRAGMENT_SHADER_CUBE );
225     mShaderSkybox = Shader::New( VERTEX_SHADER_SKYBOX, FRAGMENT_SHADER_SKYBOX );
226   }
227
228   /**
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.
232    */
233   void CreateCubeGeometry()
234   {
235     struct Vertex
236     {
237       Vector3 aPosition;
238       Vector2 aTexCoord;
239     };
240
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 ) },
278     };
279
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) );
284
285     // create indices
286     const unsigned short INDEX_CUBE[] = {
287       2, 1, 0,
288       5, 4, 3,
289       8, 7, 6,
290       11, 10, 9,
291       14, 13, 12,
292       17, 16, 15,
293       20, 19, 18,
294       23, 22, 21,
295       26, 25, 24,
296       29, 28, 27,
297       32, 31, 30,
298       35, 34, 33
299     };
300
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 );
306   }
307
308   /**
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.
312    */
313   void CreateSkyboxGeometry()
314   {
315     struct Vertex
316     {
317       Vector3 aPosition;
318     };
319
320     Vertex skyboxVertices[] = {
321         // back
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 ) },
328
329         // left
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 ) },
336
337         // right
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 ) },
344
345         // front
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 ) },
352
353         // botton
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 ) },
360
361         // top
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 ) }
368     };
369
370     PropertyBuffer vertexBuffer = PropertyBuffer::New( Property::Map()
371                                                        .Add( "aPosition", Property::VECTOR3 ) );
372     vertexBuffer.SetData( skyboxVertices, sizeof(skyboxVertices) / sizeof(Vertex) );
373
374     mSkyboxGeometry = Geometry::New();
375     mSkyboxGeometry.AddVertexBuffer( vertexBuffer );
376     mSkyboxGeometry.SetType( Geometry::TRIANGLES );
377   }
378
379   /**
380    * Display a cube at the world origin
381    */
382   void DisplayCube()
383   {
384     // Load image from file
385     PixelData pixels = SyncImageLoader::Load( TEXTURE_URL );
386
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() );
389
390     // create TextureSet
391     mTextureSet = TextureSet::New();
392     mTextureSet.SetTexture( 0, texture );
393
394     mRenderer = Renderer::New( mGeometry, mShaderCube );
395     mRenderer.SetTextures( mTextureSet );
396     mRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, 1.0f );
397
398     // A further optimization would be to enable debug testing instead
399     mRenderer.SetProperty( Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK );
400
401     // Enables the write on the depth buffer.
402     mRenderer.SetProperty( Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON );
403
404     mActor = Actor::New();
405     mActor.SetProperty( Dali::Actor::Property::NAME, "Cube" );
406     mActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
407     mActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
408     mActor.AddRenderer( mRenderer );
409
410     mActor.SetProperty( Actor::Property::SIZE, Vector3( 10.f, 10.f, 10.f ) );
411
412     Stage stage = Stage::GetCurrent();
413     stage.Add( mActor );
414   }
415
416   /**
417    * Display a skybox surrounding the camera
418    */
419   void DisplaySkybox()
420   {
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++)
424     {
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 );
427     }
428
429     // create TextureSet
430     mSkyboxTextures = TextureSet::New();
431     mSkyboxTextures.SetTexture( 0, texture );
432
433     mSkyboxRenderer = Renderer::New( mSkyboxGeometry, mShaderSkybox );
434     mSkyboxRenderer.SetTextures( mSkyboxTextures );
435     mSkyboxRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, 2.0f );
436
437     // Enables the depth test.
438     mSkyboxRenderer.SetProperty( Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON );
439
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 );
442
443     Stage stage = Stage::GetCurrent();
444
445     mSkyboxActor = Actor::New();
446     mSkyboxActor.SetProperty( Dali::Actor::Property::NAME, "SkyBox" );
447     mSkyboxActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
448     mSkyboxActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
449     mSkyboxActor.SetProperty( Actor::Property::POSITION, CAMERA_DEFAULT_POSITION );
450     mSkyboxActor.AddRenderer( mSkyboxRenderer );
451     stage.Add( mSkyboxActor );
452   }
453
454   /**
455    * Plays animation
456    */
457   void PlayAnimation()
458   {
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 ) );
464     mAnimation.Play();
465   }
466
467 private:
468   Application&  mApplication;
469
470   LookCamera mCamera;
471
472   Shader mShaderCube;
473   Shader mShaderSkybox;
474
475   Geometry mGeometry;
476   TextureSet mTextureSet;
477   Renderer mRenderer;
478   Actor mActor;
479   Animation mAnimation;
480
481   Geometry mSkyboxGeometry;
482   TextureSet mSkyboxTextures;
483   Renderer mSkyboxRenderer;
484   Actor mSkyboxActor;
485 };
486
487 int DALI_EXPORT_API main( int argc, char **argv )
488 {
489   Application application = Application::New( &argc, &argv );
490   TexturedCubeController test( application );
491   application.MainLoop();
492   return 0;
493 }