00df5482b7b476844dc27e28528d9e4429a847ab
[platform/core/uifw/dali-demo.git] / examples / renderer-stencil / renderer-stencil-example.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 // EXTERNAL INCLUDES
19 #include <dali-toolkit/dali-toolkit.h>
20
21 // INTERNAL INCLUDES
22 #include "renderer-stencil-shaders.h"
23 #include "shared/view.h"
24 #include "shared/utility.h"
25
26 using namespace Dali;
27
28 namespace
29 {
30
31 // Constants:
32
33 // Application constants:
34 const char * const APPLICATION_TITLE( "Renderer Stencil API Demo" );
35 const char * const BACKGROUND_IMAGE( DEMO_IMAGE_DIR "background-gradient.jpg" );
36
37 // Texture filenames:
38 const char * const CUBE_TEXTURE( DEMO_IMAGE_DIR "people-medium-1.jpg" );
39 const char * const FLOOR_TEXTURE( DEMO_IMAGE_DIR "wood.png" );
40
41 // Scale dimensions: These values are relative to the window size. EG. width = 0.32f * windowSize.
42 const float   CUBE_WIDTH_SCALE( 0.32f );                   ///< The width (and height + depth) of the main and reflection cubes.
43 const Vector2 FLOOR_DIMENSION_SCALE( 0.67f, 0.017f );      ///< The width and height of the floor object.
44
45 // Configurable animation characteristics:
46 const float ANIMATION_ROTATION_DURATION( 10.0f );          ///< Time in seconds to rotate the scene 360 degrees around Y.
47 const float ANIMATION_BOUNCE_TOTAL_TIME( 1.6f );           ///< Time in seconds to perform 1 full bounce animation cycle.
48 const float ANIMATION_BOUNCE_DEFORMATION_TIME( 0.4f );     ///< Time in seconds that the cube deformation animation will occur for (on contact with the floor).
49 const float ANIMATION_BOUNCE_DEFORMATION_PERCENT( 20.0f ); ///< Percentage (of the cube's size) to deform the cube by (on contact with floor).
50 const float ANIMATION_BOUNCE_HEIGHT_PERCENT( 40.0f );      ///< Percentage (of the cube's size) to bounce up in to the air by.
51
52 // Base colors for the objects:
53 const Vector4 TEXT_COLOR( 1.0f, 1.0f, 1.0f, 1.0f );        ///< White.
54 const Vector4 CUBE_COLOR( 1.0f, 1.0f, 1.0f, 1.0f );        ///< White.
55 const Vector4 FLOOR_COLOR( 1.0f, 1.0f, 1.0f, 1.0f );       ///< White.
56 const Vector4 REFLECTION_COLOR( 0.6f, 0.6f, 0.6f, 0.6f );  ///< Note that alpha is not 1.0f, to make the blend more photo-realistic.
57
58 // We need to control the draw order as we are controlling both the stencil and depth buffer per renderer.
59 const int DEPTH_INDEX_GRANULARITY( 10000 );                ///< This value is the gap in depth-index in-between each renderer.
60
61 } // Anonymous namespace
62
63 /**
64  * @brief This example shows how to manipulate stencil and depth buffer properties within the Renderer API.
65  */
66 class RendererStencilExample : public ConnectionTracker
67 {
68 public:
69
70   /**
71    * @brief Constructor.
72    * @param[in] application The DALi application object
73    */
74   RendererStencilExample( Application& application )
75   : mApplication( application )
76   {
77     // Connect to the Application's Init signal.
78     mApplication.InitSignal().Connect( this, &RendererStencilExample::Create );
79   }
80
81   /**
82    * @brief Destructor (non-virtual).
83    */
84   ~RendererStencilExample()
85   {
86   }
87
88 private:
89
90   /**
91    * @brief Enum to facilitate more readable use of the cube array.
92    */
93   enum CubeType
94   {
95     MAIN_CUBE,      ///< The main cube that bounces above the floor object.
96     REFLECTION_CUBE ///< The reflected cube object.
97   };
98
99   /**
100    * @brief Struct to store the position, normal and texture coordinates of a single vertex.
101    */
102   struct TexturedVertex
103   {
104     Vector3 position;
105     Vector3 normal;
106     Vector2 textureCoord;
107   };
108
109   /**
110    * @brief This is the main scene setup method for this demo.
111    * This is called via the Init signal which is received once (only) during the Application lifetime.
112    * @param[in] application The DALi application object
113    */
114   void Create( Application& application )
115   {
116     Window window = application.GetWindow();
117
118     // Use a gradient visual to render the background gradient.
119     Toolkit::Control background = Dali::Toolkit::Control::New();
120     background.SetProperty( Actor::Property::ANCHOR_POINT, Dali::AnchorPoint::CENTER );
121     background.SetProperty( Actor::Property::PARENT_ORIGIN, Dali::ParentOrigin::CENTER );
122     background.SetResizePolicy( Dali::ResizePolicy::FILL_TO_PARENT, Dali::Dimension::ALL_DIMENSIONS );
123
124     // Set up the background gradient.
125     Property::Array stopOffsets;
126     stopOffsets.PushBack( 0.0f );
127     stopOffsets.PushBack( 1.0f );
128     Property::Array stopColors;
129     stopColors.PushBack( Vector4( 0.17f, 0.24f, 0.35f, 1.0f ) ); // Dark, medium saturated blue  ( top   of screen)
130     stopColors.PushBack( Vector4( 0.45f, 0.70f, 0.80f, 1.0f ) ); // Medium bright, pastel blue   (bottom of screen)
131     const float percentageWindowHeight = window.GetSize().GetHeight() * 0.7f;
132
133     background.SetProperty( Toolkit::Control::Property::BACKGROUND, Dali::Property::Map()
134       .Add( Toolkit::Visual::Property::TYPE, Dali::Toolkit::Visual::GRADIENT )
135       .Add( Toolkit::GradientVisual::Property::STOP_OFFSET, stopOffsets )
136       .Add( Toolkit::GradientVisual::Property::STOP_COLOR, stopColors )
137       .Add( Toolkit::GradientVisual::Property::START_POSITION, Vector2( 0.0f, -percentageWindowHeight ) )
138       .Add( Toolkit::GradientVisual::Property::END_POSITION, Vector2( 0.0f, percentageWindowHeight ) )
139       .Add( Toolkit::GradientVisual::Property::UNITS, Toolkit::GradientVisual::Units::USER_SPACE ) );
140
141     window.Add( background );
142
143     // Create a TextLabel for the application title.
144     Toolkit::TextLabel label = Toolkit::TextLabel::New( APPLICATION_TITLE );
145     label.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
146     // Set the parent origin to a small percentage below the top (so the demo will scale for different resolutions).
147     label.SetProperty( Actor::Property::PARENT_ORIGIN, Vector3( 0.5f, 0.03f, 0.5f ) );
148     label.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
149     label.SetProperty( Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
150     label.SetProperty( Toolkit::TextLabel::Property::TEXT_COLOR, TEXT_COLOR );
151     window.Add( label );
152
153     // Layer to hold the 3D scene.
154     Layer layer = Layer::New();
155     layer.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
156     // Set the parent origin to a small percentage below the center (so the demo will scale for different resolutions).
157     layer.SetProperty( Actor::Property::PARENT_ORIGIN, Vector3( 0.5f, 0.58f, 0.5f ) );
158     layer.SetProperty( Layer::Property::BEHAVIOR, Layer::LAYER_UI );
159     layer.SetProperty( Layer::Property::DEPTH_TEST, true );
160     window.Add( layer );
161
162     // Main cube:
163     // Make the demo scalable with different resolutions by basing
164     // the cube size on a percentage of the window size.
165     Vector2 windowSize = window.GetSize();
166     float scaleSize( std::min( windowSize.width, windowSize.height ) );
167     float cubeWidth( scaleSize * CUBE_WIDTH_SCALE );
168     Vector3 cubeSize( cubeWidth, cubeWidth, cubeWidth );
169     // Create the geometry for the cube, and the texture.
170     Geometry cubeGeometry = CreateCubeVertices( Vector3::ONE, false );
171     TextureSet cubeTextureSet = CreateTextureSet( CUBE_TEXTURE );
172     // Create the cube object and add it.
173     // Note: The cube is anchored around its base for animation purposes, so the position can be zero.
174     mCubes[ MAIN_CUBE ] = CreateMainCubeObject( cubeGeometry, cubeSize, cubeTextureSet );
175     layer.Add( mCubes[ MAIN_CUBE ] );
176
177     // Floor:
178     float floorWidth( scaleSize * FLOOR_DIMENSION_SCALE.x );
179     Vector3 floorSize( floorWidth, scaleSize * FLOOR_DIMENSION_SCALE.y, floorWidth );
180     // Create the floor object using the cube geometry with a new size, and add it.
181     Actor floorObject( CreateFloorObject( cubeGeometry, floorSize ) );
182     layer.Add( floorObject );
183
184     // Stencil:
185     Vector3 planeSize( floorWidth, floorWidth, 0.0f );
186     // Create the stencil plane object, and add it.
187     Actor stencilPlaneObject( CreateStencilPlaneObject( planeSize ) );
188     layer.Add( stencilPlaneObject );
189
190     // Reflection cube:
191     // Create the reflection cube object and add it.
192     // Note: The cube is anchored around its base for animation purposes, so the position can be zero.
193     mCubes[ REFLECTION_CUBE ] = CreateReflectionCubeObject( cubeSize, cubeTextureSet );
194     layer.Add( mCubes[ REFLECTION_CUBE ] );
195
196     // Rotate the layer so we can see some of the top of the cube for a more 3D effect.
197     layer.SetProperty( Actor::Property::ORIENTATION, Quaternion( Degree( -24.0f ), Degree( 0.0f ), Degree( 0.0f ) ) );
198
199     // Set up the rotation on the Y axis.
200     mRotationAnimation = Animation::New( ANIMATION_ROTATION_DURATION );
201     float fullRotation = 360.0f;
202     mRotationAnimation.AnimateBy( Property( mCubes[ MAIN_CUBE ], Actor::Property::ORIENTATION ),
203                                  Quaternion( Degree( 0.0f ), Degree( fullRotation ), Degree( 0.0f ) ) );
204     mRotationAnimation.AnimateBy( Property( floorObject, Actor::Property::ORIENTATION ),
205                                  Quaternion( Degree( 0.0f ), Degree( fullRotation ), Degree( 0.0f ) ) );
206     // Note the stencil is pre-rotated by 90 degrees on X, so we rotate relatively on its Z axis for an equivalent Y rotation.
207     mRotationAnimation.AnimateBy( Property( stencilPlaneObject, Actor::Property::ORIENTATION ),
208                                  Quaternion( Degree( 0.0f ), Degree( 0.0f ), Degree( fullRotation ) ) );
209     mRotationAnimation.AnimateBy( Property( mCubes[ REFLECTION_CUBE ], Actor::Property::ORIENTATION ),
210                                  Quaternion( Degree( 0.0f ), Degree( fullRotation ), Degree( 0.0f ) ) );
211     mRotationAnimation.SetLooping( true );
212
213     // Set up the cube bouncing animation.
214     float totalTime = ANIMATION_BOUNCE_TOTAL_TIME;
215     float deformationTime = ANIMATION_BOUNCE_DEFORMATION_TIME;
216     // Percentage based amounts allows the bounce and deformation to scale for different resolution screens.
217     float deformationAmount = ANIMATION_BOUNCE_DEFORMATION_PERCENT / 100.0f;
218     float heightChange = ( cubeSize.y * ANIMATION_BOUNCE_HEIGHT_PERCENT ) / 100.0f;
219
220     // Animation pre-calculations:
221     float halfTime = totalTime / 2.0f;
222     float halfDeformationTime = deformationTime / 2.0f;
223
224     // First position the cubes at the top of the animation cycle.
225     mCubes[ MAIN_CUBE ].SetProperty(       Actor::Property::POSITION_Y, -heightChange );
226     mCubes[ REFLECTION_CUBE ].SetProperty( Actor::Property::POSITION_Y,  heightChange );
227
228     mBounceAnimation = Animation::New( totalTime );
229
230     // The animations for the main and reflected cubes are almost identical, so we combine the code to do both.
231     for( int cube = 0; cube < 2; ++cube )
232     {
233       // If iterating on the reflection cube, adjust the heightChange variable so the below code can be reused.
234       if( cube == 1 )
235       {
236         heightChange = -heightChange;
237       }
238
239       // 1st TimePeriod: Start moving down with increasing speed, until it is time to distort the cube due to impact.
240       mBounceAnimation.AnimateBy( Property( mCubes[ cube ], Actor::Property::POSITION_Y ),  heightChange, AlphaFunction::EASE_IN_SQUARE, TimePeriod( 0.0f, halfTime - halfDeformationTime ) );
241
242       // 2nd TimePeriod: The cube is touching the floor, start deforming it - then un-deform it again.
243       mBounceAnimation.AnimateBy( Property( mCubes[ cube ], Actor::Property::SCALE_X ),  deformationAmount, AlphaFunction::BOUNCE, TimePeriod( halfTime - halfDeformationTime, deformationTime ) );
244       mBounceAnimation.AnimateBy( Property( mCubes[ cube ], Actor::Property::SCALE_Z ),  deformationAmount, AlphaFunction::BOUNCE, TimePeriod( halfTime - halfDeformationTime, deformationTime ) );
245       mBounceAnimation.AnimateBy( Property( mCubes[ cube ], Actor::Property::SCALE_Y ), -deformationAmount, AlphaFunction::BOUNCE, TimePeriod( halfTime - halfDeformationTime, deformationTime ) );
246
247       // 3rd TimePeriod: Start moving up with decreasing speed, until at the apex of the animation.
248       mBounceAnimation.AnimateBy( Property( mCubes[ cube ], Actor::Property::POSITION_Y ), -heightChange, AlphaFunction::EASE_OUT_SQUARE, TimePeriod( halfTime + halfDeformationTime, halfTime - halfDeformationTime ) );
249     }
250
251     mBounceAnimation.SetLooping( true );
252
253     // Start the animations.
254     mRotationAnimation.Play();
255     mBounceAnimation.Play();
256
257     // Respond to a click anywhere on the window
258     window.GetRootLayer().TouchSignal().Connect( this, &RendererStencilExample::OnTouch );
259     // Connect signals to allow Back and Escape to exit.
260     window.KeyEventSignal().Connect( this, &RendererStencilExample::OnKeyEvent );
261   }
262
263 private:
264
265   // Methods to setup each component of the 3D scene:
266
267   /**
268    * @brief Creates the Main cube object.
269    * This creates the renderer from existing geometry (as the cubes geometry is shared).
270    * The texture is set and all relevant renderer properties are set-up.
271    * @param[in] geometry Pre-calculated cube geometry
272    * @param[in] size The desired cube size
273    * @param[in] textureSet A pre-existing TextureSet with a texture set up, to be applied to the cube
274    * @return An actor set-up containing the main cube object
275    */
276   Actor CreateMainCubeObject( Geometry& geometry, Vector3 size, TextureSet& textureSet )
277   {
278     Toolkit::Control container = Toolkit::Control::New();
279     container.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_CENTER );
280     container.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_CENTER );
281     container.SetProperty( Actor::Property::SIZE, Vector2( size ) );
282     container.SetResizePolicy( ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS );
283
284     // Create a renderer from the geometry and add the texture.
285     Renderer renderer = CreateRenderer( geometry, size, true, CUBE_COLOR );
286     renderer.SetTextures( textureSet );
287
288     // Setup the renderer properties:
289     // We are writing to the color buffer & culling back faces (no stencil is used for the main cube).
290     renderer.SetProperty( Renderer::Property::RENDER_MODE, RenderMode::COLOR );
291     renderer.SetProperty( Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK );
292
293     // We do need to write to the depth buffer as other objects need to appear underneath this cube.
294     renderer.SetProperty( Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON );
295     // We do not need to test the depth buffer as we are culling the back faces.
296     renderer.SetProperty( Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::OFF );
297
298     // This object must be rendered 1st.
299     renderer.SetProperty( Renderer::Property::DEPTH_INDEX, 0 * DEPTH_INDEX_GRANULARITY );
300
301     container.AddRenderer( renderer );
302     return container;
303   }
304
305   /**
306    * @brief Creates the Floor object.
307    * This creates the renderer from existing geometry (as the cube geometry can be re-used).
308    * The texture is created and set and all relevant renderer properties are set-up.
309    * @param[in] geometry Pre-calculated cube geometry
310    * @param[in] size The desired floor size
311    * @return An actor set-up containing the floor object
312    */
313   Actor CreateFloorObject( Geometry& geometry, Vector3 size )
314   {
315     Toolkit::Control container = Toolkit::Control::New();
316     container.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
317     container.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
318     container.SetProperty( Actor::Property::SIZE, Vector2( size ) );
319     container.SetResizePolicy( ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS );
320
321     // Create a renderer from the geometry and add the texture.
322     TextureSet planeTextureSet = CreateTextureSet( FLOOR_TEXTURE );
323     Renderer renderer = CreateRenderer( geometry, size, true, FLOOR_COLOR );
324     renderer.SetTextures( planeTextureSet );
325
326     // Setup the renderer properties:
327     // We are writing to the color buffer & culling back faces as we are NOT doing depth write (no stencil is used for the floor).
328     renderer.SetProperty( Renderer::Property::RENDER_MODE, RenderMode::COLOR );
329     renderer.SetProperty( Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK );
330
331     // We do not write to the depth buffer as its not needed.
332     renderer.SetProperty( Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::OFF );
333     // We do need to test the depth buffer as we need the floor to be underneath the cube.
334     renderer.SetProperty( Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON );
335
336     // This object must be rendered 2nd.
337     renderer.SetProperty( Renderer::Property::DEPTH_INDEX, 1 * DEPTH_INDEX_GRANULARITY );
338
339     container.AddRenderer( renderer );
340     return container;
341   }
342
343   /**
344    * @brief Creates the Stencil-Plane object.
345    * This is places on the floor object to allow the reflection to be drawn on to the floor.
346    * This creates the geometry and renderer.
347    * All relevant renderer properties are set-up.
348    * @param[in] size The desired plane size
349    * @return An actor set-up containing the stencil-plane object
350    */
351   Actor CreateStencilPlaneObject( Vector3 size )
352   {
353     Toolkit::Control container = Toolkit::Control::New();
354     container.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
355     container.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
356     container.SetProperty( Actor::Property::SIZE, Vector2( size ) );
357     container.SetResizePolicy( ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS );
358
359     // We rotate the plane as the geometry is created flat in X & Y. We want it to span X & Z axis.
360     container.SetProperty( Actor::Property::ORIENTATION, Quaternion( Degree( -90.0f ), Degree( 0.0f ), Degree( 0.0f ) ) );
361
362     // Create geometry for a flat plane.
363     Geometry planeGeometry = CreatePlaneVertices( Vector2::ONE );
364     // Create a renderer from the geometry.
365     Renderer renderer = CreateRenderer( planeGeometry, size, false, Vector4::ONE );
366
367     // Setup the renderer properties:
368     // The stencil plane is only for stencilling.
369     renderer.SetProperty( Renderer::Property::RENDER_MODE, RenderMode::STENCIL );
370
371     renderer.SetProperty( Renderer::Property::STENCIL_FUNCTION, StencilFunction::ALWAYS );
372     renderer.SetProperty( Renderer::Property::STENCIL_FUNCTION_REFERENCE, 1 );
373     renderer.SetProperty( Renderer::Property::STENCIL_FUNCTION_MASK, 0xFF );
374     renderer.SetProperty( Renderer::Property::STENCIL_OPERATION_ON_FAIL, StencilOperation::KEEP );
375     renderer.SetProperty( Renderer::Property::STENCIL_OPERATION_ON_Z_FAIL, StencilOperation::KEEP );
376     renderer.SetProperty( Renderer::Property::STENCIL_OPERATION_ON_Z_PASS, StencilOperation::REPLACE );
377     renderer.SetProperty( Renderer::Property::STENCIL_MASK, 0xFF );
378
379     // We don't want to write to the depth buffer, as this would block the reflection being drawn.
380     renderer.SetProperty( Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::OFF );
381     // We test the depth buffer as we want the stencil to only exist underneath the cube.
382     renderer.SetProperty( Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON );
383
384     // This object must be rendered 3rd.
385     renderer.SetProperty( Renderer::Property::DEPTH_INDEX, 2 * DEPTH_INDEX_GRANULARITY );
386
387     container.AddRenderer( renderer );
388     return container;
389   }
390
391   /**
392    * @brief Creates the Reflection cube object.
393    * This creates new geometry (as the texture UVs are different to the main cube).
394    * The renderer is then created.
395    * The texture is set and all relevant renderer properties are set-up.
396    * @param[in] size The desired cube size
397    * @param[in] textureSet A pre-existing TextureSet with a texture set up, to be applied to the cube
398    * @return An actor set-up containing the reflection cube object
399    */
400   Actor CreateReflectionCubeObject( Vector3 size, TextureSet& textureSet )
401   {
402     Toolkit::Control container = Toolkit::Control::New();
403     container.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
404     container.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
405     container.SetProperty( Actor::Property::SIZE, Vector2( size ) );
406     container.SetResizePolicy( ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS );
407
408     // Create the cube geometry of unity size.
409     // The "true" specifies we want the texture UVs flipped vertically as this is the reflection cube.
410     Geometry reflectedCubeGeometry = CreateCubeVertices( Vector3::ONE, true );
411     // Create a renderer from the geometry and add the texture.
412     Renderer renderer = CreateRenderer( reflectedCubeGeometry, size, true, REFLECTION_COLOR );
413     renderer.SetTextures( textureSet );
414
415     // Setup the renderer properties:
416     // Write to color buffer so reflection is visible.
417     // Also enable the stencil buffer, as we will be testing against it to only draw to areas within the stencil.
418     renderer.SetProperty( Renderer::Property::RENDER_MODE, RenderMode::COLOR_STENCIL );
419     // We cull to skip drawing the back faces.
420     renderer.SetProperty( Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK );
421
422     // We use blending to blend the reflection with the floor texture.
423     renderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
424     renderer.SetProperty( Renderer::Property::BLEND_EQUATION_RGB, BlendEquation::ADD );
425     renderer.SetProperty( Renderer::Property::BLEND_EQUATION_ALPHA, BlendEquation::ADD );
426     renderer.SetProperty( Renderer::Property::BLEND_FACTOR_DEST_RGB, BlendFactor::ONE );
427
428     // Enable stencil. Here we only draw to areas within the stencil.
429     renderer.SetProperty( Renderer::Property::STENCIL_FUNCTION, StencilFunction::EQUAL );
430     renderer.SetProperty( Renderer::Property::STENCIL_FUNCTION_REFERENCE, 1 );
431     renderer.SetProperty( Renderer::Property::STENCIL_FUNCTION_MASK, 0xff );
432     // Don't write to the stencil.
433     renderer.SetProperty( Renderer::Property::STENCIL_MASK, 0x00 );
434
435     // We don't need to write to the depth buffer, as we are culling.
436     renderer.SetProperty( Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::OFF );
437     // We need to test the depth buffer as we need the reflection to be underneath the cube.
438     renderer.SetProperty( Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON );
439
440     // This object must be rendered last.
441     renderer.SetProperty( Renderer::Property::DEPTH_INDEX, 3 * DEPTH_INDEX_GRANULARITY );
442
443     container.AddRenderer( renderer );
444     return container;
445   }
446
447   // Methods:
448
449   /**
450    * @brief Creates a geometry object from vertices and indices.
451    * @param[in] vertices The object vertices
452    * @param[in] indices The object indices
453    * @return A geometry object
454    */
455   Geometry CreateTexturedGeometry( Vector<TexturedVertex>& vertices, Vector<unsigned short>& indices )
456   {
457     // Vertices
458     Property::Map vertexFormat;
459     vertexFormat[POSITION] = Property::VECTOR3;
460     vertexFormat[NORMAL] =   Property::VECTOR3;
461     vertexFormat[TEXTURE] =  Property::VECTOR2;
462
463     VertexBuffer surfaceVertices = VertexBuffer::New( vertexFormat );
464     surfaceVertices.SetData( &vertices[0u], vertices.Size() );
465
466     Geometry geometry = Geometry::New();
467     geometry.AddVertexBuffer( surfaceVertices );
468
469     // Indices for triangle formulation
470     geometry.SetIndexBuffer( &indices[0u], indices.Size() );
471     return geometry;
472   }
473
474   /**
475    * @brief Creates a renderer from a geometry object.
476    * @param[in] geometry The geometry to use
477    * @param[in] dimensions The dimensions (will be passed in to the shader)
478    * @param[in] textured Set to true to use the texture versions of the shaders
479    * @param[in] color The base color for the renderer
480    * @return A renderer object
481    */
482   Renderer CreateRenderer( Geometry geometry, Vector3 dimensions, bool textured, Vector4 color )
483   {
484     Window window = mApplication.GetWindow();
485     Vector2 windowSize = window.GetSize();
486     Shader shader;
487
488     if( textured )
489     {
490       shader = Shader::New( VERTEX_SHADER_TEXTURED, FRAGMENT_SHADER_TEXTURED );
491     }
492     else
493     {
494       shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
495     }
496
497     // Here we modify the light position based on half the window size as a pre-calculation step.
498     // This avoids the work having to be done in the shader.
499     shader.RegisterProperty( LIGHT_POSITION_UNIFORM_NAME, Vector3( -windowSize.width / 2.0f, -windowSize.width / 2.0f, 1000.0f ) );
500     shader.RegisterProperty( COLOR_UNIFORM_NAME, color );
501     shader.RegisterProperty( OBJECT_DIMENSIONS_UNIFORM_NAME, dimensions );
502
503     return Renderer::New( geometry, shader );
504   }
505
506   /**
507    * @brief Helper method to create a TextureSet from an image URL.
508    * @param[in] url An image URL
509    * @return A TextureSet object
510    */
511   TextureSet CreateTextureSet( const char* url )
512   {
513     TextureSet textureSet = TextureSet::New();
514
515     if( textureSet )
516     {
517       Texture texture = DemoHelper::LoadTexture( url );
518       if( texture )
519       {
520         textureSet.SetTexture( 0u, texture );
521       }
522     }
523
524     return textureSet;
525   }
526
527   // Geometry Creation:
528
529   /**
530    * @brief Creates a geometry object for a flat plane.
531    * The plane is oriented in X & Y axis (Z is 0).
532    * @param[in] dimensions The desired plane dimensions
533    * @return A Geometry object
534    */
535   Geometry CreatePlaneVertices( Vector2 dimensions )
536   {
537     Vector<TexturedVertex> vertices;
538     Vector<unsigned short> indices;
539     vertices.Resize( 4u );
540     indices.Resize( 6u );
541
542     float scaledX = 0.5f * dimensions.x;
543     float scaledY = 0.5f * dimensions.y;
544
545     vertices[0].position     = Vector3( -scaledX, -scaledY, 0.0f );
546     vertices[0].textureCoord = Vector2( 0.0, 0.0f );
547     vertices[1].position     = Vector3(  scaledX, -scaledY, 0.0f );
548     vertices[1].textureCoord = Vector2( 1.0, 0.0f );
549     vertices[2].position     = Vector3(  scaledX,  scaledY, 0.0f );
550     vertices[2].textureCoord = Vector2( 1.0, 1.0f );
551     vertices[3].position     = Vector3( -scaledX,  scaledY, 0.0f );
552     vertices[3].textureCoord = Vector2( 0.0, 1.0f );
553
554     // All vertices have the same normal.
555     for( int i = 0; i < 4; ++i )
556     {
557       vertices[i].normal = Vector3( 0.0f, 0.0f, -1.0f );
558     }
559
560     indices[0] = 0;
561     indices[1] = 1;
562     indices[2] = 2;
563     indices[3] = 2;
564     indices[4] = 3;
565     indices[5] = 0;
566
567     // Use the helper method to create the geometry object.
568     return CreateTexturedGeometry( vertices, indices );
569   }
570
571   /**
572    * @brief Creates a geometry object for a cube (or cuboid).
573    * @param[in] dimensions The desired cube dimensions
574    * @param[in] reflectVerticalUVs Set to True to force the UVs to be vertically flipped
575    * @return A Geometry object
576    */
577   Geometry CreateCubeVertices( Vector3 dimensions, bool reflectVerticalUVs )
578   {
579     Vector<TexturedVertex> vertices;
580     Vector<unsigned short> indices;
581     int vertexIndex = 0u; // Tracks progress through vertices.
582     float scaledX = 0.5f * dimensions.x;
583     float scaledY = 0.5f * dimensions.y;
584     float scaledZ = 0.5f * dimensions.z;
585     float verticalTextureCoord = reflectVerticalUVs ? 0.0f : 1.0f;
586
587     vertices.Resize( 4u * 6u ); // 4 vertices x 6 faces
588
589     Vector<Vector3> positions;  // Stores vertex positions, which are shared between vertexes at the same position but with a different normal.
590     positions.Resize( 8u );
591     Vector<Vector3> normals;    // Stores normals, which are shared between vertexes of the same face.
592     normals.Resize( 6u );
593
594     positions[0] = Vector3( -scaledX,  scaledY, -scaledZ );
595     positions[1] = Vector3(  scaledX,  scaledY, -scaledZ );
596     positions[2] = Vector3(  scaledX,  scaledY,  scaledZ );
597     positions[3] = Vector3( -scaledX,  scaledY,  scaledZ );
598     positions[4] = Vector3( -scaledX, -scaledY, -scaledZ );
599     positions[5] = Vector3(  scaledX, -scaledY, -scaledZ );
600     positions[6] = Vector3(  scaledX, -scaledY,  scaledZ );
601     positions[7] = Vector3( -scaledX, -scaledY,  scaledZ );
602
603     normals[0] = Vector3(  0,  1,  0 );
604     normals[1] = Vector3(  0,  0, -1 );
605     normals[2] = Vector3(  1,  0,  0 );
606     normals[3] = Vector3(  0,  0,  1 );
607     normals[4] = Vector3( -1,  0,  0 );
608     normals[5] = Vector3(  0, -1,  0 );
609
610     // Top face, upward normals.
611     for( int i = 0; i < 4; ++i, ++vertexIndex )
612     {
613       vertices[vertexIndex].position = positions[i];
614       vertices[vertexIndex].normal = normals[0];
615       // The below logic forms the correct U/V pairs for a quad when "i" goes from 0 to 3.
616       vertices[vertexIndex].textureCoord = Vector2( ( i == 1 || i == 2 ) ? 1.0f : 0.0f, ( i == 2 || i == 3 ) ? 1.0f : 0.0f );
617     }
618
619     // Top face, outward normals.
620     for( int i = 0; i < 4; ++i, vertexIndex += 2 )
621     {
622       vertices[vertexIndex].position = positions[i];
623       vertices[vertexIndex].normal = normals[i + 1];
624
625       if( i == 3 )
626       {
627         // End, so loop around.
628         vertices[vertexIndex + 1].position = positions[0];
629       }
630       else
631       {
632         vertices[vertexIndex + 1].position = positions[i + 1];
633       }
634       vertices[vertexIndex + 1].normal = normals[i + 1];
635
636       vertices[vertexIndex].textureCoord = Vector2( 0.0f, verticalTextureCoord );
637       vertices[vertexIndex+1].textureCoord = Vector2( 1.0f, verticalTextureCoord );
638     }
639
640     // Flip the vertical texture coord for the UV values of the bottom points.
641     verticalTextureCoord = 1.0f - verticalTextureCoord;
642
643     // Bottom face, outward normals.
644     for( int i = 0; i < 4; ++i, vertexIndex += 2 )
645     {
646       vertices[vertexIndex].position = positions[i + 4];
647       vertices[vertexIndex].normal = normals[i + 1];
648
649       if( i == 3 )
650       {
651         // End, so loop around.
652         vertices[vertexIndex + 1].position = positions[4];
653       }
654       else
655       {
656         vertices[vertexIndex + 1].position = positions[i + 5];
657       }
658       vertices[vertexIndex + 1].normal = normals[i + 1];
659
660       vertices[vertexIndex].textureCoord = Vector2( 0.0f, verticalTextureCoord );
661       vertices[vertexIndex+1].textureCoord = Vector2( 1.0f, verticalTextureCoord );
662     }
663
664     // Bottom face, downward normals.
665     for( int i = 0; i < 4; ++i, ++vertexIndex )
666     {
667       // Reverse positions for bottom face to keep triangles clockwise (for culling).
668       vertices[vertexIndex].position = positions[ 7 - i ];
669       vertices[vertexIndex].normal = normals[5];
670       // The below logic forms the correct U/V pairs for a quad when "i" goes from 0 to 3.
671       vertices[vertexIndex].textureCoord = Vector2( ( i == 1 || i == 2 ) ? 1.0f : 0.0f, ( i == 2 || i == 3 ) ? 1.0f : 0.0f );
672     }
673
674     // Create cube indices.
675     int triangleIndex = 0u;     //Track progress through indices.
676     indices.Resize( 3u * 12u ); // 3 points x 12 triangles.
677
678     // Top face.
679     indices[triangleIndex] =     0;
680     indices[triangleIndex + 1] = 1;
681     indices[triangleIndex + 2] = 2;
682     indices[triangleIndex + 3] = 2;
683     indices[triangleIndex + 4] = 3;
684     indices[triangleIndex + 5] = 0;
685     triangleIndex += 6;
686
687     int topFaceStart = 4u;
688     int bottomFaceStart = topFaceStart + 8u;
689
690     // Side faces.
691     for( int i = 0; i < 8; i += 2, triangleIndex += 6 )
692     {
693       indices[triangleIndex    ] = i + topFaceStart;
694       indices[triangleIndex + 1] = i + bottomFaceStart + 1;
695       indices[triangleIndex + 2] = i + topFaceStart + 1;
696       indices[triangleIndex + 3] = i + topFaceStart;
697       indices[triangleIndex + 4] = i + bottomFaceStart;
698       indices[triangleIndex + 5] = i + bottomFaceStart + 1;
699     }
700
701     // Bottom face.
702     indices[triangleIndex] =     20;
703     indices[triangleIndex + 1] = 21;
704     indices[triangleIndex + 2] = 22;
705     indices[triangleIndex + 3] = 22;
706     indices[triangleIndex + 4] = 23;
707     indices[triangleIndex + 5] = 20;
708
709     // Use the helper method to create the geometry object.
710     return CreateTexturedGeometry( vertices, indices );
711   }
712
713   // Signal handlers:
714
715   /**
716    * @brief OnTouch signal handler.
717    * @param[in] actor The actor that has been touched
718    * @param[in] touch The touch information
719    * @return True if the event has been handled
720    */
721   bool OnTouch( Actor actor, const TouchEvent& touch )
722   {
723     // Quit the application.
724     mApplication.Quit();
725     return true;
726   }
727
728   /**
729    * @brief OnKeyEvent signal handler.
730    * @param[in] event The key event information
731    */
732   void OnKeyEvent( const KeyEvent& event )
733   {
734     if( event.GetState() == KeyEvent::DOWN )
735     {
736       if ( IsKey( event, Dali::DALI_KEY_ESCAPE ) || IsKey( event, Dali::DALI_KEY_BACK ) )
737       {
738         mApplication.Quit();
739       }
740     }
741   }
742
743 private:
744
745   // Member variables:
746
747   Application&     mApplication;       ///< The DALi application object
748   Toolkit::Control mView;              ///< The view used to show the background
749
750   Animation        mRotationAnimation; ///< The animation to spin the cube & floor
751   Animation        mBounceAnimation;   ///< The animation to bounce the cube
752   Actor            mCubes[2];          ///< The cube object containers
753 };
754
755 int DALI_EXPORT_API main( int argc, char **argv )
756 {
757   Application application = Application::New( &argc, &argv );
758   RendererStencilExample example( application );
759   application.MainLoop();
760   return 0;
761 }