2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/dali-toolkit.h>
22 #include "renderer-stencil-shaders.h"
23 #include "shared/utility.h"
24 #include "shared/view.h"
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");
37 const char* const CUBE_TEXTURE(DEMO_IMAGE_DIR "people-medium-1.jpg");
38 const char* const FLOOR_TEXTURE(DEMO_IMAGE_DIR "wood.png");
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.
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.
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.
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.
60 } // Anonymous namespace
63 * @brief This example shows how to manipulate stencil and depth buffer properties within the Renderer API.
65 class RendererStencilExample : public ConnectionTracker
70 * @param[in] application The DALi application object
72 RendererStencilExample(Application& application)
73 : mApplication(application)
75 // Connect to the Application's Init signal.
76 mApplication.InitSignal().Connect(this, &RendererStencilExample::Create);
80 * @brief Destructor (non-virtual).
82 ~RendererStencilExample()
88 * @brief Enum to facilitate more readable use of the cube array.
92 MAIN_CUBE, ///< The main cube that bounces above the floor object.
93 REFLECTION_CUBE ///< The reflected cube object.
97 * @brief Struct to store the position, normal and texture coordinates of a single vertex.
103 Vector2 textureCoord;
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
111 void Create(Application& application)
113 Window window = application.GetWindow();
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);
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;
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));
132 window.Add(background);
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);
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);
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]);
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);
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);
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]);
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)));
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);
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;
211 // Animation pre-calculations:
212 float halfTime = totalTime / 2.0f;
213 float halfDeformationTime = deformationTime / 2.0f;
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);
219 mBounceAnimation = Animation::New(totalTime);
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)
224 // If iterating on the reflection cube, adjust the heightChange variable so the below code can be reused.
227 heightChange = -heightChange;
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));
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));
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));
242 mBounceAnimation.SetLooping(true);
244 // Start the animations.
245 mRotationAnimation.Play();
246 mBounceAnimation.Play();
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);
255 // Methods to setup each component of the 3D scene:
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
266 Actor CreateMainCubeObject(Geometry& geometry, Vector3 size, TextureSet& textureSet)
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);
274 // Create a renderer from the geometry and add the texture.
275 Renderer renderer = CreateRenderer(geometry, size, true, CUBE_COLOR);
276 renderer.SetTextures(textureSet);
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);
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);
288 // This object must be rendered 1st.
289 renderer.SetProperty(Renderer::Property::DEPTH_INDEX, 0 * DEPTH_INDEX_GRANULARITY);
291 container.AddRenderer(renderer);
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
303 Actor CreateFloorObject(Geometry& geometry, Vector3 size)
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);
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);
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);
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);
326 // This object must be rendered 2nd.
327 renderer.SetProperty(Renderer::Property::DEPTH_INDEX, 1 * DEPTH_INDEX_GRANULARITY);
329 container.AddRenderer(renderer);
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
341 Actor CreateStencilPlaneObject(Vector3 size)
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);
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)));
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);
357 // Setup the renderer properties:
358 // The stencil plane is only for stencilling.
359 renderer.SetProperty(Renderer::Property::RENDER_MODE, RenderMode::STENCIL);
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);
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);
374 // This object must be rendered 3rd.
375 renderer.SetProperty(Renderer::Property::DEPTH_INDEX, 2 * DEPTH_INDEX_GRANULARITY);
377 container.AddRenderer(renderer);
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
390 Actor CreateReflectionCubeObject(Vector3 size, TextureSet& textureSet)
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);
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);
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);
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);
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);
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);
430 // This object must be rendered last.
431 renderer.SetProperty(Renderer::Property::DEPTH_INDEX, 3 * DEPTH_INDEX_GRANULARITY);
433 container.AddRenderer(renderer);
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
445 Geometry CreateTexturedGeometry(Vector<TexturedVertex>& vertices, Vector<unsigned short>& indices)
448 Property::Map vertexFormat;
449 vertexFormat[POSITION] = Property::VECTOR3;
450 vertexFormat[NORMAL] = Property::VECTOR3;
451 vertexFormat[TEXTURE] = Property::VECTOR2;
453 VertexBuffer surfaceVertices = VertexBuffer::New(vertexFormat);
454 surfaceVertices.SetData(&vertices[0u], vertices.Size());
456 Geometry geometry = Geometry::New();
457 geometry.AddVertexBuffer(surfaceVertices);
459 // Indices for triangle formulation
460 geometry.SetIndexBuffer(&indices[0u], indices.Size());
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
472 Renderer CreateRenderer(Geometry geometry, Vector3 dimensions, bool textured, Vector4 color)
474 Window window = mApplication.GetWindow();
475 Vector2 windowSize = window.GetSize();
480 shader = Shader::New(VERTEX_SHADER_TEXTURED, FRAGMENT_SHADER_TEXTURED);
484 shader = Shader::New(VERTEX_SHADER, FRAGMENT_SHADER);
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);
493 return Renderer::New(geometry, shader);
497 * @brief Helper method to create a TextureSet from an image URL.
498 * @param[in] url An image URL
499 * @return A TextureSet object
501 TextureSet CreateTextureSet(const char* url)
503 TextureSet textureSet = TextureSet::New();
507 Texture texture = DemoHelper::LoadTexture(url);
510 textureSet.SetTexture(0u, texture);
517 // Geometry Creation:
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
525 Geometry CreatePlaneVertices(Vector2 dimensions)
527 Vector<TexturedVertex> vertices;
528 Vector<unsigned short> indices;
532 float scaledX = 0.5f * dimensions.x;
533 float scaledY = 0.5f * dimensions.y;
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);
544 // All vertices have the same normal.
545 for(int i = 0; i < 4; ++i)
547 vertices[i].normal = Vector3(0.0f, 0.0f, -1.0f);
557 // Use the helper method to create the geometry object.
558 return CreateTexturedGeometry(vertices, indices);
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
567 Geometry CreateCubeVertices(Vector3 dimensions, bool reflectVerticalUVs)
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;
577 vertices.Resize(4u * 6u); // 4 vertices x 6 faces
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.
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);
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);
600 // Top face, upward normals.
601 for(int i = 0; i < 4; ++i, ++vertexIndex)
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);
609 // Top face, outward normals.
610 for(int i = 0; i < 4; ++i, vertexIndex += 2)
612 vertices[vertexIndex].position = positions[i];
613 vertices[vertexIndex].normal = normals[i + 1];
617 // End, so loop around.
618 vertices[vertexIndex + 1].position = positions[0];
622 vertices[vertexIndex + 1].position = positions[i + 1];
624 vertices[vertexIndex + 1].normal = normals[i + 1];
626 vertices[vertexIndex].textureCoord = Vector2(0.0f, verticalTextureCoord);
627 vertices[vertexIndex + 1].textureCoord = Vector2(1.0f, verticalTextureCoord);
630 // Flip the vertical texture coord for the UV values of the bottom points.
631 verticalTextureCoord = 1.0f - verticalTextureCoord;
633 // Bottom face, outward normals.
634 for(int i = 0; i < 4; ++i, vertexIndex += 2)
636 vertices[vertexIndex].position = positions[i + 4];
637 vertices[vertexIndex].normal = normals[i + 1];
641 // End, so loop around.
642 vertices[vertexIndex + 1].position = positions[4];
646 vertices[vertexIndex + 1].position = positions[i + 5];
648 vertices[vertexIndex + 1].normal = normals[i + 1];
650 vertices[vertexIndex].textureCoord = Vector2(0.0f, verticalTextureCoord);
651 vertices[vertexIndex + 1].textureCoord = Vector2(1.0f, verticalTextureCoord);
654 // Bottom face, downward normals.
655 for(int i = 0; i < 4; ++i, ++vertexIndex)
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);
664 // Create cube indices.
665 int triangleIndex = 0u; //Track progress through indices.
666 indices.Resize(3u * 12u); // 3 points x 12 triangles.
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;
677 int topFaceStart = 4u;
678 int bottomFaceStart = topFaceStart + 8u;
681 for(int i = 0; i < 8; i += 2, triangleIndex += 6)
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;
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;
699 // Use the helper method to create the geometry object.
700 return CreateTexturedGeometry(vertices, indices);
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
711 bool OnTouch(Actor actor, const TouchEvent& touch)
713 // Quit the application.
719 * @brief OnKeyEvent signal handler.
720 * @param[in] event The key event information
722 void OnKeyEvent(const KeyEvent& event)
724 if(event.GetState() == KeyEvent::DOWN)
726 if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
736 Application& mApplication; ///< The DALi application object
737 Toolkit::Control mView; ///< The view used to show the background
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
744 int DALI_EXPORT_API main(int argc, char** argv)
746 Application application = Application::New(&argc, &argv);
747 RendererStencilExample example(application);
748 application.MainLoop();