Commit de91eaa51ec2994d4ad3eae585475bca9b2c276d
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
Showing
12 changed files
with
1507 additions
and
0 deletions
com.samsung.dali-demo.xml
| ... | ... | @@ -169,6 +169,9 @@ |
| 169 | 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 | 170 | <label>Page Turn</label> |
| 171 | 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 | 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 | 176 | <label>perf-scroll</label> |
| 174 | 177 | </ui-application> | ... | ... |
demo/dali-demo.cpp
| ... | ... | @@ -50,6 +50,7 @@ int DALI_EXPORT_API main(int argc, char** argv) |
| 50 | 50 | demo.AddExample(Example("metaball-refrac.example", DALI_DEMO_STR_TITLE_METABALL_REFRAC)); |
| 51 | 51 | demo.AddExample(Example("motion-blur.example", DALI_DEMO_STR_TITLE_MOTION_BLUR)); |
| 52 | 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 | 54 | demo.AddExample(Example("reflection-demo.example", DALI_DEMO_STR_TITLE_REFLECTION)); |
| 54 | 55 | demo.AddExample(Example("refraction-effect.example", DALI_DEMO_STR_TITLE_REFRACTION)); |
| 55 | 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 | 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 "Negotiate Size" |
| 151 | 151 | msgid "DALI_DEMO_STR_TITLE_PAGE_TURN" |
| 152 | 152 | msgstr "Page Turn" |
| 153 | 153 | |
| 154 | +msgid "DALI_DEMO_STR_TITLE_PARTICLES" | |
| 155 | +msgstr "Particles" | |
| 156 | + | |
| 154 | 157 | msgid "DALI_DEMO_STR_TITLE_PERF_SCROLL" |
| 155 | 158 | msgstr "Scrolling Performance" |
| 156 | 159 | ... | ... |
resources/po/en_US.po
| ... | ... | @@ -154,6 +154,9 @@ msgstr "Negotiate Size" |
| 154 | 154 | msgid "DALI_DEMO_STR_TITLE_PAGE_TURN" |
| 155 | 155 | msgstr "Page Turn" |
| 156 | 156 | |
| 157 | +msgid "DALI_DEMO_STR_TITLE_PARTICLES" | |
| 158 | +msgstr "Particles" | |
| 159 | + | |
| 157 | 160 | msgid "DALI_DEMO_STR_TITLE_PERF_SCROLL" |
| 158 | 161 | msgstr "Scrolling Performance" |
| 159 | 162 | ... | ... |
shared/dali-demo-strings.h
| ... | ... | @@ -89,6 +89,7 @@ extern "C" |
| 89 | 89 | #define DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE") |
| 90 | 90 | #define DALI_DEMO_STR_TITLE_NEGOTIATE_SIZE dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_NEGOTIATE_SIZE") |
| 91 | 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 | 93 | #define DALI_DEMO_STR_TITLE_PBR dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PBR") |
| 93 | 94 | #define DALI_DEMO_STR_TITLE_PERF_SCROLL dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_PERF_SCROLL") |
| 94 | 95 | #define DALI_DEMO_STR_TITLE_POINT_MESH dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_POINT_MESH") |
| ... | ... | @@ -191,6 +192,7 @@ extern "C" |
| 191 | 192 | #define DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE "Native Image Source" |
| 192 | 193 | #define DALI_DEMO_STR_TITLE_NEGOTIATE_SIZE "Negotiate Size" |
| 193 | 194 | #define DALI_DEMO_STR_TITLE_PAGE_TURN "Page Turn" |
| 195 | +#define DALI_DEMO_STR_TITLE_PARTICLES "Particles" | |
| 194 | 196 | #define DALI_DEMO_STR_TITLE_PBR "PBR" |
| 195 | 197 | #define DALI_DEMO_STR_TITLE_PERF_SCROLL "Scrolling Performance" |
| 196 | 198 | #define DALI_DEMO_STR_TITLE_POINT_MESH "Point Mesh" | ... | ... |