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