Commit de91eaa51ec2994d4ad3eae585475bca9b2c276d

Authored by György Straub
Committed by Adeel Kazmi
1 parent e08a711d

Added Particles example.

- to tilt world, tilt device or if tilt isn't supported, swipe;
- tap to scatter particles;
- double click to trigger color change;

Change-Id: I8c72ea1ff7a8226b03b5e106936ca3644ef1f5b3
com.samsung.dali-demo.xml
@@ -169,6 +169,9 @@ @@ -169,6 +169,9 @@
169 <ui-application appid="page-turn-view.example" exec="/usr/apps/com.samsung.dali-demo/bin/page-turn-view.example" nodisplay="true" multiple="false" type="c++app" taskmanage="true"> 169 <ui-application appid="page-turn-view.example" exec="/usr/apps/com.samsung.dali-demo/bin/page-turn-view.example" nodisplay="true" multiple="false" type="c++app" taskmanage="true">
170 <label>Page Turn</label> 170 <label>Page Turn</label>
171 </ui-application> 171 </ui-application>
  172 + <ui-application appid="particles.example" exec="/usr/apps/com.samsung.dali-demo/bin/particles.example" nodisplay="true" multiple="false" type="c++app" taskmanage="true">
  173 + <label>Particles</label>
  174 + </ui-application>
172 <ui-application appid="perf-scroll.example" exec="/usr/apps/com.samsung.dali-demo/bin/perf-scroll.example" nodisplay="true" multiple="false" type="c++app" taskmanage="true"> 175 <ui-application appid="perf-scroll.example" exec="/usr/apps/com.samsung.dali-demo/bin/perf-scroll.example" nodisplay="true" multiple="false" type="c++app" taskmanage="true">
173 <label>perf-scroll</label> 176 <label>perf-scroll</label>
174 </ui-application> 177 </ui-application>
demo/dali-demo.cpp
@@ -50,6 +50,7 @@ int DALI_EXPORT_API main(int argc, char** argv) @@ -50,6 +50,7 @@ int DALI_EXPORT_API main(int argc, char** argv)
50 demo.AddExample(Example("metaball-refrac.example", DALI_DEMO_STR_TITLE_METABALL_REFRAC)); 50 demo.AddExample(Example("metaball-refrac.example", DALI_DEMO_STR_TITLE_METABALL_REFRAC));
51 demo.AddExample(Example("motion-blur.example", DALI_DEMO_STR_TITLE_MOTION_BLUR)); 51 demo.AddExample(Example("motion-blur.example", DALI_DEMO_STR_TITLE_MOTION_BLUR));
52 demo.AddExample(Example("page-turn-view.example", DALI_DEMO_STR_TITLE_PAGE_TURN)); 52 demo.AddExample(Example("page-turn-view.example", DALI_DEMO_STR_TITLE_PAGE_TURN));
  53 + demo.AddExample(Example("particles.example", DALI_DEMO_STR_TITLE_PARTICLES));
53 demo.AddExample(Example("reflection-demo.example", DALI_DEMO_STR_TITLE_REFLECTION)); 54 demo.AddExample(Example("reflection-demo.example", DALI_DEMO_STR_TITLE_REFLECTION));
54 demo.AddExample(Example("refraction-effect.example", DALI_DEMO_STR_TITLE_REFRACTION)); 55 demo.AddExample(Example("refraction-effect.example", DALI_DEMO_STR_TITLE_REFRACTION));
55 demo.AddExample(Example("renderer-stencil.example", DALI_DEMO_STR_TITLE_RENDERER_STENCIL)); 56 demo.AddExample(Example("renderer-stencil.example", DALI_DEMO_STR_TITLE_RENDERER_STENCIL));
examples/particles/float-rand.h 0 → 100644
  1 +#ifndef PARTICLES_FLOAT_RAND_H_
  2 +#define PARTICLES_FLOAT_RAND_H_
  3 +/*
  4 + * Copyright (c) 2020 Samsung Electronics Co., Ltd.
  5 + *
  6 + * Licensed under the Apache License, Version 2.0 (the "License");
  7 + * you may not use this file except in compliance with the License.
  8 + * You may obtain a copy of the License at
  9 + *
  10 + * http://www.apache.org/licenses/LICENSE-2.0
  11 + *
  12 + * Unless required by applicable law or agreed to in writing, software
  13 + * distributed under the License is distributed on an "AS IS" BASIS,
  14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15 + * See the License for the specific language governing permissions and
  16 + * limitations under the License.
  17 + *
  18 + */
  19 +#include <random>
  20 +
  21 +struct FloatRand
  22 +{
  23 + std::random_device mDevice;
  24 + std::mt19937 mMersenneTwister;
  25 + std::uniform_real_distribution<float> mDistribution;
  26 +
  27 + FloatRand()
  28 + : mMersenneTwister(mDevice()),
  29 + mDistribution(0., 1.)
  30 + {}
  31 +
  32 + float operator()()
  33 + {
  34 + return mDistribution(mMersenneTwister);
  35 + }
  36 +};
  37 +
  38 +#endif //PARTICLES_FLOAT_RAND_H_
0 \ No newline at end of file 39 \ No newline at end of file
examples/particles/particle-field.h 0 → 100644
  1 +#ifndef PARTICLES_PARTICLE_FIELD_H_
  2 +#define PARTICLES_PARTICLE_FIELD_H_
  3 +/*
  4 + * Copyright (c) 2020 Samsung Electronics Co., Ltd.
  5 + *
  6 + * Licensed under the Apache License, Version 2.0 (the "License");
  7 + * you may not use this file except in compliance with the License.
  8 + * You may obtain a copy of the License at
  9 + *
  10 + * http://www.apache.org/licenses/LICENSE-2.0
  11 + *
  12 + * Unless required by applicable law or agreed to in writing, software
  13 + * distributed under the License is distributed on an "AS IS" BASIS,
  14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15 + * See the License for the specific language governing permissions and
  16 + * limitations under the License.
  17 + *
  18 + */
  19 +#include "utils.h"
  20 +#include "float-rand.h"
  21 +#include "dali/public-api/math/vector2.h"
  22 +#include "dali/public-api/math/vector3.h"
  23 +#include "dali/public-api/math/vector4.h"
  24 +#include "dali/public-api/rendering/geometry.h"
  25 +#include <vector>
  26 +
  27 +struct ParticleField
  28 +{
  29 + float mSize;
  30 + Dali::Vector3 mBoxSize;
  31 + Dali::Vector3 mParticlesPerAxis;
  32 + float mSizeVariance;
  33 + float mNoiseAmount; // affects color, motion (phase), twinkle (frequency, phase, size, opacity),
  34 + float mDisperse;
  35 + float mMotionScale;
  36 + float mMotionCycleLength; // seconds
  37 + float mTwinkleFrequency; // per motion cycle
  38 + float mTwinkleSizeScale;
  39 + float mTwinkleOpacityWeight;
  40 +
  41 + Dali::Vector3 GetParticlesPerAxisSafe() const
  42 + {
  43 + using namespace Dali;
  44 + return Vector3(std::max(1.f, FastFloor(mParticlesPerAxis.x)),
  45 + std::max(1.f, FastFloor(mParticlesPerAxis.y)),
  46 + std::max(1.f, FastFloor(mParticlesPerAxis.z)));
  47 + }
  48 +
  49 + Dali::Geometry MakeGeometry() const
  50 + {
  51 + using namespace Dali;
  52 + FloatRand frandPath;
  53 + FloatRand frandSeed;
  54 + FloatRand frandPos;
  55 + FloatRand frandDisperse;
  56 + FloatRand frandSize;
  57 +
  58 + struct Vertex
  59 + {
  60 + Vector3 aPosition;
  61 + float aSeed;
  62 + Vector4 aPath;
  63 + Vector2 aSubPosition;
  64 + float aSize;
  65 + };
  66 +
  67 + const int numPatternVertices = 6;
  68 + Vector2 vertexPattern[numPatternVertices] = {
  69 + Vector2(-1.f, 1.f),
  70 + Vector2(-1.f, -1.f),
  71 + Vector2(1.f, 1.f),
  72 + Vector2(1.f, 1.f),
  73 + Vector2(-1.f, -1.f),
  74 + Vector2(1.f, -1.f),
  75 + };
  76 +
  77 + Vector3 particlesPerAxis = GetParticlesPerAxisSafe();
  78 + auto numParticles = particlesPerAxis.x * particlesPerAxis.y * particlesPerAxis.z;
  79 +
  80 + std::vector<Vertex> vertices;
  81 + vertices.reserve(numParticles * numPatternVertices);
  82 +
  83 + Vector3 invBoxSize(1. / std::max(mBoxSize.x, 1.f),
  84 + 1. / std::max(mBoxSize.y, 1.f),
  85 + 1. / std::max(mBoxSize.z, 1.f));
  86 + Vector3 spacing(mBoxSize.x / particlesPerAxis.x,
  87 + mBoxSize.y / particlesPerAxis.y,
  88 + mBoxSize.z / particlesPerAxis.z);
  89 + auto offset = (mBoxSize - spacing) * .5;
  90 + int nx = particlesPerAxis.x;
  91 + int ny = particlesPerAxis.y;
  92 + int nxy = nx * ny;
  93 + for (size_t i = 0; i < numParticles; ++i)
  94 + {
  95 + Vertex v;
  96 + v.aPosition = Vector3((i % nx) * spacing.x, (i / nx) % ny * spacing.y, (i / nxy) * spacing.z) - offset;
  97 +
  98 + Vector3 disperseDir(frandDisperse() - .5, frandDisperse() - .5, frandDisperse() - .5);
  99 + disperseDir.Normalize();
  100 +
  101 + v.aPosition += disperseDir * (frandDisperse() * mDisperse);
  102 + v.aPosition *= invBoxSize;
  103 +
  104 + v.aSeed = frandSeed() * mNoiseAmount;
  105 + v.aPath = Vector4(frandPath() - .5, frandPath() - .5, frandPath() - .5, frandPath() - .5) * mMotionScale;
  106 +
  107 + const float size = mSize * ((1.f + (frandSize() - .5) * mSizeVariance) * .5f);
  108 + for (int j = 0; j < numPatternVertices; ++j)
  109 + {
  110 + v.aSubPosition = vertexPattern[j];
  111 + v.aSize = size;
  112 + vertices.push_back(v);
  113 + }
  114 + }
  115 +
  116 + VertexBuffer vertexBuffer = VertexBuffer::New( Property::Map()
  117 + .Add( "aPosition", Property::VECTOR3 )
  118 + .Add( "aSeed", Property::FLOAT )
  119 + .Add( "aPath", Property::VECTOR4 )
  120 + .Add( "aSubPosition", Property::VECTOR2 )
  121 + .Add( "aSize", Property::FLOAT )
  122 + );
  123 + vertexBuffer.SetData( vertices.data(), vertices.size() );
  124 +
  125 + Geometry geometry = Geometry::New();
  126 + geometry.AddVertexBuffer( vertexBuffer );
  127 + geometry.SetType( Geometry::TRIANGLES );
  128 + return geometry;
  129 + }
  130 +};
  131 +
  132 +#endif //PARTICLES_PARTICLE_FIELD_H_
examples/particles/particle-view.cpp 0 → 100644
  1 +/*
  2 + * Copyright (c) 2020 Samsung Electronics Co., Ltd.
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *
  16 + */
  17 +#include "particle-view.h"
  18 +#include "utils.h"
  19 +#include "dali/public-api/animation/constraints.h"
  20 +
  21 +//#define ENABLE_DEBUG_VOLUME
  22 +
  23 +#define USE_GLSL_VERSION(version) "#version " #version "\n"
  24 +
  25 +using namespace Dali;
  26 +
  27 +namespace
  28 +{
  29 +
  30 +const uint32_t POPULATION_GRANULARITY = 128;
  31 +
  32 +///@brief Shader for billboarded particles, where the vertices of the particles
  33 +/// are supplied as vec3 position (particle position) + vec2 sub-position.
  34 +const char* const PARTICLES_VSH = USE_GLSL_VERSION(300 es)
  35 +DALI_COMPOSE_SHADER(
  36 + precision lowp float;
  37 + uniform mat4 uModelView; // DALi
  38 + uniform mat4 uProjection; // DALi
  39 + uniform vec3 uSize; // DALi
  40 + uniform vec4 uColor; // DALi
  41 +
  42 + uniform vec3 uSecondaryColor;
  43 + uniform vec2 uDepthRange; // x is zNear, y is 1.f / (zFar - zNear)
  44 + uniform float uTwinkleFrequency;
  45 + uniform float uTwinkleSizeScale;
  46 + uniform float uTwinkleOpacityWeight;
  47 + uniform float uTime;
  48 + uniform float uFocalLength;
  49 + uniform float uAperture;
  50 + uniform float uPopulation;
  51 +
  52 + struct Scatter
  53 + {
  54 + float radiusSqr;
  55 + float amount;
  56 + vec3 ray;
  57 + };
  58 +
  59 + const int SCATTER_VARS = 6; // Must match ParticleView::mScatterProps' size.
  60 + uniform Scatter uScatter[SCATTER_VARS];
  61 +
  62 + const int POPULATION_GRANULARITY = 128;
  63 + uniform float uOrderLookUp[POPULATION_GRANULARITY];
  64 +
  65 + in vec3 aPosition;
  66 + in float aSeed;
  67 + in vec4 aPath;
  68 + in vec2 aSubPosition;
  69 + in float aSize;
  70 +
  71 + flat out float vDepth;
  72 + flat out float vFocalDistance;
  73 + out vec2 vUvUnit;
  74 + flat out float vOpacity;
  75 + flat out vec3 vColor; // ignore alpha
  76 +
  77 + float bezier(vec3 control, float alpha)
  78 + {
  79 + return mix(mix(control.x, control.y, alpha), mix(control.y, control.z, alpha), alpha);
  80 + }
  81 +
  82 + void main() {
  83 + // Get random order from the look-up table, based on particle ID.
  84 + int particleId = gl_VertexID / 6;
  85 + float order = uOrderLookUp[particleId & (POPULATION_GRANULARITY - 1)];
  86 +
  87 + // Get twinkle scalar
  88 + float twinkle = sin(uTime * floor(uTwinkleFrequency * aSeed) + fract(aSeed * 1.17137));
  89 +
  90 + // Add Motion
  91 + float s = sin(uTime + aSeed) * .5f + .5f; // different phase for all
  92 + // NOTE: you'd think that taking the bezier() calls apart would save 4 mix() calls, since
  93 + // the mix()es (of xy / yz / zw / wx) are all calculated twice. It turns out that the MALI
  94 + // compiler is already doing this; leaving it as is for readability.
  95 + float bx0 = bezier(aPath.xyz, s);
  96 + float bx1 = bezier(aPath.zwx, s);
  97 + float by0 = bezier(aPath.yzw, s);
  98 + float by1 = bezier(aPath.wxy, s);
  99 + vec3 motion = vec3(mix(bx0, bx1, s), mix(by0, by1, s), 0.f);
  100 +
  101 + // Model to view position
  102 + vec3 position3 = aPosition * uSize + motion;
  103 +
  104 + vec4 position = uModelView * vec4(position3, 1.f);
  105 +
  106 + // Add scatter - calculated in view space, using view ray
  107 + vec3 normalizedPos = position.xyz / uSize;
  108 + for (int i = 0; i < SCATTER_VARS; ++i)
  109 + {
  110 + vec2 scatterDist = (normalizedPos - uScatter[i].ray * dot(uScatter[i].ray, normalizedPos)).xy;
  111 +
  112 + // NOTE: replacing the division with a multiplication (by inverse) oddly results in more instructions (MALI).
  113 + float scatter = max(0.f, uScatter[i].radiusSqr - dot(scatterDist, scatterDist)) *
  114 + uScatter[i].amount / aSize;
  115 + position.xy += scatter * normalize(scatterDist) * uSize.xy;
  116 + }
  117 +
  118 + // Calculate normalised depth and distance from focal plane
  119 + float depth = (position.z - uDepthRange.x) * uDepthRange.y;
  120 + vDepth = depth;
  121 +
  122 + float focalDist = (uFocalLength - depth) * uAperture;
  123 + focalDist *= focalDist;
  124 + vFocalDistance = max(focalDist, 1e-6f); // NOTE: was clamp(..., 1.f); side effect: out of focus particles get squashed at higher aperture values.
  125 +
  126 + // Calculate expiring scale - for size and opacity.
  127 + float expiringScale = smoothstep(order + 1.f, order, uPopulation);
  128 +
  129 + // Calculate billboard position and size
  130 + vec2 subPosition = aSubPosition * aSize *
  131 + (1.f + twinkle * aSeed * uTwinkleSizeScale) *
  132 + expiringScale;
  133 +
  134 + // Insist on hacking the size? Do it here...
  135 + float sizeHack = depth + .5f;
  136 + // NOTE: sizeHack *= sizeHack looked slightly better.
  137 + subPosition *= sizeHack;
  138 +
  139 + vec3 subPositionView = vec3(subPosition, 0.);
  140 +
  141 + // Add billboards to view position.
  142 + position += vec4(subPositionView, 0.f);
  143 +
  144 + // subPosition doubles as normalized (-1..1) UV.
  145 + vUvUnit = aSubPosition;
  146 +
  147 + // Vary opacity (actor alpha) by time as well as expiring scale.
  148 + vOpacity = uColor.a * expiringScale *
  149 + (1.0f + aSeed + twinkle * uTwinkleOpacityWeight) / (2.0f + uTwinkleOpacityWeight);
  150 +
  151 + // Randomize RGB using seed.
  152 + vec3 mixColor = vec3(fract(aSeed), fract(aSeed * 16.f), fract(aSeed * 256.f));
  153 + vColor = mix(uColor.rgb, uSecondaryColor, mixColor);
  154 +
  155 + gl_Position = uProjection * position;
  156 + });
  157 +
  158 +///@brief Fragment shader for particles, which simulates depth of field
  159 +/// using a combination of procedural texturing, alpha testing and alpha
  160 +/// blending.
  161 +const char* const PARTICLES_FSH = USE_GLSL_VERSION(300 es)
  162 +DALI_COMPOSE_SHADER(
  163 + precision lowp float;
  164 + uniform float uAlphaTestRefValue;
  165 + uniform vec2 uFadeRange; // near, far
  166 + in vec2 vUvUnit;
  167 + flat in float vDepth;
  168 + flat in float vFocalDistance;
  169 + flat in float vOpacity;
  170 + flat in vec3 vColor;
  171 + out vec4 oFragColor;
  172 +
  173 + const float REF_VALUE_THRESHOLD = 1. / 64.;
  174 +
  175 + void main() {
  176 + // Softened disc pattern from normalized UVs
  177 + float value = 1.f - dot(vUvUnit, vUvUnit);
  178 +
  179 + // Decrease area of particles 'in-focus'.
  180 + float refValue = (1.f - vFocalDistance) * .5f;
  181 + float threshold = REF_VALUE_THRESHOLD * (1.f + vDepth);
  182 + float alpha = pow(value, vFocalDistance) * smoothstep(refValue - threshold, refValue + threshold, value);
  183 + if (alpha < uAlphaTestRefValue)
  184 + {
  185 + discard;
  186 + }
  187 +
  188 + // Apply opacity
  189 + alpha *= vOpacity;
  190 + alpha *= alpha;
  191 +
  192 + // Fade particles out as they get close to the near and far clipping planes
  193 + alpha *= smoothstep(.0f, uFadeRange.x, vDepth) * smoothstep(1.f, uFadeRange.y, vDepth);
  194 +
  195 + oFragColor = vec4(vColor, alpha);
  196 + });
  197 +
  198 +///@brief Shader for simple textured geometry.
  199 +const char* const SIMPLE_VSH = USE_GLSL_VERSION(300 es)
  200 +DALI_COMPOSE_SHADER(
  201 + precision mediump float;
  202 + uniform mat4 uMvpMatrix;//by DALi
  203 + uniform vec3 uSize; // by DALi
  204 + in vec3 aPosition;
  205 + void main() {
  206 + gl_Position = uMvpMatrix * vec4(aPosition * uSize, 1.f);
  207 + });
  208 +
  209 +///@brief Shader for an unlit, unfogged, textured mesh.
  210 +const char* const SIMPLE_FSH = USE_GLSL_VERSION(300 es)
  211 +DALI_COMPOSE_SHADER(
  212 + precision mediump float;
  213 + uniform vec4 uColor;
  214 + out vec4 oFragColor;
  215 +
  216 + void main() {
  217 + oFragColor = uColor;
  218 + });
  219 +
  220 +
  221 +uint32_t GetSkipValue(uint32_t count, uint32_t prime)
  222 +{
  223 + uint32_t skip = 0;
  224 + do
  225 + {
  226 + skip = (rand() % prime) * count * count + (rand() % prime) * count + (rand() % prime);
  227 + }
  228 + while (skip % prime == 0);
  229 + return skip;
  230 +}
  231 +
  232 +}
  233 +
  234 +ParticleView::ParticleView(const ParticleField& field, Dali::Actor world, Dali::CameraActor camera,
  235 + Dali::Geometry particleGeom)
  236 +: mWorld(world),
  237 + mParticleBoxSize(field.mBoxSize)
  238 +{
  239 + if (!particleGeom)
  240 + {
  241 + // create particles
  242 + particleGeom = field.MakeGeometry();
  243 + }
  244 +
  245 + // create shader
  246 + Shader particleShader = Shader::New(PARTICLES_VSH, PARTICLES_FSH, Shader::Hint::MODIFIES_GEOMETRY);
  247 +
  248 + float zNear = camera.GetNearClippingPlane();
  249 + float zFar = camera.GetFarClippingPlane();
  250 + const Vector2 depthRange(zNear, 1.f / (zFar - zNear));
  251 + particleShader.RegisterProperty("uDepthRange", depthRange);
  252 +
  253 + particleShader.RegisterProperty("uTwinkleFrequency", field.mTwinkleFrequency);
  254 + particleShader.RegisterProperty("uTwinkleSizeScale", field.mTwinkleSizeScale);
  255 + particleShader.RegisterProperty("uTwinkleOpacityWeight", field.mTwinkleOpacityWeight);
  256 +
  257 + mPropPopulation = particleShader.RegisterProperty("uPopulation", 1.f);
  258 + mPropFocalLength = particleShader.RegisterProperty("uFocalLength", .5f);
  259 + mPropAperture = particleShader.RegisterProperty("uAperture", 8.f);
  260 + mPropAlphaTestRefValue = particleShader.RegisterProperty("uAlphaTestRefValue", 0.f);
  261 + mPropFadeRange = particleShader.RegisterProperty("uFadeRange", Vector2(0.f, 1.f));
  262 +
  263 + // scatter variables
  264 + char nameBuffer[64];
  265 + char* writep = nameBuffer + sprintf(nameBuffer, "uScatter[");
  266 + for (uint32_t i = 0; i < std::extent<decltype(mScatterProps)>::value; ++i)
  267 + {
  268 + char* writep2 = writep + sprintf(writep, "%d].", i);
  269 +
  270 + sprintf(writep2, "radiusSqr");
  271 + mScatterProps[i].mPropRadius = particleShader.RegisterProperty(nameBuffer, 0.f);
  272 +
  273 + sprintf(writep2, "amount");
  274 + mScatterProps[i].mPropAmount = particleShader.RegisterProperty(nameBuffer, 0.f);
  275 +
  276 + sprintf(writep2, "ray");
  277 + mScatterProps[i].mPropRay = particleShader.RegisterProperty(nameBuffer, Vector3::ZERO);
  278 + }
  279 +
  280 + // Create a look-up table for pseudo-random traversal of particles.
  281 + // Our particle mesh is sorted in Z; changing the population should remove
  282 + // particles "randomly", not from one end.
  283 + // Algorithm described in Mike McShaffry & al: Game Coding Complete.
  284 + const uint32_t prime = 131; // next prime after POPULATION_GRANULARITY
  285 + const uint32_t skip = GetSkipValue(POPULATION_GRANULARITY, prime);
  286 + uint32_t next = 0;
  287 +
  288 + writep = nameBuffer + sprintf(nameBuffer, "uOrderLookUp[");
  289 + for (uint32_t i = 0; i < POPULATION_GRANULARITY; ++i)
  290 + {
  291 + do {
  292 + next += skip;
  293 + next %= prime;
  294 + }
  295 + while (next == 0 || next > POPULATION_GRANULARITY);
  296 +
  297 + sprintf(writep, "%d]", i);
  298 + particleShader.RegisterProperty(nameBuffer, float(next - 1));
  299 + }
  300 +
  301 + // create animation for time in shader
  302 + auto propTime = particleShader.RegisterProperty("uTime", 0.f);
  303 +
  304 + Animation animTime = Animation::New(field.mMotionCycleLength);
  305 + animTime.AnimateTo(Property(particleShader, propTime), static_cast<float>(M_PI * 2.f));
  306 + animTime.SetLoopCount(0);
  307 + animTime.Play();
  308 +
  309 + mParticleShader = particleShader;
  310 +
  311 + auto renderer = CreateRenderer(TextureSet::New(), particleGeom, particleShader, OPTION_BLEND);
  312 + auto masterParticles = CreateActor();
  313 + masterParticles.SetProperty(Actor::Property::SIZE, field.mBoxSize);
  314 + masterParticles.SetProperty(Actor::Property::VISIBLE, true);
  315 + masterParticles.AddRenderer(renderer);
  316 +
  317 + mPropSecondaryColor = masterParticles.RegisterProperty("uSecondaryColor", Vector3::XAXIS);
  318 +
  319 +#ifdef ENABLE_DEBUG_VOLUME
  320 + Geometry cubeGeom = CreateCuboidWireframeGeometry();
  321 + renderer = CreateRenderer(renderer.GetTextures(), cubeGeom, Shader::New(SIMPLE_VSH, SIMPLE_FSH));
  322 + masterParticles.AddRenderer(renderer);
  323 +#endif
  324 +
  325 + world.Add(masterParticles);
  326 + mMasterParticles = masterParticles;
  327 +}
  328 +
  329 +ParticleView::~ParticleView()
  330 +{
  331 + UnparentAndReset(mMasterParticles);
  332 + UnparentAndReset(mSlaveParticles);
  333 +
  334 + for (auto anim: { mAngularAnim, mLinearAnim })
  335 + {
  336 + if (anim)
  337 + {
  338 + anim.Stop();
  339 + anim.Reset();
  340 + }
  341 + }
  342 +
  343 + for (auto& s: mScatterProps)
  344 + {
  345 + auto& anim = s.mAnim;
  346 + if (anim)
  347 + {
  348 + anim.Stop();
  349 + anim.Reset();
  350 + }
  351 + }
  352 +}
  353 +
  354 +void ParticleView::SetColorRange(const ColorRange& range)
  355 +{
  356 + mMasterParticles.SetProperty(Actor::Property::COLOR_RED, range.rgb0.r);
  357 + mMasterParticles.SetProperty(Actor::Property::COLOR_GREEN, range.rgb0.g);
  358 + mMasterParticles.SetProperty(Actor::Property::COLOR_BLUE, range.rgb0.b);
  359 +
  360 + mMasterParticles.SetProperty(mPropSecondaryColor, range.rgb1);
  361 +}
  362 +
  363 +void ParticleView::SetPopulation(float percentage)
  364 +{
  365 + percentage = 1.f - std::min(1.f, std::max(0.f, percentage));
  366 + mParticleShader.SetProperty(mPropPopulation, POPULATION_GRANULARITY * percentage);
  367 +}
  368 +
  369 +void ParticleView::SetFocalLength(float f)
  370 +{
  371 + mParticleShader.SetProperty(mPropFocalLength, f);
  372 +}
  373 +
  374 +void ParticleView::SetAperture(float a)
  375 +{
  376 + mParticleShader.SetProperty(mPropAperture, a);
  377 +}
  378 +
  379 +void ParticleView::SetAlphaTestRefValue(float rv)
  380 +{
  381 + mParticleShader.SetProperty(mPropAlphaTestRefValue, rv);
  382 +}
  383 +
  384 +void ParticleView::SetFadeRange(float near, float far)
  385 +{
  386 + mParticleShader.SetProperty(mPropFadeRange, Vector2(near, far));
  387 +}
  388 +
  389 +void ParticleView::SetAngularVelocity(float v)
  390 +{
  391 + if (mAngularAnim)
  392 + {
  393 + mAngularAnim.Stop();
  394 + mAngularAnim.Clear();
  395 + mAngularAnim.Reset();
  396 + }
  397 +
  398 + if (v * v > .0f)
  399 + {
  400 + float sign = Sign(v);
  401 + auto anim = Animation::New(std::abs(2. * M_PI / v));
  402 + anim.AnimateTo(Property(mMasterParticles, Actor::Property::ORIENTATION),
  403 + Quaternion(Radian(Degree(120. * sign)), Vector3::ZAXIS), TimePeriod(0., anim.GetDuration() / 3.));
  404 + anim.AnimateTo(Property(mMasterParticles, Actor::Property::ORIENTATION),
  405 + Quaternion(Radian(Degree(240. * sign)), Vector3::ZAXIS), TimePeriod(anim.GetDuration() / 3., anim.GetDuration() / 3.));
  406 + anim.AnimateTo(Property(mMasterParticles, Actor::Property::ORIENTATION),
  407 + Quaternion(Radian(Degree(360. * sign)), Vector3::ZAXIS), TimePeriod(2. * anim.GetDuration() / 3., anim.GetDuration() / 3.));
  408 + anim.SetLoopCount(0);
  409 + anim.Play();
  410 +
  411 + mAngularAnim = anim;
  412 + }
  413 +}
  414 +
  415 +void ParticleView::SetLinearVelocity(float v)
  416 +{
  417 + if (mLinearAnim)
  418 + {
  419 + mLinearAnim.Stop();
  420 + mLinearAnim.Clear();
  421 + mLinearAnim.Reset();
  422 + }
  423 + UnparentAndReset(mSlaveParticles);
  424 +
  425 + if (v * v > .0f)
  426 + {
  427 + float sign = Sign(v);
  428 + float directedSize = sign * mParticleBoxSize.z;
  429 +
  430 + Actor slaveParticles = CloneActor(mMasterParticles);
  431 + Vector3 position = mMasterParticles.GetCurrentProperty(Actor::Property::POSITION).Get<Vector3>();
  432 + slaveParticles.SetProperty(Actor::Property::POSITION, position + Vector3(0., 0., directedSize));
  433 +
  434 + auto propSecondaryColor = slaveParticles.RegisterProperty("uSecondaryColor", Vector3::XAXIS);
  435 +
  436 + Actor world = mWorld.GetHandle();
  437 + world.Add(slaveParticles);
  438 +
  439 + if (sign < 0.) // fix draw order
  440 + {
  441 + world.Remove(mMasterParticles);
  442 + world.Add(mMasterParticles);
  443 + }
  444 +
  445 + Constraint constraint = Constraint::New<Vector4>(slaveParticles, Actor::Property::COLOR,
  446 + EqualToConstraint());
  447 + constraint.AddSource(Source(mMasterParticles, Actor::Property::COLOR));
  448 + constraint.Apply();
  449 +
  450 + constraint = Constraint::New<Vector3>(slaveParticles, propSecondaryColor,
  451 + EqualToConstraint());
  452 + constraint.AddSource(Source(mMasterParticles, mPropSecondaryColor));
  453 + constraint.Apply();
  454 +
  455 + constraint = Constraint::New<Quaternion>(slaveParticles, Actor::Property::ORIENTATION,
  456 + EqualToConstraint());
  457 + constraint.AddSource(Source(mMasterParticles, Actor::Property::ORIENTATION));
  458 + constraint.Apply();
  459 +
  460 + auto anim = Animation::New(std::abs(directedSize / v));
  461 + anim.AnimateTo(Property(mMasterParticles, Actor::Property::POSITION_Z), position.z - directedSize);
  462 + anim.AnimateTo(Property(slaveParticles, Actor::Property::POSITION_Z), position.z);
  463 + anim.SetLoopCount(0);
  464 + anim.Play();
  465 +
  466 + mLinearAnim = anim;
  467 + mSlaveParticles = slaveParticles;
  468 + }
  469 +}
  470 +
  471 +void ParticleView::Scatter(float radius, float amount, float durationOut, float durationIn)
  472 +{
  473 + mActiveScatter = (mActiveScatter + 1) % std::extent<decltype(mScatterProps)>::value;
  474 +
  475 + auto& scatter = mScatterProps[mActiveScatter];
  476 + if (scatter.mAnim)
  477 + {
  478 + scatter.mAnim.Stop();
  479 + }
  480 +
  481 + radius /= mParticleBoxSize.y;
  482 + radius *= radius;
  483 + mParticleShader.SetProperty(scatter.mPropRadius, radius);
  484 +
  485 + Animation anim = Animation::New(durationOut + durationIn);
  486 + auto scatterAmount = Property(mParticleShader, scatter.mPropAmount);
  487 + anim.AnimateTo(scatterAmount, amount, AlphaFunction::EASE_OUT,
  488 + TimePeriod(0.f, durationOut));
  489 + anim.AnimateTo(scatterAmount, 0.f, AlphaFunction::EASE_IN_OUT_SINE,
  490 + TimePeriod(durationOut, durationIn));
  491 + anim.Play();
  492 +
  493 + scatter.mAnim = anim;
  494 +}
  495 +
  496 +void ParticleView::SetScatterRay(Dali::Vector3 rayDir)
  497 +{
  498 + auto& scatter = mScatterProps[mActiveScatter];
  499 + mParticleShader.SetProperty(scatter.mPropRay, rayDir);;
  500 +}
  501 +
  502 +void ParticleView::Fade(float duration, float target, AlphaFunction alphaFn,
  503 + std::function<void(Dali::Animation&)> onFinished)
  504 +{
  505 + if (mFadeAnim)
  506 + {
  507 + mFadeAnim.Stop();
  508 + }
  509 +
  510 + Animation anim = Animation::New(duration);
  511 + anim.AnimateTo(Property(mMasterParticles, Actor::Property::COLOR_ALPHA), target, alphaFn);
  512 + if (mSlaveParticles)
  513 + {
  514 + anim.AnimateTo(Property(mSlaveParticles, Actor::Property::COLOR_ALPHA), target, alphaFn);
  515 + }
  516 +
  517 + if (onFinished)
  518 + {
  519 + anim.FinishedSignal().Connect(this, onFinished);
  520 + }
  521 + anim.Play();
  522 +
  523 + mFadeAnim = anim;
  524 +}
  525 +
  526 +void ParticleView::Fade(float duration, float target, float from, AlphaFunction alphaFn,
  527 + std::function<void(Dali::Animation&)> onFinished)
  528 +{
  529 + mMasterParticles.SetProperty(Actor::Property::COLOR_ALPHA, from);
  530 + if (mSlaveParticles)
  531 + {
  532 + mSlaveParticles.SetProperty(Actor::Property::COLOR_ALPHA, from);
  533 + }
  534 +
  535 + Fade(duration, target, alphaFn, onFinished);
  536 +}
examples/particles/particle-view.h 0 → 100644
  1 +#ifndef PARTICLES_PARTICLE_VIEW_H_
  2 +#define PARTICLES_PARTICLE_VIEW_H_
  3 +/*
  4 + * Copyright (c) 2020 Samsung Electronics Co., Ltd.
  5 + *
  6 + * Licensed under the Apache License, Version 2.0 (the "License");
  7 + * you may not use this file except in compliance with the License.
  8 + * You may obtain a copy of the License at
  9 + *
  10 + * http://www.apache.org/licenses/LICENSE-2.0
  11 + *
  12 + * Unless required by applicable law or agreed to in writing, software
  13 + * distributed under the License is distributed on an "AS IS" BASIS,
  14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15 + * See the License for the specific language governing permissions and
  16 + * limitations under the License.
  17 + *
  18 + */
  19 +
  20 +#include "particle-field.h"
  21 +#include "dali/public-api/actors/camera-actor.h"
  22 +#include "dali/public-api/animation/animation.h"
  23 +#include "dali/public-api/object/weak-handle.h"
  24 +#include "dali/public-api/rendering/shader.h"
  25 +#include <vector>
  26 +#include <functional>
  27 +
  28 +struct ColorRange
  29 +{
  30 + Dali::Vector3 rgb0;
  31 + Dali::Vector3 rgb1;
  32 +};
  33 +
  34 +class ParticleView: public Dali::ConnectionTracker
  35 +{
  36 +public:
  37 + ParticleView(const ParticleField& field, Dali::Actor world, Dali::CameraActor camera,
  38 + Dali::Geometry particleGeom = Dali::Geometry());
  39 + ~ParticleView();
  40 +
  41 + void SetColorRange(const ColorRange& range);
  42 +
  43 + void SetPopulation(float percentage);
  44 + void SetFocalLength(float f);
  45 + void SetAperture(float a);
  46 + void SetAlphaTestRefValue(float rv);
  47 + void SetFadeRange(float near, float far);
  48 + void SetAngularVelocity(float v);
  49 + void SetLinearVelocity(float v);
  50 +
  51 + ///@brief Starts a scatter & regroup animation, cancelling any previously played animation
  52 + /// of the same kind. Bigger particles, and those further away from the Z axis are affected
  53 + /// less.
  54 + ///@param radius the normalised radius, within which particles are affected.
  55 + ///@param amount the amount of displacement applied to particles at the peak of the animation.
  56 + ///@param durationOut the duration of scattering, in seconds.
  57 + ///@param durationIn the duration of regrouping, in seconds.
  58 + void Scatter(float radius, float amount, float durationOut, float durationIn);
  59 +
  60 + void SetScatterRay(Dali::Vector3 rayDir);
  61 +
  62 + ///@brief Starts an animation to change the opacity of the particles to @a target.
  63 + ///@param duration Number of seconds to complete transition in.
  64 + ///@param target Target opacity in the 0..1.f range.
  65 + void Fade(float duration, float target, Dali::AlphaFunction alphaFn = Dali::AlphaFunction::DEFAULT,
  66 + std::function<void(Dali::Animation&)> onFinished = nullptr);
  67 +
  68 + ///@brief Starts an animation to change the opacity of the particles to @a target.
  69 + ///@param duration Number of seconds to complete transition in.
  70 + ///@param target Target opacity in the 0..1.f range.
  71 + ///@param from The value to set the opacity to prior to the animation.
  72 + void Fade(float duration, float target, float from, Dali::AlphaFunction alphaFn = Dali::AlphaFunction::DEFAULT,
  73 + std::function<void(Dali::Animation&)> onFinished = nullptr);
  74 +
  75 +private: // DATA
  76 + struct ScatterProps
  77 + {
  78 + Dali::Property::Index mPropRadius;
  79 + Dali::Property::Index mPropAmount;
  80 + Dali::Property::Index mPropRay;
  81 +
  82 + Dali::Animation mAnim;
  83 + };
  84 +
  85 + Dali::WeakHandle<Dali::Actor> mWorld;
  86 + Dali::Vector3 mParticleBoxSize;
  87 +
  88 + Dali::Shader mParticleShader;
  89 + Dali::Property::Index mPropPopulation;
  90 + Dali::Property::Index mPropFocalLength;
  91 + Dali::Property::Index mPropAperture;
  92 + Dali::Property::Index mPropAlphaTestRefValue;
  93 + Dali::Property::Index mPropFadeRange;
  94 +
  95 + ScatterProps mScatterProps[6];
  96 + int mActiveScatter = 0;
  97 +
  98 + Dali::Actor mMasterParticles;
  99 + Dali::Property::Index mPropSecondaryColor;
  100 +
  101 + Dali::Actor mSlaveParticles;
  102 +
  103 + Dali::Animation mAngularAnim;
  104 + Dali::Animation mLinearAnim;
  105 + Dali::Animation mFadeAnim;
  106 +};
  107 +
  108 +#endif //PARTICLES_PARTICLE_VIEW_H_
examples/particles/particles-example.cpp 0 → 100644
  1 +/*
  2 + * Copyright (c) 2018 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 +// INTERNAL INCLUDES
  19 +#include "particle-view.h"
  20 +#include "float-rand.h"
  21 +#include "particle-field.h"
  22 +#include "utils.h"
  23 +#include "dali/devel-api/adaptor-framework/tilt-sensor.h"
  24 +#include "dali/public-api/adaptor-framework/application.h"
  25 +#include "dali/public-api/adaptor-framework/key.h"
  26 +#include "dali/public-api/actors/camera-actor.h"
  27 +#include "dali/public-api/actors/layer.h"
  28 +#include "dali/public-api/events/tap-gesture-detector.h"
  29 +#include "dali/public-api/events/pan-gesture-detector.h"
  30 +#include "dali/public-api/events/touch-event.h"
  31 +#include "dali/public-api/render-tasks/render-task-list.h"
  32 +#include "dali/public-api/render-tasks/render-task.h"
  33 +#include "dali/public-api/object/property-index-ranges.h"
  34 +#include <fstream>
  35 +#include <iostream>
  36 +#include <numeric>
  37 +#include <list>
  38 +#include <memory>
  39 +#include <random>
  40 +
  41 +using namespace Dali;
  42 +
  43 +namespace
  44 +{
  45 +
  46 +const float PARTICLE_ALPHA = .75;
  47 +const float ALPHA_TEST_REF_VALUE = .0625;
  48 +
  49 +const float NEAR_FADE = 0.04; // normalized depth
  50 +const float FAR_FADE = 0.8; // normalized depth
  51 +
  52 +const ParticleField PARTICLE_FIELD = {
  53 + 200.f, // particle size
  54 + Vector3(2500., 2500., 7800.), // box size - depth needs to be >= camera depth range
  55 + Vector3(6., 6., 12.), // number of particles
  56 + .333, // size variance
  57 + 1., // noise amount
  58 + 200., // disperse
  59 + 250., // motion scale
  60 + 15., // motion cycle length
  61 + 6., // twinkle frequency
  62 + .11, // twinkle size scale
  63 + .333 // twinkle opacity weight
  64 +};
  65 +
  66 +const float WORLD_ANGULAR_VELOCITY = .08; // radians per seconds
  67 +const float WORLD_LINEAR_VELOCITY = -500; // units along z
  68 +
  69 +const float SCATTER_RADIUS = 450.0f;
  70 +const float SCATTER_AMOUNT = 180.0f;
  71 +const float SCATTER_DURATION_OUT = .8f;
  72 +const float SCATTER_DURATION_IN = 1.5f;
  73 +
  74 +const float FADE_DURATION = 1.2f;
  75 +const float FADEOUT_SPEED_MULTIPLIER = 4.f; // speed multiplier upon fading out.
  76 +
  77 +const float FOCAL_LENGTH = 0.5f; // normalized depth value where particles appear sharp.
  78 +const float APERTURE = 2.2f; // distance scale - the higher it is, the quicker the particles get blurred out moving away from the focal length.
  79 +
  80 +const ColorRange DEFAULT_COLOR_RANGE { Vector3(0., 48. / 255., 1.), Vector3(0., 216. / 255., 1.) };
  81 +
  82 +const float TILT_SCALE = 0.2;
  83 +const float TILT_RANGE_DEGREES = 30.f;
  84 +
  85 +FloatRand sFloatRand;
  86 +
  87 +class TiltFilter
  88 +{
  89 +public:
  90 + void Reset()
  91 + {
  92 + std::fill(mTiltSamples, mTiltSamples + FILTER_SIZE, Vector2(.0f, .0f));
  93 + }
  94 +
  95 + void Add(Dali::Vector2 tilt)
  96 + {
  97 + mTiltSamples[mIdxNextSample] = tilt;
  98 + mIdxNextSample = (mIdxNextSample + 1) % FILTER_SIZE;
  99 + }
  100 +
  101 + Dali::Vector2 Filter() const
  102 + {
  103 + return std::accumulate(mTiltSamples, mTiltSamples + FILTER_SIZE, Vector2(.0f, .0f)) / FILTER_SIZE;
  104 + }
  105 +
  106 +private:
  107 + enum { FILTER_SIZE = 8u };
  108 +
  109 + Dali::Vector2 mTiltSamples[FILTER_SIZE];
  110 + size_t mIdxNextSample = 0;
  111 +};
  112 +
  113 +class ParticlesExample : public ConnectionTracker
  114 +{
  115 +public:
  116 + ParticlesExample( Application& app )
  117 + : mApp( app )
  118 + {
  119 + mApp.InitSignal().Connect(this, &ParticlesExample::OnInit);
  120 + mApp.TerminateSignal().Connect(this, &ParticlesExample::OnTerminate);
  121 + }
  122 +
  123 + ~ParticlesExample() = default;
  124 +
  125 +private:
  126 + Application& mApp;
  127 +
  128 + CameraActor mCamera;
  129 +
  130 + Actor mWorld;
  131 + Vector2 mAngularPosition;
  132 + ColorRange mColors;
  133 +
  134 + std::unique_ptr<ParticleView> mParticles;
  135 + std::unique_ptr<ParticleView> mExpiringParticles;
  136 +
  137 + TapGestureDetector mDoubleTapGesture;
  138 +
  139 + TiltSensor mTiltSensor;
  140 + TiltFilter mTiltFilter;
  141 +
  142 + PanGestureDetector mPanGesture;
  143 +
  144 + void OnInit( Application& application )
  145 + {
  146 + Window window = application.GetWindow();
  147 + auto rootLayer = window.GetRootLayer();
  148 + rootLayer.SetProperty(Layer::Property::BEHAVIOR, Layer::Behavior::LAYER_3D);
  149 +
  150 + window.KeyEventSignal().Connect( this, &ParticlesExample::OnKeyEvent );
  151 + window.GetRootLayer().TouchedSignal().Connect( this, &ParticlesExample::OnTouched );
  152 +
  153 + auto tiltSensor = TiltSensor::Get();
  154 + if ( tiltSensor.Start() )
  155 + {
  156 + // Get notifications when the device is tilted
  157 + tiltSensor.TiltedSignal().Connect( this, &ParticlesExample::OnTilted );
  158 + }
  159 + else
  160 + {
  161 + mPanGesture = PanGestureDetector::New();
  162 + mPanGesture.Attach(rootLayer);
  163 + mPanGesture.DetectedSignal().Connect(this, &ParticlesExample::OnPan);
  164 + }
  165 +
  166 + // Get camera
  167 + RenderTaskList tasks = window.GetRenderTaskList();
  168 + RenderTask mainPass = tasks.GetTask(0);
  169 + CameraActor camera = mainPass.GetCameraActor();
  170 + mCamera = camera;
  171 +
  172 + // Create world - particles and clock are added to it; this is what we apply tilt to.
  173 + auto world = CreateActor();
  174 + world.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
  175 + window.Add(world);
  176 + mWorld = world;
  177 +
  178 + // Create particles
  179 + TriggerColorTransition(DEFAULT_COLOR_RANGE);
  180 +
  181 + // Setup double tap detector for color change
  182 + mDoubleTapGesture = TapGestureDetector::New(2);
  183 + mDoubleTapGesture.Attach(rootLayer);
  184 + mDoubleTapGesture.DetectedSignal().Connect(this, &ParticlesExample::OnDoubleTap);
  185 + }
  186 +
  187 + void OnTerminate(Application& app)
  188 + {
  189 + UnparentAndReset(mWorld);
  190 +
  191 + mDoubleTapGesture.Reset();
  192 + mPanGesture.Reset();
  193 + mTiltSensor.Reset();
  194 + }
  195 +
  196 + void OnPause(Application& app)
  197 + {
  198 + mTiltSensor.Stop();
  199 + }
  200 +
  201 + void OnResume(Application& app)
  202 + {
  203 + mTiltSensor.Start();
  204 + }
  205 +
  206 + void OnKeyEvent(const KeyEvent& event)
  207 + {
  208 + if ( event.GetState() == KeyEvent::UP) // single keystrokes
  209 + {
  210 + if( IsKey( event, DALI_KEY_ESCAPE ) || IsKey( event, DALI_KEY_BACK ) )
  211 + {
  212 + mApp.Quit();
  213 + }
  214 + }
  215 + }
  216 +
  217 + bool OnTouched( Actor a, const TouchEvent& event )
  218 + {
  219 + if (event.GetPointCount() > 0)
  220 + {
  221 + auto screenPos = event.GetScreenPosition(0);
  222 + switch (event.GetState(0))
  223 + {
  224 + case PointState::STARTED:
  225 + {
  226 + mParticles->Scatter(SCATTER_RADIUS, SCATTER_AMOUNT, SCATTER_DURATION_OUT, SCATTER_DURATION_IN);
  227 +
  228 + Vector3 ray = GetViewRay(screenPos);
  229 + mParticles->SetScatterRay(ray);
  230 + }
  231 + break;
  232 +
  233 + default:
  234 + break;
  235 + }
  236 + }
  237 +
  238 + return false;
  239 + }
  240 +
  241 + void OnDoubleTap(Actor /*actor*/, const TapGesture& /*gesture*/)
  242 + {
  243 + if (!mExpiringParticles)
  244 + {
  245 + mColors.rgb0 = Vector3::ONE - mColors.rgb1;
  246 + mColors.rgb1 = FromHueSaturationLightness(Vector3(sFloatRand() * 360.f, sFloatRand() * .5f + .5f, sFloatRand() * .25 + .75f));
  247 +
  248 + TriggerColorTransition(mColors);
  249 + }
  250 + }
  251 +
  252 + void OnPan(Actor actor, const PanGesture& gesture)
  253 + {
  254 + auto tilt = gesture.GetDisplacement() / Vector2(mApp.GetWindow().GetSize()) * TILT_SCALE;
  255 + Quaternion q(Radian(-tilt.y), Radian(tilt.x), Radian(0.f));
  256 + Quaternion q0 = mWorld.GetProperty(Actor::Property::ORIENTATION).Get<Quaternion>();
  257 + mWorld.SetProperty(Actor::Property::ORIENTATION, q * q0);
  258 + }
  259 +
  260 + void OnTilted( const TiltSensor& sensor)
  261 + {
  262 + mTiltFilter.Add(Vector2(sensor.GetPitch(), sensor.GetRoll()));
  263 + Vector2 tilt = mTiltFilter.Filter() * TILT_RANGE_DEGREES;
  264 + Quaternion q(Radian(Degree(tilt.x)), Radian(Degree(tilt.y)), Radian(0.f));
  265 + mWorld.SetProperty(Actor::Property::ORIENTATION, q);
  266 + }
  267 +
  268 + Vector3 GetViewRay(const Vector2& screenPos)
  269 + {
  270 + Vector2 screenSize = mApp.GetWindow().GetSize();
  271 + Vector2 normScreenPos = (screenPos / screenSize) * 2.f - Vector2::ONE;
  272 +
  273 + const float fov = mCamera.GetProperty(CameraActor::Property::FIELD_OF_VIEW).Get<float>();
  274 + const float tanFov = std::tan(fov);
  275 +
  276 + const float zNear = mCamera.GetProperty(CameraActor::Property::NEAR_PLANE_DISTANCE).Get<float>();
  277 + const float hProj = zNear * tanFov;
  278 +
  279 + const float aspectRatio = mCamera.GetProperty(CameraActor::Property::ASPECT_RATIO).Get<float>();
  280 + const float wProj = hProj * aspectRatio;
  281 +
  282 + // Get camera orientation for view space ray casting. Assume:
  283 + // - this to be world space, i.e. no parent transforms;
  284 + // - no scaling;
  285 + Quaternion cameraOrientation = mCamera.GetProperty(Actor::Property::ORIENTATION).Get<Quaternion>();
  286 +
  287 + Matrix worldCamera;
  288 + worldCamera.SetTransformComponents(Vector3::ONE, cameraOrientation, Vector3::ZERO);
  289 +
  290 + float* data = worldCamera.AsFloat();
  291 + Vector3 xWorldCamera(data[0], data[4], data[8]);
  292 + xWorldCamera *= wProj * normScreenPos.x / xWorldCamera.Length();
  293 +
  294 + Vector3 yWorldCamera(data[1], data[5], data[9]);
  295 + yWorldCamera *= hProj * normScreenPos.y / yWorldCamera.Length();
  296 +
  297 + Vector3 zWorldCamera(data[2], data[6], data[10]);
  298 + Vector3 ray = xWorldCamera + yWorldCamera + zWorldCamera * -zNear;
  299 + ray.Normalize();
  300 +
  301 + return ray;
  302 + }
  303 +
  304 + void TriggerColorTransition(const ColorRange& range)
  305 + {
  306 + if (mParticles)
  307 + {
  308 + mExpiringParticles = std::move(mParticles);
  309 +
  310 + // NOTE: this will break the perfect looping, but we can get away with it because we're fading out.
  311 + mExpiringParticles->SetLinearVelocity(WORLD_LINEAR_VELOCITY * FADEOUT_SPEED_MULTIPLIER);
  312 +
  313 + auto& p = mExpiringParticles;
  314 + mExpiringParticles->Fade(FADE_DURATION, 0.f, AlphaFunction::EASE_IN, [&p](Animation&) {
  315 + p.reset();
  316 + });
  317 + }
  318 +
  319 + mParticles.reset(new ParticleView(PARTICLE_FIELD, mWorld, mCamera));
  320 + mParticles->SetColorRange(range);
  321 + mParticles->SetFocalLength(FOCAL_LENGTH);
  322 + mParticles->SetAperture(APERTURE);
  323 + mParticles->SetAlphaTestRefValue(ALPHA_TEST_REF_VALUE);
  324 + mParticles->SetFadeRange(NEAR_FADE, FAR_FADE);
  325 + mParticles->SetAngularVelocity(WORLD_ANGULAR_VELOCITY);
  326 + mParticles->SetLinearVelocity(WORLD_LINEAR_VELOCITY);
  327 + mParticles->Fade(FADE_DURATION, PARTICLE_ALPHA, 0.f, AlphaFunction::EASE_OUT);
  328 + }
  329 +};
  330 +
  331 +} // nonamespace
  332 +
  333 +int DALI_EXPORT_API main( int argc, char **argv )
  334 +{
  335 + Application application = Application::New( &argc, &argv, DEMO_THEME_PATH );
  336 + ParticlesExample example( application);
  337 + application.MainLoop();
  338 + return 0;
  339 +}
examples/particles/utils.cpp 0 → 100644
  1 +/*
  2 + * Copyright (c) 2020 Samsung Electronics Co., Ltd.
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *
  16 + */
  17 +#include "utils.h"
  18 +
  19 +using namespace Dali;
  20 +
  21 +Vector3 ToHueSaturationLightness(Vector3 rgb)
  22 +{
  23 + float min = std::min(rgb.r, std::min(rgb.g, rgb.b));
  24 + float max = std::max(rgb.r, std::max(rgb.g, rgb.b));
  25 +
  26 + Vector3 hsl(max - min, 0.f, (max + min) * .5f);
  27 + if (hsl.x * hsl.x > .0f)
  28 + {
  29 + hsl.y = hsl.x / max;
  30 + if (max == rgb.r)
  31 + {
  32 + hsl.x = (rgb.g - rgb.b) / hsl.x;
  33 + }
  34 + else if(max == rgb.g)
  35 + {
  36 + hsl.x = 2.f + (rgb.b - rgb.r) / hsl.x;
  37 + }
  38 + else
  39 + {
  40 + hsl.x = 4.f + (rgb.r - rgb.g) / hsl.x;
  41 + }
  42 + hsl.x *= 60.f;
  43 + if (hsl.x < 0.f)
  44 + {
  45 + hsl.x += 360.f;
  46 + }
  47 + }
  48 + else
  49 + {
  50 + hsl.y = 0.f;
  51 + }
  52 +
  53 + return hsl;
  54 +}
  55 +
  56 +Vector3 FromHueSaturationLightness(Vector3 hsl)
  57 +{
  58 + Vector3 rgb;
  59 + if (hsl.y * hsl.y > 0.f)
  60 + {
  61 + if(hsl.x >= 360.f)
  62 + {
  63 + hsl.x -= 360.f;
  64 + }
  65 + hsl.x /= 60.f;
  66 +
  67 + int i = FastFloor(hsl.x);
  68 + float ff = hsl.x - i;
  69 + float p = hsl.z * (1.0 - hsl.y);
  70 + float q = hsl.z * (1.0 - (hsl.y * ff));
  71 + float t = hsl.z * (1.0 - (hsl.y * (1.f - ff)));
  72 +
  73 + switch (i)
  74 + {
  75 + case 0:
  76 + rgb.r = hsl.z;
  77 + rgb.g = t;
  78 + rgb.b = p;
  79 + break;
  80 +
  81 + case 1:
  82 + rgb.r = q;
  83 + rgb.g = hsl.z;
  84 + rgb.b = p;
  85 + break;
  86 +
  87 + case 2:
  88 + rgb.r = p;
  89 + rgb.g = hsl.z;
  90 + rgb.b = t;
  91 + break;
  92 +
  93 + case 3:
  94 + rgb.r = p;
  95 + rgb.g = q;
  96 + rgb.b = hsl.z;
  97 + break;
  98 +
  99 + case 4:
  100 + rgb.r = t;
  101 + rgb.g = p;
  102 + rgb.b = hsl.z;
  103 + break;
  104 +
  105 + case 5:
  106 + default:
  107 + rgb.r = hsl.z;
  108 + rgb.g = p;
  109 + rgb.b = q;
  110 + break;
  111 + }
  112 + }
  113 + else
  114 + {
  115 + rgb = Vector3::ONE * hsl.z;
  116 + }
  117 +
  118 + return rgb;
  119 +}
  120 +
  121 +Geometry CreateCuboidWireframeGeometry()
  122 +{
  123 +//
  124 +// 2---3
  125 +// |- |-
  126 +// | 6---7
  127 +// | | | |
  128 +// 0-|-1 |
  129 +// -| -|
  130 +// 4---5
  131 +//
  132 + Vector3 vertexData[] = {
  133 + Vector3(-.5, -.5, -.5),
  134 + Vector3( .5, -.5, -.5),
  135 + Vector3(-.5, .5, -.5),
  136 + Vector3( .5, .5, -.5),
  137 + Vector3(-.5, -.5, .5),
  138 + Vector3( .5, -.5, .5),
  139 + Vector3(-.5, .5, .5),
  140 + Vector3( .5, .5, .5),
  141 + };
  142 +
  143 + uint16_t indices[] = {
  144 + 0, 1, 1, 3, 3, 2, 2, 0,
  145 + 0, 4, 1, 5, 3, 7, 2, 6,
  146 + 4, 5, 5, 7, 7, 6, 6, 4
  147 + };
  148 + VertexBuffer vertexBuffer = VertexBuffer::New( Property::Map()
  149 + .Add( "aPosition", Property::VECTOR3));
  150 + vertexBuffer.SetData(vertexData, std::extent<decltype(vertexData)>::value );
  151 +
  152 + Geometry geometry = Geometry::New();
  153 + geometry.AddVertexBuffer( vertexBuffer );
  154 + geometry.SetIndexBuffer( indices, std::extent<decltype(indices)>::value );
  155 + geometry.SetType(Geometry::LINES);
  156 + return geometry;
  157 +}
  158 +
  159 +Renderer CreateRenderer(TextureSet textures, Geometry geometry, Shader shader, uint32_t options)
  160 +{
  161 + Renderer renderer = Renderer::New(geometry, shader);
  162 + renderer.SetProperty(Renderer::Property::BLEND_MODE,
  163 + (options & OPTION_BLEND) ? BlendMode::ON : BlendMode::OFF);
  164 + renderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE,
  165 + (options & OPTION_DEPTH_TEST) ? DepthTestMode::ON : DepthTestMode::OFF);
  166 + renderer.SetProperty(Renderer::Property::DEPTH_WRITE_MODE,
  167 + (options & OPTION_DEPTH_WRITE) ? DepthWriteMode::ON : DepthWriteMode::OFF);
  168 + renderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);
  169 +
  170 + if (!textures)
  171 + {
  172 + textures = TextureSet::New();
  173 + }
  174 +
  175 + renderer.SetTextures(textures);
  176 + return renderer;
  177 +}
  178 +
  179 +void CenterActor(Actor actor)
  180 +{
  181 + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
  182 + actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
  183 +}
  184 +
  185 +Actor CreateActor()
  186 +{
  187 + auto actor = Actor::New();
  188 + CenterActor(actor);
  189 + return actor;
  190 +}
  191 +
  192 +Renderer CloneRenderer(Renderer original)
  193 +{
  194 + Geometry geom = original.GetGeometry();
  195 + Shader shader = original.GetShader();
  196 + Renderer clone = Renderer::New(geom, shader);
  197 +
  198 + // Copy properties.
  199 + Property::IndexContainer indices;
  200 + original.GetPropertyIndices(indices);
  201 +
  202 + for (auto& i: indices)
  203 + {
  204 + auto actualIndex = PropertyRanges::DEFAULT_RENDERER_PROPERTY_START_INDEX + i;
  205 + clone.SetProperty(actualIndex, original.GetProperty(actualIndex));
  206 + }
  207 +
  208 + // Copy texture references (and create TextureSet, if there's any textures).
  209 + TextureSet ts = original.GetTextures();
  210 + clone.SetTextures(ts);
  211 +
  212 + return clone;
  213 +}
  214 +
  215 +Actor CloneActor(Actor original)
  216 +{
  217 + using namespace Dali;
  218 +
  219 + auto clone = Actor::New();
  220 + clone.SetProperty(Actor::Property::NAME, original.GetProperty(Actor::Property::NAME));
  221 +
  222 + // Copy properties.
  223 + // Don't copy every single one of them.
  224 + // Definitely don't copy resize policy related things, which will internally enable
  225 + // relayout, which in turn will result in losing the ability to set Z size.
  226 + for (auto i : {
  227 + Actor::Property::PARENT_ORIGIN,
  228 + Actor::Property::ANCHOR_POINT,
  229 + Actor::Property::SIZE,
  230 + Actor::Property::POSITION,
  231 + Actor::Property::ORIENTATION,
  232 + Actor::Property::SCALE,
  233 + Actor::Property::VISIBLE,
  234 + Actor::Property::COLOR,
  235 + Actor::Property::NAME,
  236 + })
  237 + {
  238 + clone.SetProperty(i, original.GetProperty(i));
  239 + }
  240 +
  241 + // Clone renderers.
  242 + for(unsigned int i = 0; i < original.GetRendererCount(); ++i)
  243 + {
  244 + auto rClone = CloneRenderer(original.GetRendererAt(i));
  245 + clone.AddRenderer(rClone);
  246 + }
  247 +
  248 + // Recurse into children.
  249 + for(unsigned int i = 0; i < original.GetChildCount(); ++i)
  250 + {
  251 + Actor newChild = CloneActor(original.GetChildAt(i));
  252 + clone.Add(newChild);
  253 + }
  254 +
  255 + return clone;
  256 +}
examples/particles/utils.h 0 → 100644
  1 +#ifndef PARTICLES_UTILS_H_
  2 +#define PARTICLES_UTILS_H_
  3 +/*
  4 + * Copyright (c) 2020 Samsung Electronics Co., Ltd.
  5 + *
  6 + * Licensed under the Apache License, Version 2.0 (the "License");
  7 + * you may not use this file except in compliance with the License.
  8 + * You may obtain a copy of the License at
  9 + *
  10 + * http://www.apache.org/licenses/LICENSE-2.0
  11 + *
  12 + * Unless required by applicable law or agreed to in writing, software
  13 + * distributed under the License is distributed on an "AS IS" BASIS,
  14 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15 + * See the License for the specific language governing permissions and
  16 + * limitations under the License.
  17 + *
  18 + */
  19 +
  20 +#include "dali/public-api/actors/actor.h"
  21 +#include "dali/public-api/actors/actor.h"
  22 +#include "dali/public-api/rendering/geometry.h"
  23 +#include "dali/public-api/rendering/renderer.h"
  24 +#include "dali/public-api/rendering/shader.h"
  25 +#include "dali/public-api/rendering/texture.h"
  26 +#include "dali/public-api/math/vector3.h"
  27 +#include <cmath>
  28 +
  29 +//
  30 +// Maths
  31 +//
  32 +inline
  33 +float FastFloor(float x)
  34 +{
  35 + return static_cast<int>(x) - static_cast<int>(x < 0);
  36 +}
  37 +
  38 +///@brief Converts RGB values (in the 0..1 range) to HSL, where hue is in degrees,
  39 +/// in the 0..360 range, and saturation and lightness are in the 0..1 range.
  40 +Dali::Vector3 ToHueSaturationLightness(Dali::Vector3 rgb);
  41 +
  42 +///@brief Converts HSL values, where hue is in degrees, in the 0..360 range, and
  43 +/// saturation and lightness are in 0..1 to RGB (in the 0..1 range)
  44 +Dali::Vector3 FromHueSaturationLightness(Dali::Vector3 hsl);
  45 +
  46 +//
  47 +// Dali entities
  48 +//
  49 +///@brief Creates geometry for a unit cube wireframe (i.e. vertex positions between
  50 +/// -.5 and .5)..
  51 +Dali::Geometry CreateCuboidWireframeGeometry();
  52 +
  53 +enum RendererOptions
  54 +{
  55 + OPTION_NONE = 0x0,
  56 + OPTION_BLEND = 0x01,
  57 + OPTION_DEPTH_TEST = 0x02,
  58 + OPTION_DEPTH_WRITE = 0x04
  59 +};
  60 +
  61 +///@brief Creates a renderer with the given @a textures set, @a geometry, @a shader
  62 +/// and @a options from above.
  63 +///@note Back face culling is on.
  64 +///@note If textures is not a valid handle, an empty texture set will be created.
  65 +Dali::Renderer CreateRenderer(Dali::TextureSet textures, Dali::Geometry geometry,
  66 + Dali::Shader shader, uint32_t options = OPTION_NONE);
  67 +
  68 +///@brief Sets @a actor's anchor point and parent origin to center.
  69 +void CenterActor(Dali::Actor actor);
  70 +
  71 +///@brief Creates an empty and centered actor.
  72 +Dali::Actor CreateActor();
  73 +
  74 +///@brief Creates a copy of @a original, sharing the same geometry and shader and
  75 +/// copying each properties.
  76 +///@note Breaks if @a original has any custom properties. TODO: fix.
  77 +Dali::Renderer CloneRenderer(Dali::Renderer original);
  78 +
  79 +///@brief Creates a copy of @a original, cloning each renderer, and a select set
  80 +/// of properties: parent origin, anchor point, size, position, orientation, scale,
  81 +/// visible, color and name.
  82 +///@note Does not copy resize policy related properties, as setting those, even if
  83 +/// default, will break the ability to specify a size for the actor in Z.
  84 +Dali::Actor CloneActor(Dali::Actor original);
  85 +
  86 +#endif //PARTICLES_UTILS_H_
resources/po/en_GB.po
@@ -151,6 +151,9 @@ msgstr &quot;Negotiate Size&quot; @@ -151,6 +151,9 @@ msgstr &quot;Negotiate Size&quot;
151 msgid "DALI_DEMO_STR_TITLE_PAGE_TURN" 151 msgid "DALI_DEMO_STR_TITLE_PAGE_TURN"
152 msgstr "Page Turn" 152 msgstr "Page Turn"
153 153
  154 +msgid "DALI_DEMO_STR_TITLE_PARTICLES"
  155 +msgstr "Particles"
  156 +
154 msgid "DALI_DEMO_STR_TITLE_PERF_SCROLL" 157 msgid "DALI_DEMO_STR_TITLE_PERF_SCROLL"
155 msgstr "Scrolling Performance" 158 msgstr "Scrolling Performance"
156 159
resources/po/en_US.po
@@ -154,6 +154,9 @@ msgstr &quot;Negotiate Size&quot; @@ -154,6 +154,9 @@ msgstr &quot;Negotiate Size&quot;
154 msgid "DALI_DEMO_STR_TITLE_PAGE_TURN" 154 msgid "DALI_DEMO_STR_TITLE_PAGE_TURN"
155 msgstr "Page Turn" 155 msgstr "Page Turn"
156 156
  157 +msgid "DALI_DEMO_STR_TITLE_PARTICLES"
  158 +msgstr "Particles"
  159 +
157 msgid "DALI_DEMO_STR_TITLE_PERF_SCROLL" 160 msgid "DALI_DEMO_STR_TITLE_PERF_SCROLL"
158 msgstr "Scrolling Performance" 161 msgstr "Scrolling Performance"
159 162
shared/dali-demo-strings.h
@@ -89,6 +89,7 @@ extern &quot;C&quot; @@ -89,6 +89,7 @@ extern &quot;C&quot;
89 #define DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE") 89 #define DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE")
90 #define DALI_DEMO_STR_TITLE_NEGOTIATE_SIZE dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_NEGOTIATE_SIZE") 90 #define DALI_DEMO_STR_TITLE_NEGOTIATE_SIZE dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_NEGOTIATE_SIZE")
91 #define DALI_DEMO_STR_TITLE_PAGE_TURN dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PAGE_TURN") 91 #define DALI_DEMO_STR_TITLE_PAGE_TURN dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PAGE_TURN")
  92 +#define DALI_DEMO_STR_TITLE_PARTICLES dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PARTICLES")
92 #define DALI_DEMO_STR_TITLE_PBR dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PBR") 93 #define DALI_DEMO_STR_TITLE_PBR dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PBR")
93 #define DALI_DEMO_STR_TITLE_PERF_SCROLL dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PERF_SCROLL") 94 #define DALI_DEMO_STR_TITLE_PERF_SCROLL dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PERF_SCROLL")
94 #define DALI_DEMO_STR_TITLE_POINT_MESH dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_POINT_MESH") 95 #define DALI_DEMO_STR_TITLE_POINT_MESH dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_POINT_MESH")
@@ -191,6 +192,7 @@ extern &quot;C&quot; @@ -191,6 +192,7 @@ extern &quot;C&quot;
191 #define DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE "Native Image Source" 192 #define DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE "Native Image Source"
192 #define DALI_DEMO_STR_TITLE_NEGOTIATE_SIZE "Negotiate Size" 193 #define DALI_DEMO_STR_TITLE_NEGOTIATE_SIZE "Negotiate Size"
193 #define DALI_DEMO_STR_TITLE_PAGE_TURN "Page Turn" 194 #define DALI_DEMO_STR_TITLE_PAGE_TURN "Page Turn"
  195 +#define DALI_DEMO_STR_TITLE_PARTICLES "Particles"
194 #define DALI_DEMO_STR_TITLE_PBR "PBR" 196 #define DALI_DEMO_STR_TITLE_PBR "PBR"
195 #define DALI_DEMO_STR_TITLE_PERF_SCROLL "Scrolling Performance" 197 #define DALI_DEMO_STR_TITLE_PERF_SCROLL "Scrolling Performance"
196 #define DALI_DEMO_STR_TITLE_POINT_MESH "Point Mesh" 198 #define DALI_DEMO_STR_TITLE_POINT_MESH "Point Mesh"