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