renderer-stencil-example.cpp 32.6 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
/*
 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// EXTERNAL INCLUDES
#include <dali/public-api/rendering/shader.h>
#include <dali-toolkit/dali-toolkit.h>

// INTERNAL INCLUDES
#include "shared/utility.h"
#include "shared/view.h"
#include "generated/render-stencil-vert.h"
#include "generated/render-stencil-frag.h"
#include "generated/render-stencil-textured-vert.h"
#include "generated/render-stencil-textured-frag.h"

using namespace Dali;

namespace
{
// Constants:

// Application constants:
const char* const APPLICATION_TITLE("Renderer Stencil API Demo");
const char* const BACKGROUND_IMAGE(DEMO_IMAGE_DIR "background-gradient.jpg");

// Texture filenames:
const char* const CUBE_TEXTURE(DEMO_IMAGE_DIR "people-medium-1.jpg");
const char* const FLOOR_TEXTURE(DEMO_IMAGE_DIR "wood.png");

// Scale dimensions: These values are relative to the window size. EG. width = 0.32f * windowSize.
const float   CUBE_WIDTH_SCALE(0.32f);              ///< The width (and height + depth) of the main and reflection cubes.
const Vector2 FLOOR_DIMENSION_SCALE(0.67f, 0.017f); ///< The width and height of the floor object.

// Configurable animation characteristics:
const float ANIMATION_ROTATION_DURATION(10.0f);          ///< Time in seconds to rotate the scene 360 degrees around Y.
const float ANIMATION_BOUNCE_TOTAL_TIME(1.6f);           ///< Time in seconds to perform 1 full bounce animation cycle.
const float ANIMATION_BOUNCE_DEFORMATION_TIME(0.4f);     ///< Time in seconds that the cube deformation animation will occur for (on contact with the floor).
const float ANIMATION_BOUNCE_DEFORMATION_PERCENT(20.0f); ///< Percentage (of the cube's size) to deform the cube by (on contact with floor).
const float ANIMATION_BOUNCE_HEIGHT_PERCENT(40.0f);      ///< Percentage (of the cube's size) to bounce up in to the air by.

// Base colors for the objects:
const Vector4 TEXT_COLOR(1.0f, 1.0f, 1.0f, 1.0f);       ///< White.
const Vector4 CUBE_COLOR(1.0f, 1.0f, 1.0f, 1.0f);       ///< White.
const Vector4 FLOOR_COLOR(1.0f, 1.0f, 1.0f, 1.0f);      ///< White.
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.

// We need to control the draw order as we are controlling both the stencil and depth buffer per renderer.
const int DEPTH_INDEX_GRANULARITY(10000); ///< This value is the gap in depth-index in-between each renderer.

// Shader uniforms:
const char* const COLOR_UNIFORM_NAME("uColor");
const char* const OBJECT_DIMENSIONS_UNIFORM_NAME("uObjectDimensions");
const char* const LIGHT_POSITION_UNIFORM_NAME = "uLightPosition";
const char* const POSITION("aPosition");
const char* const NORMAL("aNormal");
const char* const TEXTURE("aTexCoord");

} // Anonymous namespace

/**
 * @brief This example shows how to manipulate stencil and depth buffer properties within the Renderer API.
 */
class RendererStencilExample : public ConnectionTracker
{
public:
  /**
   * @brief Constructor.
   * @param[in] application The DALi application object
   */
  RendererStencilExample(Application& application)
  : mApplication(application)
  {
    // Connect to the Application's Init signal.
    mApplication.InitSignal().Connect(this, &RendererStencilExample::Create);
  }

  /**
   * @brief Destructor (non-virtual).
   */
  ~RendererStencilExample()
  {
  }

private:
  /**
   * @brief Enum to facilitate more readable use of the cube array.
   */
  enum CubeType
  {
    MAIN_CUBE,      ///< The main cube that bounces above the floor object.
    REFLECTION_CUBE ///< The reflected cube object.
  };

  /**
   * @brief Struct to store the position, normal and texture coordinates of a single vertex.
   */
  struct TexturedVertex
  {
    Vector3 position;
    Vector3 normal;
    Vector2 textureCoord;
  };

  /**
   * @brief This is the main scene setup method for this demo.
   * This is called via the Init signal which is received once (only) during the Application lifetime.
   * @param[in] application The DALi application object
   */
  void Create(Application& application)
  {
    Window window = application.GetWindow();

    // Use a gradient visual to render the background gradient.
    Toolkit::Control background = Dali::Toolkit::Control::New();
    background.SetProperty(Actor::Property::ANCHOR_POINT, Dali::AnchorPoint::CENTER);
    background.SetProperty(Actor::Property::PARENT_ORIGIN, Dali::ParentOrigin::CENTER);
    background.SetResizePolicy(Dali::ResizePolicy::FILL_TO_PARENT, Dali::Dimension::ALL_DIMENSIONS);

    // Set up the background gradient.
    Property::Array stopOffsets;
    stopOffsets.PushBack(0.0f);
    stopOffsets.PushBack(1.0f);
    Property::Array stopColors;
    stopColors.PushBack(Vector4(0.17f, 0.24f, 0.35f, 1.0f)); // Dark, medium saturated blue  ( top   of screen)
    stopColors.PushBack(Vector4(0.45f, 0.70f, 0.80f, 1.0f)); // Medium bright, pastel blue   (bottom of screen)
    const float percentageWindowHeight = window.GetSize().GetHeight() * 0.7f;

    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));

    window.Add(background);

    // Create a TextLabel for the application title.
    Toolkit::TextLabel label = Toolkit::TextLabel::New(APPLICATION_TITLE);
    label.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
    // Set the parent origin to a small percentage below the top (so the demo will scale for different resolutions).
    label.SetProperty(Actor::Property::PARENT_ORIGIN, Vector3(0.5f, 0.03f, 0.5f));
    label.SetProperty(Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER");
    label.SetProperty(Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER");
    label.SetProperty(Toolkit::TextLabel::Property::TEXT_COLOR, TEXT_COLOR);
    window.Add(label);

    // Layer to hold the 3D scene.
    Layer layer = Layer::New();
    layer.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
    // Set the parent origin to a small percentage below the center (so the demo will scale for different resolutions).
    layer.SetProperty(Actor::Property::PARENT_ORIGIN, Vector3(0.5f, 0.58f, 0.5f));
    layer.SetProperty(Layer::Property::BEHAVIOR, Layer::LAYER_UI);
    layer.SetProperty(Layer::Property::DEPTH_TEST, true);
    window.Add(layer);

    // Main cube:
    // Make the demo scalable with different resolutions by basing
    // the cube size on a percentage of the window size.
    Vector2 windowSize = window.GetSize();
    float   scaleSize(std::min(windowSize.width, windowSize.height));
    float   cubeWidth(scaleSize * CUBE_WIDTH_SCALE);
    Vector3 cubeSize(cubeWidth, cubeWidth, cubeWidth);
    // Create the geometry for the cube, and the texture.
    Geometry   cubeGeometry   = CreateCubeVertices(Vector3::ONE, false);
    TextureSet cubeTextureSet = CreateTextureSet(CUBE_TEXTURE);
    // Create the cube object and add it.
    // Note: The cube is anchored around its base for animation purposes, so the position can be zero.
    mCubes[MAIN_CUBE] = CreateMainCubeObject(cubeGeometry, cubeSize, cubeTextureSet);
    layer.Add(mCubes[MAIN_CUBE]);

    // Floor:
    float   floorWidth(scaleSize * FLOOR_DIMENSION_SCALE.x);
    Vector3 floorSize(floorWidth, scaleSize * FLOOR_DIMENSION_SCALE.y, floorWidth);
    // Create the floor object using the cube geometry with a new size, and add it.
    Actor floorObject(CreateFloorObject(cubeGeometry, floorSize));
    layer.Add(floorObject);

    // Stencil:
    Vector3 planeSize(floorWidth, floorWidth, 0.0f);
    // Create the stencil plane object, and add it.
    Actor stencilPlaneObject(CreateStencilPlaneObject(planeSize));
    layer.Add(stencilPlaneObject);

    // Reflection cube:
    // Create the reflection cube object and add it.
    // Note: The cube is anchored around its base for animation purposes, so the position can be zero.
    mCubes[REFLECTION_CUBE] = CreateReflectionCubeObject(cubeSize, cubeTextureSet);
    layer.Add(mCubes[REFLECTION_CUBE]);

    // Rotate the layer so we can see some of the top of the cube for a more 3D effect.
    layer.SetProperty(Actor::Property::ORIENTATION, Quaternion(Degree(-24.0f), Degree(0.0f), Degree(0.0f)));

    // Set up the rotation on the Y axis.
    mRotationAnimation = Animation::New(ANIMATION_ROTATION_DURATION);
    float fullRotation = 360.0f;
    mRotationAnimation.AnimateBy(Property(mCubes[MAIN_CUBE], Actor::Property::ORIENTATION),
                                 Quaternion(Degree(0.0f), Degree(fullRotation), Degree(0.0f)));
    mRotationAnimation.AnimateBy(Property(floorObject, Actor::Property::ORIENTATION),
                                 Quaternion(Degree(0.0f), Degree(fullRotation), Degree(0.0f)));
    // Note the stencil is pre-rotated by 90 degrees on X, so we rotate relatively on its Z axis for an equivalent Y rotation.
    mRotationAnimation.AnimateBy(Property(stencilPlaneObject, Actor::Property::ORIENTATION),
                                 Quaternion(Degree(0.0f), Degree(0.0f), Degree(fullRotation)));
    mRotationAnimation.AnimateBy(Property(mCubes[REFLECTION_CUBE], Actor::Property::ORIENTATION),
                                 Quaternion(Degree(0.0f), Degree(fullRotation), Degree(0.0f)));
    mRotationAnimation.SetLooping(true);

    // Set up the cube bouncing animation.
    float totalTime       = ANIMATION_BOUNCE_TOTAL_TIME;
    float deformationTime = ANIMATION_BOUNCE_DEFORMATION_TIME;
    // Percentage based amounts allows the bounce and deformation to scale for different resolution screens.
    float deformationAmount = ANIMATION_BOUNCE_DEFORMATION_PERCENT / 100.0f;
    float heightChange      = (cubeSize.y * ANIMATION_BOUNCE_HEIGHT_PERCENT) / 100.0f;

    // Animation pre-calculations:
    float halfTime            = totalTime / 2.0f;
    float halfDeformationTime = deformationTime / 2.0f;

    // First position the cubes at the top of the animation cycle.
    mCubes[MAIN_CUBE].SetProperty(Actor::Property::POSITION_Y, -heightChange);
    mCubes[REFLECTION_CUBE].SetProperty(Actor::Property::POSITION_Y, heightChange);

    mBounceAnimation = Animation::New(totalTime);

    // The animations for the main and reflected cubes are almost identical, so we combine the code to do both.
    for(int cube = 0; cube < 2; ++cube)
    {
      // If iterating on the reflection cube, adjust the heightChange variable so the below code can be reused.
      if(cube == 1)
      {
        heightChange = -heightChange;
      }

      // 1st TimePeriod: Start moving down with increasing speed, until it is time to distort the cube due to impact.
      mBounceAnimation.AnimateBy(Property(mCubes[cube], Actor::Property::POSITION_Y), heightChange, AlphaFunction::EASE_IN_SQUARE, TimePeriod(0.0f, halfTime - halfDeformationTime));

      // 2nd TimePeriod: The cube is touching the floor, start deforming it - then un-deform it again.
      mBounceAnimation.AnimateBy(Property(mCubes[cube], Actor::Property::SCALE_X), deformationAmount, AlphaFunction::BOUNCE, TimePeriod(halfTime - halfDeformationTime, deformationTime));
      mBounceAnimation.AnimateBy(Property(mCubes[cube], Actor::Property::SCALE_Z), deformationAmount, AlphaFunction::BOUNCE, TimePeriod(halfTime - halfDeformationTime, deformationTime));
      mBounceAnimation.AnimateBy(Property(mCubes[cube], Actor::Property::SCALE_Y), -deformationAmount, AlphaFunction::BOUNCE, TimePeriod(halfTime - halfDeformationTime, deformationTime));

      // 3rd TimePeriod: Start moving up with decreasing speed, until at the apex of the animation.
      mBounceAnimation.AnimateBy(Property(mCubes[cube], Actor::Property::POSITION_Y), -heightChange, AlphaFunction::EASE_OUT_SQUARE, TimePeriod(halfTime + halfDeformationTime, halfTime - halfDeformationTime));
    }

    mBounceAnimation.SetLooping(true);

    // Start the animations.
    mRotationAnimation.Play();
    mBounceAnimation.Play();

    // Respond to a click anywhere on the window
    window.GetRootLayer().TouchedSignal().Connect(this, &RendererStencilExample::OnTouch);
    // Connect signals to allow Back and Escape to exit.
    window.KeyEventSignal().Connect(this, &RendererStencilExample::OnKeyEvent);
  }

private:
  // Methods to setup each component of the 3D scene:

  /**
   * @brief Creates the Main cube object.
   * This creates the renderer from existing geometry (as the cubes geometry is shared).
   * The texture is set and all relevant renderer properties are set-up.
   * @param[in] geometry Pre-calculated cube geometry
   * @param[in] size The desired cube size
   * @param[in] textureSet A pre-existing TextureSet with a texture set up, to be applied to the cube
   * @return An actor set-up containing the main cube object
   */
  Actor CreateMainCubeObject(Geometry& geometry, Vector3 size, TextureSet& textureSet)
  {
    Toolkit::Control container = Toolkit::Control::New();
    container.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_CENTER);
    container.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_CENTER);
    container.SetProperty(Actor::Property::SIZE, Vector2(size));
    container.SetResizePolicy(ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS);

    // Create a renderer from the geometry and add the texture.
    Renderer renderer = CreateRenderer(geometry, size, true, CUBE_COLOR);
    renderer.SetTextures(textureSet);

    // Setup the renderer properties:
    // We are writing to the color buffer & culling back faces (no stencil is used for the main cube).
    renderer.SetProperty(Renderer::Property::RENDER_MODE, RenderMode::COLOR);
    renderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);

    // We do need to write to the depth buffer as other objects need to appear underneath this cube.
    renderer.SetProperty(Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON);
    // We do not need to test the depth buffer as we are culling the back faces.
    renderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::OFF);

    // This object must be rendered 1st.
    renderer.SetProperty(Renderer::Property::DEPTH_INDEX, 0 * DEPTH_INDEX_GRANULARITY);

    container.AddRenderer(renderer);
    return container;
  }

  /**
   * @brief Creates the Floor object.
   * This creates the renderer from existing geometry (as the cube geometry can be re-used).
   * The texture is created and set and all relevant renderer properties are set-up.
   * @param[in] geometry Pre-calculated cube geometry
   * @param[in] size The desired floor size
   * @return An actor set-up containing the floor object
   */
  Actor CreateFloorObject(Geometry& geometry, Vector3 size)
  {
    Toolkit::Control container = Toolkit::Control::New();
    container.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
    container.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
    container.SetProperty(Actor::Property::SIZE, Vector2(size));
    container.SetResizePolicy(ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS);

    // Create a renderer from the geometry and add the texture.
    TextureSet planeTextureSet = CreateTextureSet(FLOOR_TEXTURE);
    Renderer   renderer        = CreateRenderer(geometry, size, true, FLOOR_COLOR);
    renderer.SetTextures(planeTextureSet);

    // Setup the renderer properties:
    // We are writing to the color buffer & culling back faces as we are NOT doing depth write (no stencil is used for the floor).
    renderer.SetProperty(Renderer::Property::RENDER_MODE, RenderMode::COLOR);
    renderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);

    // We do not write to the depth buffer as its not needed.
    renderer.SetProperty(Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::OFF);
    // We do need to test the depth buffer as we need the floor to be underneath the cube.
    renderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON);

    // This object must be rendered 2nd.
    renderer.SetProperty(Renderer::Property::DEPTH_INDEX, 1 * DEPTH_INDEX_GRANULARITY);

    container.AddRenderer(renderer);
    return container;
  }

  /**
   * @brief Creates the Stencil-Plane object.
   * This is places on the floor object to allow the reflection to be drawn on to the floor.
   * This creates the geometry and renderer.
   * All relevant renderer properties are set-up.
   * @param[in] size The desired plane size
   * @return An actor set-up containing the stencil-plane object
   */
  Actor CreateStencilPlaneObject(Vector3 size)
  {
    Toolkit::Control container = Toolkit::Control::New();
    container.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
    container.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
    container.SetProperty(Actor::Property::SIZE, Vector2(size));
    container.SetResizePolicy(ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS);

    // We rotate the plane as the geometry is created flat in X & Y. We want it to span X & Z axis.
    container.SetProperty(Actor::Property::ORIENTATION, Quaternion(Degree(-90.0f), Degree(0.0f), Degree(0.0f)));

    // Create geometry for a flat plane.
    Geometry planeGeometry = CreatePlaneVertices(Vector2::ONE);
    // Create a renderer from the geometry.
    Renderer renderer = CreateRenderer(planeGeometry, size, false, Vector4::ONE);

    // Setup the renderer properties:
    // The stencil plane is only for stencilling.
    renderer.SetProperty(Renderer::Property::RENDER_MODE, RenderMode::STENCIL);

    renderer.SetProperty(Renderer::Property::STENCIL_FUNCTION, StencilFunction::ALWAYS);
    renderer.SetProperty(Renderer::Property::STENCIL_FUNCTION_REFERENCE, 1);
    renderer.SetProperty(Renderer::Property::STENCIL_FUNCTION_MASK, 0xFF);
    renderer.SetProperty(Renderer::Property::STENCIL_OPERATION_ON_FAIL, StencilOperation::KEEP);
    renderer.SetProperty(Renderer::Property::STENCIL_OPERATION_ON_Z_FAIL, StencilOperation::KEEP);
    renderer.SetProperty(Renderer::Property::STENCIL_OPERATION_ON_Z_PASS, StencilOperation::REPLACE);
    renderer.SetProperty(Renderer::Property::STENCIL_MASK, 0xFF);

    // We don't want to write to the depth buffer, as this would block the reflection being drawn.
    renderer.SetProperty(Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::OFF);
    // We test the depth buffer as we want the stencil to only exist underneath the cube.
    renderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON);

    // This object must be rendered 3rd.
    renderer.SetProperty(Renderer::Property::DEPTH_INDEX, 2 * DEPTH_INDEX_GRANULARITY);

    container.AddRenderer(renderer);
    return container;
  }

  /**
   * @brief Creates the Reflection cube object.
   * This creates new geometry (as the texture UVs are different to the main cube).
   * The renderer is then created.
   * The texture is set and all relevant renderer properties are set-up.
   * @param[in] size The desired cube size
   * @param[in] textureSet A pre-existing TextureSet with a texture set up, to be applied to the cube
   * @return An actor set-up containing the reflection cube object
   */
  Actor CreateReflectionCubeObject(Vector3 size, TextureSet& textureSet)
  {
    Toolkit::Control container = Toolkit::Control::New();
    container.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER);
    container.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER);
    container.SetProperty(Actor::Property::SIZE, Vector2(size));
    container.SetResizePolicy(ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS);

    // Create the cube geometry of unity size.
    // The "true" specifies we want the texture UVs flipped vertically as this is the reflection cube.
    Geometry reflectedCubeGeometry = CreateCubeVertices(Vector3::ONE, true);
    // Create a renderer from the geometry and add the texture.
    Renderer renderer = CreateRenderer(reflectedCubeGeometry, size, true, REFLECTION_COLOR);
    renderer.SetTextures(textureSet);

    // Setup the renderer properties:
    // Write to color buffer so reflection is visible.
    // Also enable the stencil buffer, as we will be testing against it to only draw to areas within the stencil.
    renderer.SetProperty(Renderer::Property::RENDER_MODE, RenderMode::COLOR_STENCIL);
    // We cull to skip drawing the back faces.
    renderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);

    // We use blending to blend the reflection with the floor texture.
    renderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
    renderer.SetProperty(Renderer::Property::BLEND_EQUATION_RGB, BlendEquation::ADD);
    renderer.SetProperty(Renderer::Property::BLEND_EQUATION_ALPHA, BlendEquation::ADD);
    renderer.SetProperty(Renderer::Property::BLEND_FACTOR_DEST_RGB, BlendFactor::ONE);

    // Enable stencil. Here we only draw to areas within the stencil.
    renderer.SetProperty(Renderer::Property::STENCIL_FUNCTION, StencilFunction::EQUAL);
    renderer.SetProperty(Renderer::Property::STENCIL_FUNCTION_REFERENCE, 1);
    renderer.SetProperty(Renderer::Property::STENCIL_FUNCTION_MASK, 0xff);
    // Don't write to the stencil.
    renderer.SetProperty(Renderer::Property::STENCIL_MASK, 0x00);

    // We don't need to write to the depth buffer, as we are culling.
    renderer.SetProperty(Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::OFF);
    // We need to test the depth buffer as we need the reflection to be underneath the cube.
    renderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON);

    // This object must be rendered last.
    renderer.SetProperty(Renderer::Property::DEPTH_INDEX, 3 * DEPTH_INDEX_GRANULARITY);

    container.AddRenderer(renderer);
    return container;
  }

  // Methods:

  /**
   * @brief Creates a geometry object from vertices and indices.
   * @param[in] vertices The object vertices
   * @param[in] indices The object indices
   * @return A geometry object
   */
  Geometry CreateTexturedGeometry(Vector<TexturedVertex>& vertices, Vector<unsigned short>& indices)
  {
    // Vertices
    Property::Map vertexFormat;
    vertexFormat[POSITION] = Property::VECTOR3;
    vertexFormat[NORMAL]   = Property::VECTOR3;
    vertexFormat[TEXTURE]  = Property::VECTOR2;

    VertexBuffer surfaceVertices = VertexBuffer::New(vertexFormat);
    surfaceVertices.SetData(&vertices[0u], vertices.Size());

    Geometry geometry = Geometry::New();
    geometry.AddVertexBuffer(surfaceVertices);

    // Indices for triangle formulation
    geometry.SetIndexBuffer(&indices[0u], indices.Size());
    return geometry;
  }

  /**
   * @brief Creates a renderer from a geometry object.
   * @param[in] geometry The geometry to use
   * @param[in] dimensions The dimensions (will be passed in to the shader)
   * @param[in] textured Set to true to use the texture versions of the shaders
   * @param[in] color The base color for the renderer
   * @return A renderer object
   */
  Renderer CreateRenderer(Geometry geometry, Vector3 dimensions, bool textured, Vector4 color)
  {
    Window  window     = mApplication.GetWindow();
    Vector2 windowSize = window.GetSize();
    Shader  shader;

    if(textured)
    {
      shader = Shader::New(SHADER_RENDER_STENCIL_TEXTURED_VERT, SHADER_RENDER_STENCIL_TEXTURED_FRAG);
    }
    else
    {
      shader = Shader::New(SHADER_RENDER_STENCIL_VERT, SHADER_RENDER_STENCIL_FRAG);
    }

    // Here we modify the light position based on half the window size as a pre-calculation step.
    // This avoids the work having to be done in the shader.
    shader.RegisterProperty(LIGHT_POSITION_UNIFORM_NAME, Vector3(-windowSize.width / 2.0f, -windowSize.width / 2.0f, 1000.0f));
    shader.RegisterProperty(COLOR_UNIFORM_NAME, color);
    shader.RegisterProperty(OBJECT_DIMENSIONS_UNIFORM_NAME, dimensions);

    return Renderer::New(geometry, shader);
  }

  /**
   * @brief Helper method to create a TextureSet from an image URL.
   * @param[in] url An image URL
   * @return A TextureSet object
   */
  TextureSet CreateTextureSet(const char* url)
  {
    TextureSet textureSet = TextureSet::New();

    if(textureSet)
    {
      Texture texture = DemoHelper::LoadTexture(url);
      if(texture)
      {
        textureSet.SetTexture(0u, texture);
      }
    }

    return textureSet;
  }

  // Geometry Creation:

  /**
   * @brief Creates a geometry object for a flat plane.
   * The plane is oriented in X & Y axis (Z is 0).
   * @param[in] dimensions The desired plane dimensions
   * @return A Geometry object
   */
  Geometry CreatePlaneVertices(Vector2 dimensions)
  {
    Vector<TexturedVertex> vertices;
    Vector<unsigned short> indices;
    vertices.Resize(4u);
    indices.Resize(6u);

    float scaledX = 0.5f * dimensions.x;
    float scaledY = 0.5f * dimensions.y;

    vertices[0].position     = Vector3(-scaledX, -scaledY, 0.0f);
    vertices[0].textureCoord = Vector2(0.0, 0.0f);
    vertices[1].position     = Vector3(scaledX, -scaledY, 0.0f);
    vertices[1].textureCoord = Vector2(1.0, 0.0f);
    vertices[2].position     = Vector3(scaledX, scaledY, 0.0f);
    vertices[2].textureCoord = Vector2(1.0, 1.0f);
    vertices[3].position     = Vector3(-scaledX, scaledY, 0.0f);
    vertices[3].textureCoord = Vector2(0.0, 1.0f);

    // All vertices have the same normal.
    for(int i = 0; i < 4; ++i)
    {
      vertices[i].normal = Vector3(0.0f, 0.0f, -1.0f);
    }

    indices[0] = 0;
    indices[1] = 1;
    indices[2] = 2;
    indices[3] = 2;
    indices[4] = 3;
    indices[5] = 0;

    // Use the helper method to create the geometry object.
    return CreateTexturedGeometry(vertices, indices);
  }

  /**
   * @brief Creates a geometry object for a cube (or cuboid).
   * @param[in] dimensions The desired cube dimensions
   * @param[in] reflectVerticalUVs Set to True to force the UVs to be vertically flipped
   * @return A Geometry object
   */
  Geometry CreateCubeVertices(Vector3 dimensions, bool reflectVerticalUVs)
  {
    Vector<TexturedVertex> vertices;
    Vector<unsigned short> indices;
    int                    vertexIndex          = 0u; // Tracks progress through vertices.
    float                  scaledX              = 0.5f * dimensions.x;
    float                  scaledY              = 0.5f * dimensions.y;
    float                  scaledZ              = 0.5f * dimensions.z;
    float                  verticalTextureCoord = reflectVerticalUVs ? 0.0f : 1.0f;

    vertices.Resize(4u * 6u); // 4 vertices x 6 faces

    Vector<Vector3> positions; // Stores vertex positions, which are shared between vertexes at the same position but with a different normal.
    positions.Resize(8u);
    Vector<Vector3> normals; // Stores normals, which are shared between vertexes of the same face.
    normals.Resize(6u);

    positions[0] = Vector3(-scaledX, scaledY, -scaledZ);
    positions[1] = Vector3(scaledX, scaledY, -scaledZ);
    positions[2] = Vector3(scaledX, scaledY, scaledZ);
    positions[3] = Vector3(-scaledX, scaledY, scaledZ);
    positions[4] = Vector3(-scaledX, -scaledY, -scaledZ);
    positions[5] = Vector3(scaledX, -scaledY, -scaledZ);
    positions[6] = Vector3(scaledX, -scaledY, scaledZ);
    positions[7] = Vector3(-scaledX, -scaledY, scaledZ);

    normals[0] = Vector3(0, 1, 0);
    normals[1] = Vector3(0, 0, -1);
    normals[2] = Vector3(1, 0, 0);
    normals[3] = Vector3(0, 0, 1);
    normals[4] = Vector3(-1, 0, 0);
    normals[5] = Vector3(0, -1, 0);

    // Top face, upward normals.
    for(int i = 0; i < 4; ++i, ++vertexIndex)
    {
      vertices[vertexIndex].position = positions[i];
      vertices[vertexIndex].normal   = normals[0];
      // The below logic forms the correct U/V pairs for a quad when "i" goes from 0 to 3.
      vertices[vertexIndex].textureCoord = Vector2((i == 1 || i == 2) ? 1.0f : 0.0f, (i == 2 || i == 3) ? 1.0f : 0.0f);
    }

    // Top face, outward normals.
    for(int i = 0; i < 4; ++i, vertexIndex += 2)
    {
      vertices[vertexIndex].position = positions[i];
      vertices[vertexIndex].normal   = normals[i + 1];

      if(i == 3)
      {
        // End, so loop around.
        vertices[vertexIndex + 1].position = positions[0];
      }
      else
      {
        vertices[vertexIndex + 1].position = positions[i + 1];
      }
      vertices[vertexIndex + 1].normal = normals[i + 1];

      vertices[vertexIndex].textureCoord     = Vector2(0.0f, verticalTextureCoord);
      vertices[vertexIndex + 1].textureCoord = Vector2(1.0f, verticalTextureCoord);
    }

    // Flip the vertical texture coord for the UV values of the bottom points.
    verticalTextureCoord = 1.0f - verticalTextureCoord;

    // Bottom face, outward normals.
    for(int i = 0; i < 4; ++i, vertexIndex += 2)
    {
      vertices[vertexIndex].position = positions[i + 4];
      vertices[vertexIndex].normal   = normals[i + 1];

      if(i == 3)
      {
        // End, so loop around.
        vertices[vertexIndex + 1].position = positions[4];
      }
      else
      {
        vertices[vertexIndex + 1].position = positions[i + 5];
      }
      vertices[vertexIndex + 1].normal = normals[i + 1];

      vertices[vertexIndex].textureCoord     = Vector2(0.0f, verticalTextureCoord);
      vertices[vertexIndex + 1].textureCoord = Vector2(1.0f, verticalTextureCoord);
    }

    // Bottom face, downward normals.
    for(int i = 0; i < 4; ++i, ++vertexIndex)
    {
      // Reverse positions for bottom face to keep triangles clockwise (for culling).
      vertices[vertexIndex].position = positions[7 - i];
      vertices[vertexIndex].normal   = normals[5];
      // The below logic forms the correct U/V pairs for a quad when "i" goes from 0 to 3.
      vertices[vertexIndex].textureCoord = Vector2((i == 1 || i == 2) ? 1.0f : 0.0f, (i == 2 || i == 3) ? 1.0f : 0.0f);
    }

    // Create cube indices.
    int triangleIndex = 0u;   //Track progress through indices.
    indices.Resize(3u * 12u); // 3 points x 12 triangles.

    // Top face.
    indices[triangleIndex]     = 0;
    indices[triangleIndex + 1] = 1;
    indices[triangleIndex + 2] = 2;
    indices[triangleIndex + 3] = 2;
    indices[triangleIndex + 4] = 3;
    indices[triangleIndex + 5] = 0;
    triangleIndex += 6;

    int topFaceStart    = 4u;
    int bottomFaceStart = topFaceStart + 8u;

    // Side faces.
    for(int i = 0; i < 8; i += 2, triangleIndex += 6)
    {
      indices[triangleIndex]     = i + topFaceStart;
      indices[triangleIndex + 1] = i + bottomFaceStart + 1;
      indices[triangleIndex + 2] = i + topFaceStart + 1;
      indices[triangleIndex + 3] = i + topFaceStart;
      indices[triangleIndex + 4] = i + bottomFaceStart;
      indices[triangleIndex + 5] = i + bottomFaceStart + 1;
    }

    // Bottom face.
    indices[triangleIndex]     = 20;
    indices[triangleIndex + 1] = 21;
    indices[triangleIndex + 2] = 22;
    indices[triangleIndex + 3] = 22;
    indices[triangleIndex + 4] = 23;
    indices[triangleIndex + 5] = 20;

    // Use the helper method to create the geometry object.
    return CreateTexturedGeometry(vertices, indices);
  }

  // Signal handlers:

  /**
   * @brief OnTouch signal handler.
   * @param[in] actor The actor that has been touched
   * @param[in] touch The touch information
   * @return True if the event has been handled
   */
  bool OnTouch(Actor actor, const TouchEvent& touch)
  {
    // Quit the application.
    mApplication.Quit();
    return true;
  }

  /**
   * @brief OnKeyEvent signal handler.
   * @param[in] event The key event information
   */
  void OnKeyEvent(const KeyEvent& event)
  {
    if(event.GetState() == KeyEvent::DOWN)
    {
      if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
      {
        mApplication.Quit();
      }
    }
  }

private:
  // Member variables:

  Application&     mApplication; ///< The DALi application object
  Toolkit::Control mView;        ///< The view used to show the background

  Animation mRotationAnimation; ///< The animation to spin the cube & floor
  Animation mBounceAnimation;   ///< The animation to bounce the cube
  Actor     mCubes[2];          ///< The cube object containers
};

int DALI_EXPORT_API main(int argc, char** argv)
{
  Application            application = Application::New(&argc, &argv);
  RendererStencilExample example(application);
  application.MainLoop();
  return 0;
}