2 * Copyright (c) 2021 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"
18 #include "dali/public-api/animation/constraints.h"
21 #include "generated/particle-view-frag.h"
22 #include "generated/particle-view-simple-frag.h"
23 #include "generated/particle-view-simple-vert.h"
24 #include "generated/particle-view-vert.h"
26 //#define ENABLE_DEBUG_VOLUME
28 #define USE_GLSL_VERSION(version) "#version " #version "\n"
34 const uint32_t POPULATION_GRANULARITY = 128;
36 uint32_t GetSkipValue(uint32_t count, uint32_t prime)
41 skip = (rand() % prime) * count * count + (rand() % prime) * count + (rand() % prime);
42 } while(skip % prime == 0);
48 ParticleView::ParticleView(const ParticleField& field, Dali::Actor world, Dali::CameraActor camera, Dali::Geometry particleGeom)
50 mParticleBoxSize(field.mBoxSize)
55 particleGeom = field.MakeGeometry();
59 Shader particleShader = Shader::New(SHADER_PARTICLE_VIEW_VERT, SHADER_PARTICLE_VIEW_FRAG, Shader::Hint::MODIFIES_GEOMETRY);
61 float zNear = camera.GetNearClippingPlane();
62 float zFar = camera.GetFarClippingPlane();
63 const Vector2 depthRange(zNear, 1.f / (zFar - zNear));
64 particleShader.RegisterProperty("uDepthRange", depthRange);
66 particleShader.RegisterProperty("uTwinkleFrequency", field.mTwinkleFrequency);
67 particleShader.RegisterProperty("uTwinkleSizeScale", field.mTwinkleSizeScale);
68 particleShader.RegisterProperty("uTwinkleOpacityWeight", field.mTwinkleOpacityWeight);
70 mPropPopulation = particleShader.RegisterProperty("uPopulation", 1.f);
71 mPropFocalLength = particleShader.RegisterProperty("uFocalLength", .5f);
72 mPropAperture = particleShader.RegisterProperty("uAperture", 8.f);
73 mPropAlphaTestRefValue = particleShader.RegisterProperty("uAlphaTestRefValue", 0.f);
74 mPropFadeRange = particleShader.RegisterProperty("uFadeRange", Vector2(0.f, 1.f));
78 char* writep = nameBuffer + snprintf(nameBuffer, sizeof(nameBuffer), "uScatter[");
79 for(uint32_t i = 0; i < std::extent<decltype(mScatterProps)>::value; ++i)
81 char* writep2 = writep + snprintf(writep, sizeof(nameBuffer) - std::distance(nameBuffer, writep), "%d].", i);
83 snprintf(writep2, sizeof(nameBuffer) - std::distance(nameBuffer, writep2), "radiusSqr");
84 mScatterProps[i].mPropRadius = particleShader.RegisterProperty(nameBuffer, 0.f);
86 snprintf(writep2, sizeof(nameBuffer) - std::distance(nameBuffer, writep2), "amount");
87 mScatterProps[i].mPropAmount = particleShader.RegisterProperty(nameBuffer, 0.f);
89 snprintf(writep2, sizeof(nameBuffer) - std::distance(nameBuffer, writep2), "ray");
90 mScatterProps[i].mPropRay = particleShader.RegisterProperty(nameBuffer, Vector3::ZERO);
93 // Create a look-up table for pseudo-random traversal of particles.
94 // Our particle mesh is sorted in Z; changing the population should remove
95 // particles "randomly", not from one end.
96 // Algorithm described in Mike McShaffry & al: Game Coding Complete.
97 const uint32_t prime = 131; // next prime after POPULATION_GRANULARITY
98 const uint32_t skip = GetSkipValue(POPULATION_GRANULARITY, prime);
101 writep = nameBuffer + snprintf(nameBuffer, sizeof(nameBuffer), "uOrderLookUp[");
102 for(uint32_t i = 0; i < POPULATION_GRANULARITY; ++i)
108 } while(next == 0 || next > POPULATION_GRANULARITY);
110 snprintf(writep, sizeof(nameBuffer) - std::distance(nameBuffer, writep), "%d]", i);
111 particleShader.RegisterProperty(nameBuffer, float(next - 1));
114 // create animation for time in shader
115 auto propTime = particleShader.RegisterProperty("uTime", 0.f);
117 Animation animTime = Animation::New(field.mMotionCycleLength);
118 animTime.AnimateTo(Property(particleShader, propTime), static_cast<float>(M_PI * 2.f));
119 animTime.SetLoopCount(0);
122 mParticleShader = particleShader;
124 auto renderer = CreateRenderer(TextureSet::New(), particleGeom, particleShader, OPTION_BLEND);
125 auto masterParticles = CreateActor();
126 masterParticles.SetProperty(Actor::Property::SIZE, field.mBoxSize);
127 masterParticles.SetProperty(Actor::Property::VISIBLE, true);
128 masterParticles.AddRenderer(renderer);
130 mPropSecondaryColor = masterParticles.RegisterProperty("uSecondaryColor", Vector3::XAXIS);
132 #ifdef ENABLE_DEBUG_VOLUME
133 Geometry cubeGeom = CreateCuboidWireframeGeometry();
134 renderer = CreateRenderer(renderer.GetTextures(), cubeGeom, Shader::New(SHADER_PARTICLE_VIEW_SIMPLE_VERT, SHADER_PARTICLE_VIEW_SIMPLE_FRAG));
135 masterParticles.AddRenderer(renderer);
138 world.Add(masterParticles);
139 mMasterParticles = masterParticles;
142 ParticleView::~ParticleView()
144 UnparentAndReset(mMasterParticles);
145 UnparentAndReset(mSlaveParticles);
147 for(auto anim : {mAngularAnim, mLinearAnim})
156 for(auto& s : mScatterProps)
158 auto& anim = s.mAnim;
167 void ParticleView::SetColorRange(const ColorRange& range)
169 mMasterParticles.SetProperty(Actor::Property::COLOR_RED, range.rgb0.r);
170 mMasterParticles.SetProperty(Actor::Property::COLOR_GREEN, range.rgb0.g);
171 mMasterParticles.SetProperty(Actor::Property::COLOR_BLUE, range.rgb0.b);
173 mMasterParticles.SetProperty(mPropSecondaryColor, range.rgb1);
176 void ParticleView::SetPopulation(float percentage)
178 percentage = 1.f - std::min(1.f, std::max(0.f, percentage));
179 mParticleShader.SetProperty(mPropPopulation, POPULATION_GRANULARITY * percentage);
182 void ParticleView::SetFocalLength(float f)
184 mParticleShader.SetProperty(mPropFocalLength, f);
187 void ParticleView::SetAperture(float a)
189 mParticleShader.SetProperty(mPropAperture, a);
192 void ParticleView::SetAlphaTestRefValue(float rv)
194 mParticleShader.SetProperty(mPropAlphaTestRefValue, rv);
197 void ParticleView::SetFadeRange(float near, float far)
199 mParticleShader.SetProperty(mPropFadeRange, Vector2(near, far));
202 void ParticleView::SetAngularVelocity(float v)
207 mAngularAnim.Clear();
208 mAngularAnim.Reset();
213 float sign = Sign(v);
214 auto anim = Animation::New(std::abs(2. * M_PI / v));
215 anim.AnimateTo(Property(mMasterParticles, Actor::Property::ORIENTATION),
216 Quaternion(Radian(Degree(120. * sign)), Vector3::ZAXIS),
217 TimePeriod(0., anim.GetDuration() / 3.));
218 anim.AnimateTo(Property(mMasterParticles, Actor::Property::ORIENTATION),
219 Quaternion(Radian(Degree(240. * sign)), Vector3::ZAXIS),
220 TimePeriod(anim.GetDuration() / 3., anim.GetDuration() / 3.));
221 anim.AnimateTo(Property(mMasterParticles, Actor::Property::ORIENTATION),
222 Quaternion(Radian(Degree(360. * sign)), Vector3::ZAXIS),
223 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, EqualToConstraint());
262 constraint.AddSource(Source(mMasterParticles, Actor::Property::COLOR));
265 constraint = Constraint::New<Vector3>(slaveParticles, propSecondaryColor, EqualToConstraint());
266 constraint.AddSource(Source(mMasterParticles, mPropSecondaryColor));
269 constraint = Constraint::New<Quaternion>(slaveParticles, Actor::Property::ORIENTATION, EqualToConstraint());
270 constraint.AddSource(Source(mMasterParticles, Actor::Property::ORIENTATION));
273 auto anim = Animation::New(std::abs(directedSize / v));
274 anim.AnimateTo(Property(mMasterParticles, Actor::Property::POSITION_Z), position.z - directedSize);
275 anim.AnimateTo(Property(slaveParticles, Actor::Property::POSITION_Z), position.z);
276 anim.SetLoopCount(0);
280 mSlaveParticles = slaveParticles;
284 void ParticleView::Scatter(float radius, float amount, float durationOut, float durationIn)
286 mActiveScatter = (mActiveScatter + 1) % std::extent<decltype(mScatterProps)>::value;
288 auto& scatter = mScatterProps[mActiveScatter];
291 scatter.mAnim.Stop();
294 radius /= mParticleBoxSize.y;
296 mParticleShader.SetProperty(scatter.mPropRadius, radius);
298 Animation anim = Animation::New(durationOut + durationIn);
299 auto scatterAmount = Property(mParticleShader, scatter.mPropAmount);
300 anim.AnimateTo(scatterAmount, amount, AlphaFunction::EASE_OUT, TimePeriod(0.f, durationOut));
301 anim.AnimateTo(scatterAmount, 0.f, AlphaFunction::EASE_IN_OUT_SINE, TimePeriod(durationOut, durationIn));
304 scatter.mAnim = anim;
307 void ParticleView::SetScatterRay(Dali::Vector3 rayDir)
309 auto& scatter = mScatterProps[mActiveScatter];
310 mParticleShader.SetProperty(scatter.mPropRay, rayDir);
314 void ParticleView::Fade(float duration, float target, AlphaFunction alphaFn, std::function<void(Dali::Animation&)> onFinished)
321 Animation anim = Animation::New(duration);
322 anim.AnimateTo(Property(mMasterParticles, Actor::Property::COLOR_ALPHA), target, alphaFn);
325 anim.AnimateTo(Property(mSlaveParticles, Actor::Property::COLOR_ALPHA), target, alphaFn);
330 anim.FinishedSignal().Connect(this, onFinished);
337 void ParticleView::Fade(float duration, float target, float from, AlphaFunction alphaFn, std::function<void(Dali::Animation&)> onFinished)
339 mMasterParticles.SetProperty(Actor::Property::COLOR_ALPHA, from);
342 mSlaveParticles.SetProperty(Actor::Property::COLOR_ALPHA, from);
345 Fade(duration, target, alphaFn, onFinished);