Updated all files to new format
[platform/core/uifw/dali-demo.git] / examples / particles / particle-view.cpp
1 /*
2  * Copyright (c) 2021 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 "dali/public-api/animation/constraints.h"
19 #include "utils.h"
20
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"
25
26 //#define ENABLE_DEBUG_VOLUME
27
28 #define USE_GLSL_VERSION(version) "#version " #version "\n"
29
30 using namespace Dali;
31
32 namespace
33 {
34 const uint32_t POPULATION_GRANULARITY = 128;
35
36 uint32_t GetSkipValue(uint32_t count, uint32_t prime)
37 {
38   uint32_t skip = 0;
39   do
40   {
41     skip = (rand() % prime) * count * count + (rand() % prime) * count + (rand() % prime);
42   } while(skip % prime == 0);
43   return skip;
44 }
45
46 } // namespace
47
48 ParticleView::ParticleView(const ParticleField& field, Dali::Actor world, Dali::CameraActor camera, Dali::Geometry particleGeom)
49 : mWorld(world),
50   mParticleBoxSize(field.mBoxSize)
51 {
52   if(!particleGeom)
53   {
54     // create particles
55     particleGeom = field.MakeGeometry();
56   }
57
58   // create shader
59   Shader particleShader = Shader::New(SHADER_PARTICLE_VIEW_VERT, SHADER_PARTICLE_VIEW_FRAG, Shader::Hint::MODIFIES_GEOMETRY);
60
61   float         zNear = camera.GetNearClippingPlane();
62   float         zFar  = camera.GetFarClippingPlane();
63   const Vector2 depthRange(zNear, 1.f / (zFar - zNear));
64   particleShader.RegisterProperty("uDepthRange", depthRange);
65
66   particleShader.RegisterProperty("uTwinkleFrequency", field.mTwinkleFrequency);
67   particleShader.RegisterProperty("uTwinkleSizeScale", field.mTwinkleSizeScale);
68   particleShader.RegisterProperty("uTwinkleOpacityWeight", field.mTwinkleOpacityWeight);
69
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));
75
76   // scatter variables
77   char  nameBuffer[64];
78   char* writep = nameBuffer + snprintf(nameBuffer, sizeof(nameBuffer), "uScatter[");
79   for(uint32_t i = 0; i < std::extent<decltype(mScatterProps)>::value; ++i)
80   {
81     char* writep2 = writep + snprintf(writep, sizeof(nameBuffer) - std::distance(nameBuffer, writep), "%d].", i);
82
83     snprintf(writep2, sizeof(nameBuffer) - std::distance(nameBuffer, writep2), "radiusSqr");
84     mScatterProps[i].mPropRadius = particleShader.RegisterProperty(nameBuffer, 0.f);
85
86     snprintf(writep2, sizeof(nameBuffer) - std::distance(nameBuffer, writep2), "amount");
87     mScatterProps[i].mPropAmount = particleShader.RegisterProperty(nameBuffer, 0.f);
88
89     snprintf(writep2, sizeof(nameBuffer) - std::distance(nameBuffer, writep2), "ray");
90     mScatterProps[i].mPropRay = particleShader.RegisterProperty(nameBuffer, Vector3::ZERO);
91   }
92
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);
99   uint32_t       next  = 0;
100
101   writep = nameBuffer + snprintf(nameBuffer, sizeof(nameBuffer), "uOrderLookUp[");
102   for(uint32_t i = 0; i < POPULATION_GRANULARITY; ++i)
103   {
104     do
105     {
106       next += skip;
107       next %= prime;
108     } while(next == 0 || next > POPULATION_GRANULARITY);
109
110     snprintf(writep, sizeof(nameBuffer) - std::distance(nameBuffer, writep), "%d]", i);
111     particleShader.RegisterProperty(nameBuffer, float(next - 1));
112   }
113
114   // create animation for time in shader
115   auto propTime = particleShader.RegisterProperty("uTime", 0.f);
116
117   Animation animTime = Animation::New(field.mMotionCycleLength);
118   animTime.AnimateTo(Property(particleShader, propTime), static_cast<float>(M_PI * 2.f));
119   animTime.SetLoopCount(0);
120   animTime.Play();
121
122   mParticleShader = particleShader;
123
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);
129
130   mPropSecondaryColor = masterParticles.RegisterProperty("uSecondaryColor", Vector3::XAXIS);
131
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);
136 #endif
137
138   world.Add(masterParticles);
139   mMasterParticles = masterParticles;
140 }
141
142 ParticleView::~ParticleView()
143 {
144   UnparentAndReset(mMasterParticles);
145   UnparentAndReset(mSlaveParticles);
146
147   for(auto anim : {mAngularAnim, mLinearAnim})
148   {
149     if(anim)
150     {
151       anim.Stop();
152       anim.Reset();
153     }
154   }
155
156   for(auto& s : mScatterProps)
157   {
158     auto& anim = s.mAnim;
159     if(anim)
160     {
161       anim.Stop();
162       anim.Reset();
163     }
164   }
165 }
166
167 void ParticleView::SetColorRange(const ColorRange& range)
168 {
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);
172
173   mMasterParticles.SetProperty(mPropSecondaryColor, range.rgb1);
174 }
175
176 void ParticleView::SetPopulation(float percentage)
177 {
178   percentage = 1.f - std::min(1.f, std::max(0.f, percentage));
179   mParticleShader.SetProperty(mPropPopulation, POPULATION_GRANULARITY * percentage);
180 }
181
182 void ParticleView::SetFocalLength(float f)
183 {
184   mParticleShader.SetProperty(mPropFocalLength, f);
185 }
186
187 void ParticleView::SetAperture(float a)
188 {
189   mParticleShader.SetProperty(mPropAperture, a);
190 }
191
192 void ParticleView::SetAlphaTestRefValue(float rv)
193 {
194   mParticleShader.SetProperty(mPropAlphaTestRefValue, rv);
195 }
196
197 void ParticleView::SetFadeRange(float near, float far)
198 {
199   mParticleShader.SetProperty(mPropFadeRange, Vector2(near, far));
200 }
201
202 void ParticleView::SetAngularVelocity(float v)
203 {
204   if(mAngularAnim)
205   {
206     mAngularAnim.Stop();
207     mAngularAnim.Clear();
208     mAngularAnim.Reset();
209   }
210
211   if(v * v > .0f)
212   {
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);
225     anim.Play();
226
227     mAngularAnim = anim;
228   }
229 }
230
231 void ParticleView::SetLinearVelocity(float v)
232 {
233   if(mLinearAnim)
234   {
235     mLinearAnim.Stop();
236     mLinearAnim.Clear();
237     mLinearAnim.Reset();
238   }
239   UnparentAndReset(mSlaveParticles);
240
241   if(v * v > .0f)
242   {
243     float sign         = Sign(v);
244     float directedSize = sign * mParticleBoxSize.z;
245
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));
249
250     auto propSecondaryColor = slaveParticles.RegisterProperty("uSecondaryColor", Vector3::XAXIS);
251
252     Actor world = mWorld.GetHandle();
253     world.Add(slaveParticles);
254
255     if(sign < 0.) // fix draw order
256     {
257       world.Remove(mMasterParticles);
258       world.Add(mMasterParticles);
259     }
260
261     Constraint constraint = Constraint::New<Vector4>(slaveParticles, Actor::Property::COLOR, EqualToConstraint());
262     constraint.AddSource(Source(mMasterParticles, Actor::Property::COLOR));
263     constraint.Apply();
264
265     constraint = Constraint::New<Vector3>(slaveParticles, propSecondaryColor, EqualToConstraint());
266     constraint.AddSource(Source(mMasterParticles, mPropSecondaryColor));
267     constraint.Apply();
268
269     constraint = Constraint::New<Quaternion>(slaveParticles, Actor::Property::ORIENTATION, EqualToConstraint());
270     constraint.AddSource(Source(mMasterParticles, Actor::Property::ORIENTATION));
271     constraint.Apply();
272
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);
277     anim.Play();
278
279     mLinearAnim     = anim;
280     mSlaveParticles = slaveParticles;
281   }
282 }
283
284 void ParticleView::Scatter(float radius, float amount, float durationOut, float durationIn)
285 {
286   mActiveScatter = (mActiveScatter + 1) % std::extent<decltype(mScatterProps)>::value;
287
288   auto& scatter = mScatterProps[mActiveScatter];
289   if(scatter.mAnim)
290   {
291     scatter.mAnim.Stop();
292   }
293
294   radius /= mParticleBoxSize.y;
295   radius *= radius;
296   mParticleShader.SetProperty(scatter.mPropRadius, radius);
297
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));
302   anim.Play();
303
304   scatter.mAnim = anim;
305 }
306
307 void ParticleView::SetScatterRay(Dali::Vector3 rayDir)
308 {
309   auto& scatter = mScatterProps[mActiveScatter];
310   mParticleShader.SetProperty(scatter.mPropRay, rayDir);
311   ;
312 }
313
314 void ParticleView::Fade(float duration, float target, AlphaFunction alphaFn, std::function<void(Dali::Animation&)> onFinished)
315 {
316   if(mFadeAnim)
317   {
318     mFadeAnim.Stop();
319   }
320
321   Animation anim = Animation::New(duration);
322   anim.AnimateTo(Property(mMasterParticles, Actor::Property::COLOR_ALPHA), target, alphaFn);
323   if(mSlaveParticles)
324   {
325     anim.AnimateTo(Property(mSlaveParticles, Actor::Property::COLOR_ALPHA), target, alphaFn);
326   }
327
328   if(onFinished)
329   {
330     anim.FinishedSignal().Connect(this, onFinished);
331   }
332   anim.Play();
333
334   mFadeAnim = anim;
335 }
336
337 void ParticleView::Fade(float duration, float target, float from, AlphaFunction alphaFn, std::function<void(Dali::Animation&)> onFinished)
338 {
339   mMasterParticles.SetProperty(Actor::Property::COLOR_ALPHA, from);
340   if(mSlaveParticles)
341   {
342     mSlaveParticles.SetProperty(Actor::Property::COLOR_ALPHA, from);
343   }
344
345   Fade(duration, target, alphaFn, onFinished);
346 }