Revert "Updates following rename of PropertyBuffer"
[platform/core/uifw/dali-demo.git] / examples / rendering-skybox / rendering-skybox.cpp
1 /*
2  * Copyright (c) 2020 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     // Get a handle to the window
151     Window window = application.GetWindow();
152     window.SetBackgroundColor( Color::WHITE );
153
154     // Step 1. Setup camera
155     SetupCamera();
156
157     // Step 2. Create shaders
158     CreateShaders();
159
160     // Step 3. Create geometry
161     CreateCubeGeometry();
162     CreateSkyboxGeometry();
163
164     // Step 4. Display first a cube at the world origin.
165     //         The write on the depth buffer is enabled.
166     DisplayCube();
167
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.
172     DisplaySkybox();
173
174     // Step 6. Play animation to rotate the cube
175     PlayAnimation();
176
177     // Respond to key events
178     window.KeyEventSignal().Connect( this, &TexturedCubeController::OnKeyEvent );
179   }
180
181   /**
182    * @brief Called when any key event is received
183    *
184    * Will use this to quit the application if Back or the Escape key is received
185    * @param[in] event The key event information
186    */
187   void OnKeyEvent( const KeyEvent& event )
188   {
189     if( event.state == KeyEvent::Down )
190     {
191       if ( IsKey( event, Dali::DALI_KEY_ESCAPE ) || IsKey( event, Dali::DALI_KEY_BACK ) )
192       {
193         mApplication.Quit();
194       }
195     }
196   }
197
198   /**
199    * @brief Setup a perspective camera pointing in the negative Z direction
200    */
201   void SetupCamera()
202   {
203     Window window = mApplication.GetWindow();
204
205     RenderTask renderTask = window.GetRenderTaskList().GetTask( 0 );
206     renderTask.SetCullMode( false ); // avoid frustum culling affecting the skybox
207
208     mCamera.Initialise( window, CAMERA_DEFAULT_POSITION, CAMERA_DEFAULT_FOV, CAMERA_DEFAULT_NEAR, CAMERA_DEFAULT_FAR );
209   }
210
211   /**
212    * Creates a shader using inlined variable VERTEX_SHADER and FRAGMENT_SHADER
213    *
214    * Shaders are very basic and all they do is transforming vertices and sampling
215    * a texture.
216    */
217   void CreateShaders()
218   {
219     mShaderCube   = Shader::New( VERTEX_SHADER_CUBE, FRAGMENT_SHADER_CUBE );
220     mShaderSkybox = Shader::New( VERTEX_SHADER_SKYBOX, FRAGMENT_SHADER_SKYBOX );
221   }
222
223   /**
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.
227    */
228   void CreateCubeGeometry()
229   {
230     struct Vertex
231     {
232       Vector3 aPosition;
233       Vector2 aTexCoord;
234     };
235
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 ) },
273     };
274
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) );
279
280     // create indices
281     const unsigned short INDEX_CUBE[] = {
282       2, 1, 0,
283       5, 4, 3,
284       8, 7, 6,
285       11, 10, 9,
286       14, 13, 12,
287       17, 16, 15,
288       20, 19, 18,
289       23, 22, 21,
290       26, 25, 24,
291       29, 28, 27,
292       32, 31, 30,
293       35, 34, 33
294     };
295
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 );
301   }
302
303   /**
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.
307    */
308   void CreateSkyboxGeometry()
309   {
310     struct Vertex
311     {
312       Vector3 aPosition;
313     };
314
315     Vertex skyboxVertices[] = {
316         // back
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 ) },
323
324         // left
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 ) },
331
332         // right
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 ) },
339
340         // front
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 ) },
347
348         // botton
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 ) },
355
356         // top
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 ) }
363     };
364
365     PropertyBuffer vertexBuffer = PropertyBuffer::New( Property::Map()
366                                                        .Add( "aPosition", Property::VECTOR3 ) );
367     vertexBuffer.SetData( skyboxVertices, sizeof(skyboxVertices) / sizeof(Vertex) );
368
369     mSkyboxGeometry = Geometry::New();
370     mSkyboxGeometry.AddVertexBuffer( vertexBuffer );
371     mSkyboxGeometry.SetType( Geometry::TRIANGLES );
372   }
373
374   /**
375    * Display a cube at the world origin
376    */
377   void DisplayCube()
378   {
379     // Load image from file
380     PixelData pixels = SyncImageLoader::Load( TEXTURE_URL );
381
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() );
384
385     // create TextureSet
386     mTextureSet = TextureSet::New();
387     mTextureSet.SetTexture( 0, texture );
388
389     mRenderer = Renderer::New( mGeometry, mShaderCube );
390     mRenderer.SetTextures( mTextureSet );
391     mRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, 1.0f );
392
393     // A further optimization would be to enable debug testing instead
394     mRenderer.SetProperty( Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK );
395
396     // Enables the write on the depth buffer.
397     mRenderer.SetProperty( Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON );
398
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 );
404
405     mActor.SetProperty( Actor::Property::SIZE, Vector3( 10.f, 10.f, 10.f ) );
406
407     Window window = mApplication.GetWindow();
408     window.Add( mActor );
409   }
410
411   /**
412    * Display a skybox surrounding the camera
413    */
414   void DisplaySkybox()
415   {
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++)
419     {
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 );
422     }
423
424     // create TextureSet
425     mSkyboxTextures = TextureSet::New();
426     mSkyboxTextures.SetTexture( 0, texture );
427
428     mSkyboxRenderer = Renderer::New( mSkyboxGeometry, mShaderSkybox );
429     mSkyboxRenderer.SetTextures( mSkyboxTextures );
430     mSkyboxRenderer.SetProperty( Renderer::Property::DEPTH_INDEX, 2.0f );
431
432     // Enables the depth test.
433     mSkyboxRenderer.SetProperty( Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON );
434
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 );
437
438     Window window = mApplication.GetWindow();
439
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     window.Add( mSkyboxActor );
447   }
448
449   /**
450    * Plays animation
451    */
452   void PlayAnimation()
453   {
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 ) );
459     mAnimation.Play();
460   }
461
462 private:
463   Application&  mApplication;
464
465   LookCamera mCamera;
466
467   Shader mShaderCube;
468   Shader mShaderSkybox;
469
470   Geometry mGeometry;
471   TextureSet mTextureSet;
472   Renderer mRenderer;
473   Actor mActor;
474   Animation mAnimation;
475
476   Geometry mSkyboxGeometry;
477   TextureSet mSkyboxTextures;
478   Renderer mSkyboxRenderer;
479   Actor mSkyboxActor;
480 };
481
482 int DALI_EXPORT_API main( int argc, char **argv )
483 {
484   Application application = Application::New( &argc, &argv );
485   TexturedCubeController test( application );
486   application.MainLoop();
487   return 0;
488 }