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