2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "particle-view.h"
19 #include "dali/public-api/animation/constraints.h"
21 #include "generated/particle-view-vert.h"
22 #include "generated/particle-view-frag.h"
23 #include "generated/particle-view-simple-vert.h"
24 #include "generated/particle-view-simple-frag.h"
26 //#define ENABLE_DEBUG_VOLUME
28 #define USE_GLSL_VERSION(version) "#version " #version "\n"
35 const uint32_t POPULATION_GRANULARITY = 128;
37 uint32_t GetSkipValue(uint32_t count, uint32_t prime)
42 skip = (rand() % prime) * count * count + (rand() % prime) * count + (rand() % prime);
44 while (skip % prime == 0);
50 ParticleView::ParticleView(const ParticleField& field, Dali::Actor world, Dali::CameraActor camera,
51 Dali::Geometry particleGeom)
53 mParticleBoxSize(field.mBoxSize)
58 particleGeom = field.MakeGeometry();
62 Shader particleShader = Shader::New(SHADER_PARTICLE_VIEW_VERT, SHADER_PARTICLE_VIEW_FRAG, Shader::Hint::MODIFIES_GEOMETRY);
64 float zNear = camera.GetNearClippingPlane();
65 float zFar = camera.GetFarClippingPlane();
66 const Vector2 depthRange(zNear, 1.f / (zFar - zNear));
67 particleShader.RegisterProperty("uDepthRange", depthRange);
69 particleShader.RegisterProperty("uTwinkleFrequency", field.mTwinkleFrequency);
70 particleShader.RegisterProperty("uTwinkleSizeScale", field.mTwinkleSizeScale);
71 particleShader.RegisterProperty("uTwinkleOpacityWeight", field.mTwinkleOpacityWeight);
73 mPropPopulation = particleShader.RegisterProperty("uPopulation", 1.f);
74 mPropFocalLength = particleShader.RegisterProperty("uFocalLength", .5f);
75 mPropAperture = particleShader.RegisterProperty("uAperture", 8.f);
76 mPropAlphaTestRefValue = particleShader.RegisterProperty("uAlphaTestRefValue", 0.f);
77 mPropFadeRange = particleShader.RegisterProperty("uFadeRange", Vector2(0.f, 1.f));
81 char* writep = nameBuffer + snprintf(nameBuffer, sizeof(nameBuffer), "uScatter[");
82 for (uint32_t i = 0; i < std::extent<decltype(mScatterProps)>::value; ++i)
84 char* writep2 = writep + snprintf(writep, sizeof(nameBuffer) - std::distance(nameBuffer, writep), "%d].", i);
86 snprintf(writep2, sizeof(nameBuffer) - std::distance(nameBuffer, writep2), "radiusSqr");
87 mScatterProps[i].mPropRadius = particleShader.RegisterProperty(nameBuffer, 0.f);
89 snprintf(writep2, sizeof(nameBuffer) - std::distance(nameBuffer, writep2), "amount");
90 mScatterProps[i].mPropAmount = particleShader.RegisterProperty(nameBuffer, 0.f);
92 snprintf(writep2, sizeof(nameBuffer) - std::distance(nameBuffer, writep2), "ray");
93 mScatterProps[i].mPropRay = particleShader.RegisterProperty(nameBuffer, Vector3::ZERO);
96 // Create a look-up table for pseudo-random traversal of particles.
97 // Our particle mesh is sorted in Z; changing the population should remove
98 // particles "randomly", not from one end.
99 // Algorithm described in Mike McShaffry & al: Game Coding Complete.
100 const uint32_t prime = 131; // next prime after POPULATION_GRANULARITY
101 const uint32_t skip = GetSkipValue(POPULATION_GRANULARITY, prime);
104 writep = nameBuffer + snprintf(nameBuffer, sizeof(nameBuffer), "uOrderLookUp[");
105 for (uint32_t i = 0; i < POPULATION_GRANULARITY; ++i)
111 while (next == 0 || next > POPULATION_GRANULARITY);
113 snprintf(writep, sizeof(nameBuffer) - std::distance(nameBuffer, writep), "%d]", i);
114 particleShader.RegisterProperty(nameBuffer, float(next - 1));
117 // create animation for time in shader
118 auto propTime = particleShader.RegisterProperty("uTime", 0.f);
120 Animation animTime = Animation::New(field.mMotionCycleLength);
121 animTime.AnimateTo(Property(particleShader, propTime), static_cast<float>(M_PI * 2.f));
122 animTime.SetLoopCount(0);
125 mParticleShader = particleShader;
127 auto renderer = CreateRenderer(TextureSet::New(), particleGeom, particleShader, OPTION_BLEND);
128 auto masterParticles = CreateActor();
129 masterParticles.SetProperty(Actor::Property::SIZE, field.mBoxSize);
130 masterParticles.SetProperty(Actor::Property::VISIBLE, true);
131 masterParticles.AddRenderer(renderer);
133 mPropSecondaryColor = masterParticles.RegisterProperty("uSecondaryColor", Vector3::XAXIS);
135 #ifdef ENABLE_DEBUG_VOLUME
136 Geometry cubeGeom = CreateCuboidWireframeGeometry();
137 renderer = CreateRenderer(renderer.GetTextures(), cubeGeom, Shader::New(SHADER_PARTICLE_VIEW_SIMPLE_VERT, SHADER_PARTICLE_VIEW_SIMPLE_FRAG));
138 masterParticles.AddRenderer(renderer);
141 world.Add(masterParticles);
142 mMasterParticles = masterParticles;
145 ParticleView::~ParticleView()
147 UnparentAndReset(mMasterParticles);
148 UnparentAndReset(mSlaveParticles);
150 for (auto anim: { mAngularAnim, mLinearAnim })
159 for (auto& s: mScatterProps)
161 auto& anim = s.mAnim;
170 void ParticleView::SetColorRange(const ColorRange& range)
172 mMasterParticles.SetProperty(Actor::Property::COLOR_RED, range.rgb0.r);
173 mMasterParticles.SetProperty(Actor::Property::COLOR_GREEN, range.rgb0.g);
174 mMasterParticles.SetProperty(Actor::Property::COLOR_BLUE, range.rgb0.b);
176 mMasterParticles.SetProperty(mPropSecondaryColor, range.rgb1);
179 void ParticleView::SetPopulation(float percentage)
181 percentage = 1.f - std::min(1.f, std::max(0.f, percentage));
182 mParticleShader.SetProperty(mPropPopulation, POPULATION_GRANULARITY * percentage);
185 void ParticleView::SetFocalLength(float f)
187 mParticleShader.SetProperty(mPropFocalLength, f);
190 void ParticleView::SetAperture(float a)
192 mParticleShader.SetProperty(mPropAperture, a);
195 void ParticleView::SetAlphaTestRefValue(float rv)
197 mParticleShader.SetProperty(mPropAlphaTestRefValue, rv);
200 void ParticleView::SetFadeRange(float near, float far)
202 mParticleShader.SetProperty(mPropFadeRange, Vector2(near, far));
205 void ParticleView::SetAngularVelocity(float v)
210 mAngularAnim.Clear();
211 mAngularAnim.Reset();
216 float sign = Sign(v);
217 auto anim = Animation::New(std::abs(2. * M_PI / v));
218 anim.AnimateTo(Property(mMasterParticles, Actor::Property::ORIENTATION),
219 Quaternion(Radian(Degree(120. * sign)), Vector3::ZAXIS), TimePeriod(0., anim.GetDuration() / 3.));
220 anim.AnimateTo(Property(mMasterParticles, Actor::Property::ORIENTATION),
221 Quaternion(Radian(Degree(240. * sign)), Vector3::ZAXIS), TimePeriod(anim.GetDuration() / 3., anim.GetDuration() / 3.));
222 anim.AnimateTo(Property(mMasterParticles, Actor::Property::ORIENTATION),
223 Quaternion(Radian(Degree(360. * sign)), Vector3::ZAXIS), TimePeriod(2. * anim.GetDuration() / 3., anim.GetDuration() / 3.));
224 anim.SetLoopCount(0);
231 void ParticleView::SetLinearVelocity(float v)
239 UnparentAndReset(mSlaveParticles);
243 float sign = Sign(v);
244 float directedSize = sign * mParticleBoxSize.z;
246 Actor slaveParticles = CloneActor(mMasterParticles);
247 Vector3 position = mMasterParticles.GetCurrentProperty(Actor::Property::POSITION).Get<Vector3>();
248 slaveParticles.SetProperty(Actor::Property::POSITION, position + Vector3(0., 0., directedSize));
250 auto propSecondaryColor = slaveParticles.RegisterProperty("uSecondaryColor", Vector3::XAXIS);
252 Actor world = mWorld.GetHandle();
253 world.Add(slaveParticles);
255 if (sign < 0.) // fix draw order
257 world.Remove(mMasterParticles);
258 world.Add(mMasterParticles);
261 Constraint constraint = Constraint::New<Vector4>(slaveParticles, Actor::Property::COLOR,
262 EqualToConstraint());
263 constraint.AddSource(Source(mMasterParticles, Actor::Property::COLOR));
266 constraint = Constraint::New<Vector3>(slaveParticles, propSecondaryColor,
267 EqualToConstraint());
268 constraint.AddSource(Source(mMasterParticles, mPropSecondaryColor));
271 constraint = Constraint::New<Quaternion>(slaveParticles, Actor::Property::ORIENTATION,
272 EqualToConstraint());
273 constraint.AddSource(Source(mMasterParticles, Actor::Property::ORIENTATION));
276 auto anim = Animation::New(std::abs(directedSize / v));
277 anim.AnimateTo(Property(mMasterParticles, Actor::Property::POSITION_Z), position.z - directedSize);
278 anim.AnimateTo(Property(slaveParticles, Actor::Property::POSITION_Z), position.z);
279 anim.SetLoopCount(0);
283 mSlaveParticles = slaveParticles;
287 void ParticleView::Scatter(float radius, float amount, float durationOut, float durationIn)
289 mActiveScatter = (mActiveScatter + 1) % std::extent<decltype(mScatterProps)>::value;
291 auto& scatter = mScatterProps[mActiveScatter];
294 scatter.mAnim.Stop();
297 radius /= mParticleBoxSize.y;
299 mParticleShader.SetProperty(scatter.mPropRadius, radius);
301 Animation anim = Animation::New(durationOut + durationIn);
302 auto scatterAmount = Property(mParticleShader, scatter.mPropAmount);
303 anim.AnimateTo(scatterAmount, amount, AlphaFunction::EASE_OUT,
304 TimePeriod(0.f, durationOut));
305 anim.AnimateTo(scatterAmount, 0.f, AlphaFunction::EASE_IN_OUT_SINE,
306 TimePeriod(durationOut, durationIn));
309 scatter.mAnim = anim;
312 void ParticleView::SetScatterRay(Dali::Vector3 rayDir)
314 auto& scatter = mScatterProps[mActiveScatter];
315 mParticleShader.SetProperty(scatter.mPropRay, rayDir);;
318 void ParticleView::Fade(float duration, float target, AlphaFunction alphaFn,
319 std::function<void(Dali::Animation&)> onFinished)
326 Animation anim = Animation::New(duration);
327 anim.AnimateTo(Property(mMasterParticles, Actor::Property::COLOR_ALPHA), target, alphaFn);
330 anim.AnimateTo(Property(mSlaveParticles, Actor::Property::COLOR_ALPHA), target, alphaFn);
335 anim.FinishedSignal().Connect(this, onFinished);
342 void ParticleView::Fade(float duration, float target, float from, AlphaFunction alphaFn,
343 std::function<void(Dali::Animation&)> onFinished)
345 mMasterParticles.SetProperty(Actor::Property::COLOR_ALPHA, from);
348 mSlaveParticles.SetProperty(Actor::Property::COLOR_ALPHA, from);
351 Fade(duration, target, alphaFn, onFinished);