Convert shaders in dali-demo to use shader compilation tool
[platform/core/uifw/dali-demo.git] / examples / particles / particle-view.cpp
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 #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"
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
35 const uint32_t POPULATION_GRANULARITY = 128;
36
37 uint32_t GetSkipValue(uint32_t count, uint32_t prime)
38 {
39   uint32_t skip = 0;
40   do
41   {
42     skip = (rand() % prime) * count * count + (rand() % prime) * count + (rand() % prime);
43   }
44   while (skip % prime == 0);
45   return skip;
46 }
47
48 }
49
50 ParticleView::ParticleView(const ParticleField& field, Dali::Actor world, Dali::CameraActor camera,
51   Dali::Geometry particleGeom)
52 : mWorld(world),
53   mParticleBoxSize(field.mBoxSize)
54 {
55   if (!particleGeom)
56   {
57     // create particles
58     particleGeom = field.MakeGeometry();
59   }
60
61   // create shader
62   Shader particleShader = Shader::New(SHADER_PARTICLE_VIEW_VERT, SHADER_PARTICLE_VIEW_FRAG, Shader::Hint::MODIFIES_GEOMETRY);
63
64   float zNear = camera.GetNearClippingPlane();
65   float zFar = camera.GetFarClippingPlane();
66   const Vector2 depthRange(zNear, 1.f / (zFar - zNear));
67   particleShader.RegisterProperty("uDepthRange", depthRange);
68
69   particleShader.RegisterProperty("uTwinkleFrequency", field.mTwinkleFrequency);
70   particleShader.RegisterProperty("uTwinkleSizeScale", field.mTwinkleSizeScale);
71   particleShader.RegisterProperty("uTwinkleOpacityWeight", field.mTwinkleOpacityWeight);
72
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));
78
79   // scatter variables
80   char nameBuffer[64];
81   char* writep = nameBuffer + snprintf(nameBuffer, sizeof(nameBuffer), "uScatter[");
82   for (uint32_t i = 0; i < std::extent<decltype(mScatterProps)>::value; ++i)
83   {
84     char* writep2 = writep + snprintf(writep, sizeof(nameBuffer) - std::distance(nameBuffer, writep), "%d].", i);
85
86     snprintf(writep2, sizeof(nameBuffer) - std::distance(nameBuffer, writep2), "radiusSqr");
87     mScatterProps[i].mPropRadius = particleShader.RegisterProperty(nameBuffer, 0.f);
88
89     snprintf(writep2, sizeof(nameBuffer) - std::distance(nameBuffer, writep2), "amount");
90     mScatterProps[i].mPropAmount = particleShader.RegisterProperty(nameBuffer, 0.f);
91
92     snprintf(writep2, sizeof(nameBuffer) - std::distance(nameBuffer, writep2), "ray");
93     mScatterProps[i].mPropRay = particleShader.RegisterProperty(nameBuffer, Vector3::ZERO);
94   }
95
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);
102   uint32_t next = 0;
103
104   writep = nameBuffer + snprintf(nameBuffer, sizeof(nameBuffer), "uOrderLookUp[");
105   for (uint32_t i = 0; i < POPULATION_GRANULARITY; ++i)
106   {
107     do {
108       next += skip;
109       next %= prime;
110     }
111     while (next == 0 || next > POPULATION_GRANULARITY);
112
113     snprintf(writep, sizeof(nameBuffer) - std::distance(nameBuffer, writep), "%d]", i);
114     particleShader.RegisterProperty(nameBuffer, float(next - 1));
115   }
116
117   // create animation for time in shader
118   auto propTime = particleShader.RegisterProperty("uTime", 0.f);
119
120   Animation animTime = Animation::New(field.mMotionCycleLength);
121   animTime.AnimateTo(Property(particleShader, propTime), static_cast<float>(M_PI * 2.f));
122   animTime.SetLoopCount(0);
123   animTime.Play();
124
125   mParticleShader = particleShader;
126
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);
132
133   mPropSecondaryColor = masterParticles.RegisterProperty("uSecondaryColor", Vector3::XAXIS);
134
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);
139 #endif
140
141   world.Add(masterParticles);
142   mMasterParticles = masterParticles;
143 }
144
145 ParticleView::~ParticleView()
146 {
147   UnparentAndReset(mMasterParticles);
148   UnparentAndReset(mSlaveParticles);
149
150   for (auto anim: { mAngularAnim, mLinearAnim })
151   {
152     if (anim)
153     {
154       anim.Stop();
155       anim.Reset();
156     }
157   }
158
159   for (auto& s: mScatterProps)
160   {
161     auto& anim = s.mAnim;
162     if (anim)
163     {
164       anim.Stop();
165       anim.Reset();
166     }
167   }
168 }
169
170 void ParticleView::SetColorRange(const ColorRange& range)
171 {
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);
175
176   mMasterParticles.SetProperty(mPropSecondaryColor, range.rgb1);
177 }
178
179 void ParticleView::SetPopulation(float percentage)
180 {
181   percentage = 1.f - std::min(1.f, std::max(0.f, percentage));
182   mParticleShader.SetProperty(mPropPopulation, POPULATION_GRANULARITY * percentage);
183 }
184
185 void ParticleView::SetFocalLength(float f)
186 {
187   mParticleShader.SetProperty(mPropFocalLength, f);
188 }
189
190 void ParticleView::SetAperture(float a)
191 {
192   mParticleShader.SetProperty(mPropAperture, a);
193 }
194
195 void ParticleView::SetAlphaTestRefValue(float rv)
196 {
197   mParticleShader.SetProperty(mPropAlphaTestRefValue, rv);
198 }
199
200 void ParticleView::SetFadeRange(float near, float far)
201 {
202   mParticleShader.SetProperty(mPropFadeRange, Vector2(near, far));
203 }
204
205 void ParticleView::SetAngularVelocity(float v)
206 {
207   if (mAngularAnim)
208   {
209     mAngularAnim.Stop();
210     mAngularAnim.Clear();
211     mAngularAnim.Reset();
212   }
213
214   if (v * v > .0f)
215   {
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);
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,
262       EqualToConstraint());
263     constraint.AddSource(Source(mMasterParticles, Actor::Property::COLOR));
264     constraint.Apply();
265
266     constraint = Constraint::New<Vector3>(slaveParticles, propSecondaryColor,
267       EqualToConstraint());
268     constraint.AddSource(Source(mMasterParticles, mPropSecondaryColor));
269     constraint.Apply();
270
271     constraint = Constraint::New<Quaternion>(slaveParticles, Actor::Property::ORIENTATION,
272       EqualToConstraint());
273     constraint.AddSource(Source(mMasterParticles, Actor::Property::ORIENTATION));
274     constraint.Apply();
275
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);
280     anim.Play();
281
282     mLinearAnim = anim;
283     mSlaveParticles = slaveParticles;
284   }
285 }
286
287 void ParticleView::Scatter(float radius, float amount, float durationOut, float durationIn)
288 {
289   mActiveScatter = (mActiveScatter + 1) % std::extent<decltype(mScatterProps)>::value;
290
291   auto& scatter = mScatterProps[mActiveScatter];
292   if (scatter.mAnim)
293   {
294     scatter.mAnim.Stop();
295   }
296
297   radius /= mParticleBoxSize.y;
298   radius *= radius;
299   mParticleShader.SetProperty(scatter.mPropRadius, radius);
300
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));
307   anim.Play();
308
309   scatter.mAnim = anim;
310 }
311
312 void ParticleView::SetScatterRay(Dali::Vector3 rayDir)
313 {
314   auto& scatter = mScatterProps[mActiveScatter];
315   mParticleShader.SetProperty(scatter.mPropRay, rayDir);;
316 }
317
318 void ParticleView::Fade(float duration, float target, AlphaFunction alphaFn,
319   std::function<void(Dali::Animation&)> onFinished)
320 {
321   if (mFadeAnim)
322   {
323     mFadeAnim.Stop();
324   }
325
326   Animation anim = Animation::New(duration);
327   anim.AnimateTo(Property(mMasterParticles, Actor::Property::COLOR_ALPHA), target, alphaFn);
328   if (mSlaveParticles)
329   {
330     anim.AnimateTo(Property(mSlaveParticles, Actor::Property::COLOR_ALPHA), target, alphaFn);
331   }
332
333   if (onFinished)
334   {
335     anim.FinishedSignal().Connect(this, onFinished);
336   }
337   anim.Play();
338
339   mFadeAnim = anim;
340 }
341
342 void ParticleView::Fade(float duration, float target, float from, AlphaFunction alphaFn,
343   std::function<void(Dali::Animation&)> onFinished)
344 {
345   mMasterParticles.SetProperty(Actor::Property::COLOR_ALPHA, from);
346   if (mSlaveParticles)
347   {
348     mSlaveParticles.SetProperty(Actor::Property::COLOR_ALPHA, from);
349   }
350
351   Fade(duration, target, alphaFn, onFinished);
352 }