1 /*************************************************************************/
2 /* particle_system_sw.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
10 /* */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the */
13 /* "Software"), to deal in the Software without restriction, including */
14 /* without limitation the rights to use, copy, modify, merge, publish, */
15 /* distribute, sublicense, and/or sell copies of the Software, and to */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions: */
18 /* */
19 /* The above copyright notice and this permission notice shall be */
20 /* included in all copies or substantial portions of the Software. */
21 /* */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29 /*************************************************************************/
30 #include "particle_system_sw.h"
31 #include "sort.h"
32
ParticleSystemSW()33 ParticleSystemSW::ParticleSystemSW() {
34
35 amount = 8;
36 emitting = true;
37
38 for (int i = 0; i < VS::PARTICLE_VAR_MAX; i++) {
39 particle_randomness[i] = 0.0;
40 }
41
42 particle_vars[VS::PARTICLE_LIFETIME] = 2.0; //
43 particle_vars[VS::PARTICLE_SPREAD] = 0.2; //
44 particle_vars[VS::PARTICLE_GRAVITY] = 9.8; //
45 particle_vars[VS::PARTICLE_LINEAR_VELOCITY] = 0.2; //
46 particle_vars[VS::PARTICLE_ANGULAR_VELOCITY] = 0.0; //
47 particle_vars[VS::PARTICLE_LINEAR_ACCELERATION] = 0.0; //
48 particle_vars[VS::PARTICLE_RADIAL_ACCELERATION] = 0.0; //
49 particle_vars[VS::PARTICLE_TANGENTIAL_ACCELERATION] = 1.0; //
50 particle_vars[VS::PARTICLE_DAMPING] = 0.0; //
51 particle_vars[VS::PARTICLE_INITIAL_SIZE] = 1.0;
52 particle_vars[VS::PARTICLE_FINAL_SIZE] = 0.8;
53 particle_vars[VS::PARTICLE_HEIGHT] = 1;
54 particle_vars[VS::PARTICLE_HEIGHT_SPEED_SCALE] = 1;
55
56 height_from_velocity = false;
57 local_coordinates = false;
58
59 particle_vars[VS::PARTICLE_INITIAL_ANGLE] = 0.0; //
60
61 gravity_normal = Vector3(0, -1.0, 0);
62 //emission_half_extents=Vector3(0.1,0.1,0.1);
63 emission_half_extents = Vector3(1, 1, 1);
64 color_phase_count = 0;
65 color_phases[0].pos = 0.0;
66 color_phases[0].color = Color(1.0, 0.0, 0.0);
67 visibility_aabb = AABB(Vector3(-64, -64, -64), Vector3(128, 128, 128));
68
69 attractor_count = 0;
70 }
71
~ParticleSystemSW()72 ParticleSystemSW::~ParticleSystemSW() {
73 }
74
75 #define DEFAULT_SEED 1234567
76
_rand_from_seed(uint32_t * seed)77 _FORCE_INLINE_ static float _rand_from_seed(uint32_t *seed) {
78
79 uint32_t k;
80 uint32_t s = (*seed);
81 if (s == 0)
82 s = 0x12345987;
83 k = s / 127773;
84 s = 16807 * (s - k * 127773) - 2836 * k;
85 if (s < 0)
86 s += 2147483647;
87 (*seed) = s;
88
89 float v = ((float)((*seed) & 0xFFFFF)) / (float)0xFFFFF;
90 v = v * 2.0 - 1.0;
91 return v;
92 }
93
_irand_from_seed(uint32_t * seed)94 _FORCE_INLINE_ static uint32_t _irand_from_seed(uint32_t *seed) {
95
96 uint32_t k;
97 uint32_t s = (*seed);
98 if (s == 0)
99 s = 0x12345987;
100 k = s / 127773;
101 s = 16807 * (s - k * 127773) - 2836 * k;
102 if (s < 0)
103 s += 2147483647;
104 (*seed) = s;
105
106 return s;
107 }
108
process(const ParticleSystemSW * p_system,const Transform & p_transform,float p_time)109 void ParticleSystemProcessSW::process(const ParticleSystemSW *p_system, const Transform &p_transform, float p_time) {
110
111 valid = false;
112 if (p_system->amount <= 0) {
113 ERR_EXPLAIN("Invalid amount of particles: " + itos(p_system->amount));
114 ERR_FAIL_COND(p_system->amount <= 0);
115 }
116 if (p_system->attractor_count < 0 || p_system->attractor_count > VS::MAX_PARTICLE_ATTRACTORS) {
117 ERR_EXPLAIN("Invalid amount of particle attractors.");
118 ERR_FAIL_COND(p_system->attractor_count < 0 || p_system->attractor_count > VS::MAX_PARTICLE_ATTRACTORS);
119 }
120 float lifetime = p_system->particle_vars[VS::PARTICLE_LIFETIME];
121 if (lifetime < CMP_EPSILON) {
122 ERR_EXPLAIN("Particle system lifetime too small.");
123 ERR_FAIL_COND(lifetime < CMP_EPSILON);
124 }
125 valid = true;
126 int particle_count = MIN(p_system->amount, ParticleSystemSW::MAX_PARTICLES);
127 ;
128
129 int emission_point_count = p_system->emission_points.size();
130 DVector<Vector3>::Read r;
131 if (emission_point_count)
132 r = p_system->emission_points.read();
133
134 if (particle_count != particle_data.size()) {
135
136 //clear the whole system if particle amount changed
137 particle_data.clear();
138 particle_data.resize(p_system->amount);
139 particle_system_time = 0;
140 }
141
142 float next_time = particle_system_time + p_time;
143
144 if (next_time > lifetime)
145 next_time = Math::fmod(next_time, lifetime);
146
147 ParticleData *pdata = &particle_data[0];
148 Vector3 attractor_positions[VS::MAX_PARTICLE_ATTRACTORS];
149
150 for (int i = 0; i < p_system->attractor_count; i++) {
151
152 attractor_positions[i] = p_transform.xform(p_system->attractors[i].pos);
153 }
154
155 for (int i = 0; i < particle_count; i++) {
156
157 ParticleData &p = pdata[i];
158
159 float restart_time = (i * lifetime / p_system->amount);
160
161 bool restart = false;
162
163 if (next_time < particle_system_time) {
164
165 if (restart_time > particle_system_time || restart_time < next_time)
166 restart = true;
167
168 } else if (restart_time > particle_system_time && restart_time < next_time) {
169 restart = true;
170 }
171
172 if (restart) {
173
174 if (p_system->emitting) {
175 if (emission_point_count == 0) { //use AABB
176 if (p_system->local_coordinates)
177 p.pos = p_system->emission_half_extents * Vector3(_rand_from_seed(&rand_seed), _rand_from_seed(&rand_seed), _rand_from_seed(&rand_seed));
178 else
179 p.pos = p_transform.xform(p_system->emission_half_extents * Vector3(_rand_from_seed(&rand_seed), _rand_from_seed(&rand_seed), _rand_from_seed(&rand_seed)));
180 } else {
181 //use preset positions
182 if (p_system->local_coordinates)
183 p.pos = r[_irand_from_seed(&rand_seed) % emission_point_count];
184 else
185 p.pos = p_transform.xform(r[_irand_from_seed(&rand_seed) % emission_point_count]);
186 }
187
188 float angle1 = _rand_from_seed(&rand_seed) * p_system->particle_vars[VS::PARTICLE_SPREAD] * Math_PI;
189 float angle2 = _rand_from_seed(&rand_seed) * 20.0 * Math_PI; // make it more random like
190
191 Vector3 rot_xz = Vector3(Math::sin(angle1), 0.0, Math::cos(angle1));
192 Vector3 rot = Vector3(Math::cos(angle2) * rot_xz.x, Math::sin(angle2) * rot_xz.x, rot_xz.z);
193
194 p.vel = (rot * p_system->particle_vars[VS::PARTICLE_LINEAR_VELOCITY] + rot * p_system->particle_randomness[VS::PARTICLE_LINEAR_VELOCITY] * _rand_from_seed(&rand_seed));
195 if (!p_system->local_coordinates)
196 p.vel = p_transform.basis.xform(p.vel);
197
198 p.vel += p_system->emission_base_velocity;
199
200 p.rot = p_system->particle_vars[VS::PARTICLE_INITIAL_ANGLE] + p_system->particle_randomness[VS::PARTICLE_INITIAL_ANGLE] * _rand_from_seed(&rand_seed);
201 p.active = true;
202 for (int r = 0; r < PARTICLE_RANDOM_NUMBERS; r++)
203 p.random[r] = _rand_from_seed(&rand_seed);
204
205 } else {
206
207 p.pos = Vector3();
208 p.rot = 0;
209 p.vel = Vector3();
210 p.active = false;
211 }
212
213 } else {
214
215 if (!p.active)
216 continue;
217
218 Vector3 force;
219 //apply gravity
220 force = p_system->gravity_normal * (p_system->particle_vars[VS::PARTICLE_GRAVITY] + (p_system->particle_randomness[VS::PARTICLE_GRAVITY] * p.random[0]));
221 //apply linear acceleration
222 force += p.vel.normalized() * (p_system->particle_vars[VS::PARTICLE_LINEAR_ACCELERATION] + p_system->particle_randomness[VS::PARTICLE_LINEAR_ACCELERATION] * p.random[1]);
223 //apply radial acceleration
224 Vector3 org;
225 if (!p_system->local_coordinates)
226 org = p_transform.origin;
227 force += (p.pos - org).normalized() * (p_system->particle_vars[VS::PARTICLE_RADIAL_ACCELERATION] + p_system->particle_randomness[VS::PARTICLE_RADIAL_ACCELERATION] * p.random[2]);
228 //apply tangential acceleration
229 force += (p.pos - org).cross(p_system->gravity_normal).normalized() * (p_system->particle_vars[VS::PARTICLE_TANGENTIAL_ACCELERATION] + p_system->particle_randomness[VS::PARTICLE_TANGENTIAL_ACCELERATION] * p.random[3]);
230 //apply attractor forces
231 for (int a = 0; a < p_system->attractor_count; a++) {
232
233 force += (p.pos - attractor_positions[a]).normalized() * p_system->attractors[a].force;
234 }
235
236 p.vel += force * p_time;
237 if (p_system->particle_vars[VS::PARTICLE_DAMPING]) {
238
239 float v = p.vel.length();
240 float damp = p_system->particle_vars[VS::PARTICLE_DAMPING] + p_system->particle_vars[VS::PARTICLE_DAMPING] * p_system->particle_randomness[VS::PARTICLE_DAMPING];
241 v -= damp * p_time;
242 if (v < 0) {
243 p.vel = Vector3();
244 } else {
245 p.vel = p.vel.normalized() * v;
246 }
247 }
248 p.rot += (p_system->particle_vars[VS::PARTICLE_ANGULAR_VELOCITY] + p_system->particle_randomness[VS::PARTICLE_ANGULAR_VELOCITY] * p.random[4]) * p_time;
249 p.pos += p.vel * p_time;
250 }
251 }
252
253 particle_system_time = Math::fmod(particle_system_time + p_time, lifetime);
254 }
255
ParticleSystemProcessSW()256 ParticleSystemProcessSW::ParticleSystemProcessSW() {
257
258 particle_system_time = 0;
259 rand_seed = 1234567;
260 valid = false;
261 }
262
263 struct _ParticleSorterSW {
264
operator ()_ParticleSorterSW265 _FORCE_INLINE_ bool operator()(const ParticleSystemDrawInfoSW::ParticleDrawInfo *p_a, const ParticleSystemDrawInfoSW::ParticleDrawInfo *p_b) const {
266
267 return p_a->d > p_b->d; // draw from further away to closest
268 }
269 };
270
prepare(const ParticleSystemSW * p_system,const ParticleSystemProcessSW * p_process,const Transform & p_system_transform,const Transform & p_camera_transform)271 void ParticleSystemDrawInfoSW::prepare(const ParticleSystemSW *p_system, const ParticleSystemProcessSW *p_process, const Transform &p_system_transform, const Transform &p_camera_transform) {
272
273 ERR_FAIL_COND(p_process->particle_data.size() != p_system->amount);
274 ERR_FAIL_COND(p_system->amount <= 0 || p_system->amount >= ParticleSystemSW::MAX_PARTICLES);
275
276 const ParticleSystemProcessSW::ParticleData *pdata = &p_process->particle_data[0];
277 float time_pos = p_process->particle_system_time / p_system->particle_vars[VS::PARTICLE_LIFETIME];
278
279 ParticleSystemSW::ColorPhase cphase[VS::MAX_PARTICLE_COLOR_PHASES];
280
281 float last = -1;
282 int col_count = 0;
283
284 for (int i = 0; i < p_system->color_phase_count; i++) {
285
286 if (p_system->color_phases[i].pos <= last)
287 break;
288 cphase[i] = p_system->color_phases[i];
289 col_count++;
290 }
291
292 Vector3 camera_z_axis = p_camera_transform.basis.get_axis(2);
293
294 for (int i = 0; i < p_system->amount; i++) {
295
296 ParticleDrawInfo &pdi = draw_info[i];
297 pdi.data = &pdata[i];
298 pdi.transform.origin = pdi.data->pos;
299 if (p_system->local_coordinates)
300 pdi.transform.origin = p_system_transform.xform(pdi.transform.origin);
301
302 pdi.d = -camera_z_axis.dot(pdi.transform.origin);
303
304 // adjust particle size, color and rotation
305
306 float time = ((float)i / p_system->amount);
307 if (time < time_pos)
308 time = time_pos - time;
309 else
310 time = (1.0 - time) + time_pos;
311
312 Vector3 up = p_camera_transform.basis.get_axis(1); // up determines the rotation
313 float up_scale = 1.0;
314
315 if (p_system->height_from_velocity) {
316
317 Vector3 veld = pdi.data->vel;
318 Vector3 cam_z = camera_z_axis.normalized();
319 float vc = Math::abs(veld.normalized().dot(cam_z));
320
321 if (vc < (1.0 - CMP_EPSILON)) {
322 up = Plane(cam_z, 0).project(veld).normalized();
323 float h = p_system->particle_vars[VS::PARTICLE_HEIGHT] + p_system->particle_randomness[VS::PARTICLE_HEIGHT] * pdi.data->random[7];
324 float velh = veld.length();
325 h += velh * (p_system->particle_vars[VS::PARTICLE_HEIGHT_SPEED_SCALE] + p_system->particle_randomness[VS::PARTICLE_HEIGHT_SPEED_SCALE] * pdi.data->random[7]);
326
327 up_scale = Math::lerp(1.0, h, (1.0 - vc));
328 }
329
330 } else if (pdi.data->rot) {
331
332 up.rotate(camera_z_axis, pdi.data->rot);
333 }
334
335 {
336 // matrix
337 Vector3 v_z = (p_camera_transform.origin - pdi.transform.origin).normalized();
338 // Vector3 v_z = (p_camera_transform.origin-pdi.data->pos).normalized();
339 Vector3 v_y = up;
340 Vector3 v_x = v_y.cross(v_z);
341 v_y = v_z.cross(v_x);
342 v_x.normalize();
343 v_y.normalize();
344
345 float initial_scale, final_scale;
346 initial_scale = p_system->particle_vars[VS::PARTICLE_INITIAL_SIZE] + p_system->particle_randomness[VS::PARTICLE_INITIAL_SIZE] * pdi.data->random[5];
347 final_scale = p_system->particle_vars[VS::PARTICLE_FINAL_SIZE] + p_system->particle_randomness[VS::PARTICLE_FINAL_SIZE] * pdi.data->random[6];
348 float scale = initial_scale + time * (final_scale - initial_scale);
349
350 pdi.transform.basis.set_axis(0, v_x * scale);
351 pdi.transform.basis.set_axis(1, v_y * scale * up_scale);
352 pdi.transform.basis.set_axis(2, v_z * scale);
353 }
354
355 int cpos = 0;
356
357 while (cpos < col_count) {
358
359 if (cphase[cpos].pos > time)
360 break;
361 cpos++;
362 }
363
364 cpos--;
365
366 if (cpos == -1)
367 pdi.color = Color(1, 1, 1, 1);
368 else {
369 if (cpos == col_count - 1)
370 pdi.color = cphase[cpos].color;
371 else {
372 float diff = (cphase[cpos + 1].pos - cphase[cpos].pos);
373 if (diff > 0)
374 pdi.color = cphase[cpos].color.linear_interpolate(cphase[cpos + 1].color, (time - cphase[cpos].pos) / diff);
375 else
376 pdi.color = cphase[cpos + 1].color;
377 }
378 }
379
380 draw_info_order[i] = &pdi;
381 }
382
383 SortArray<ParticleDrawInfo *, _ParticleSorterSW> particle_sort;
384 particle_sort.sort(&draw_info_order[0], p_system->amount);
385 }
386