xref: /haiku/src/add-ons/screen_savers/flurry/Smoke.cpp (revision 97dfeb96704e5dbc5bec32ad7b21379d0125e031)
1 /*
2 
3 Copyright (c) 2002, Calum Robinson
4 All rights reserved.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8 
9 * Redistributions of source code must retain the above copyright notice, this
10   list of conditions and the following disclaimer.
11 
12 * Redistributions in binary form must reproduce the above copyright notice,
13   this list of conditions and the following disclaimer in the documentation
14   and/or other materials provided with the distribution.
15 
16 * Neither the name of the author nor the names of its contributors may be used
17   to endorse or promote products derived from this software without specific
18   prior written permission.
19 
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
24 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 */
32 
33 
34 #include "Smoke.h"
35 
36 #include "Shared.h"
37 #include "Star.h"
38 #include "Spark.h"
39 
40 
41 #define MAXANGLES 16384
42 #define NOT_QUITE_DEAD 3
43 
44 #define streamBias 7.0f
45 #define incohesion 0.07f
46 #define streamSpeed 450.0
47 #define gravity 1500000.0f
48 #define intensity 75000.0f;
49 #define streamSize 25000.0f
50 #define colorIncoherence 0.15f
51 
52 
53 static float
54 FastDistance2D(float x, float y)
55 {
56 	// this function computes the distance from 0,0 to x,y with ~3.5% error
57 
58 	// first compute the absolute value of x,y
59 	x = (x < 0.0f) ? -x : x;
60 	y = (y < 0.0f) ? -y : y;
61 
62 	// compute the minimum of x,y
63 	float mn = x < y ? x : y;
64 
65 	// return the distance
66 	return x + y - (mn * 0.5f) - (mn * 0.25f) + (mn * 0.0625f);
67 }
68 
69 
70 void
71 InitSmoke(SmokeV* s)
72 {
73 	s->nextParticle = 0;
74 	s->nextSubParticle = 0;
75 	s->lastParticleTime = 0.25f;
76 	s->firstTime = 1;
77 	s->frame = 0;
78 
79 	for (int i = 0; i < 3; i++)
80 		s->old[i] = RandFlt(-100.0, 100.0);
81 }
82 
83 
84 void
85 UpdateSmoke_ScalarBase(flurry_info_t* info, SmokeV* s)
86 {
87 	float sx = info->star->position[0];
88 	float sy = info->star->position[1];
89 	float sz = info->star->position[2];
90 	double frameRate;
91 	double frameRateModifier;
92 
93 	s->frame++;
94 
95 	if (!s->firstTime) {
96 		/* release 12 puffs every frame */
97 		if (info->fTime - s->lastParticleTime >= 1.0f / 121.0f) {
98 			float dx;
99 			float dy;
100 			float dz;
101 			float deltax;
102 			float deltay;
103 			float deltaz;
104 			float f;
105 			float rsquared;
106 			float mag;
107 
108 			dx = s->old[0] - sx;
109 			dy = s->old[1] - sy;
110 			dz = s->old[2] - sz;
111 			mag = 5.0f;
112 			deltax = (dx * mag);
113 			deltay = (dy * mag);
114 			deltaz = (dz * mag);
115 			for(int i=0; i < info->numStreams; i++) {
116 				float streamSpeedCoherenceFactor;
117 
118 				s->p[s->nextParticle].delta[0].f[s->nextSubParticle] = deltax;
119 				s->p[s->nextParticle].delta[1].f[s->nextSubParticle] = deltay;
120 				s->p[s->nextParticle].delta[2].f[s->nextSubParticle] = deltaz;
121 				s->p[s->nextParticle].position[0].f[s->nextSubParticle] = sx;
122 				s->p[s->nextParticle].position[1].f[s->nextSubParticle] = sy;
123 				s->p[s->nextParticle].position[2].f[s->nextSubParticle] = sz;
124 				s->p[s->nextParticle].oldposition[0].f[s->nextSubParticle] = sx;
125 				s->p[s->nextParticle].oldposition[1].f[s->nextSubParticle] = sy;
126 				s->p[s->nextParticle].oldposition[2].f[s->nextSubParticle] = sz;
127 				streamSpeedCoherenceFactor = MAX_(0.0f,1.0f
128 					+ RandBell(0.25f * incohesion));
129 				dx = s->p[s->nextParticle].position[0].f[s->nextSubParticle]
130 					- info->spark[i]->position[0];
131 				dy = s->p[s->nextParticle].position[1].f[s->nextSubParticle]
132 					- info->spark[i]->position[1];
133 				dz = s->p[s->nextParticle].position[2].f[s->nextSubParticle]
134 					- info->spark[i]->position[2];
135 				rsquared = (dx * dx + dy * dy + dz * dz);
136 				f = streamSpeed * streamSpeedCoherenceFactor;
137 
138 				mag = f / (float)sqrt(rsquared);
139 
140 				s->p[s->nextParticle].delta[0].f[s->nextSubParticle]
141 					-= (dx * mag);
142 				s->p[s->nextParticle].delta[1].f[s->nextSubParticle]
143 					-= (dy * mag);
144 				s->p[s->nextParticle].delta[2].f[s->nextSubParticle]
145 					-= (dz * mag);
146 				s->p[s->nextParticle].color[0].f[s->nextSubParticle]
147 					= info->spark[i]->color[0] * (1.0f
148 						+ RandBell(colorIncoherence));
149 				s->p[s->nextParticle].color[1].f[s->nextSubParticle]
150 					= info->spark[i]->color[1] * (1.0f
151 						+ RandBell(colorIncoherence));
152 				s->p[s->nextParticle].color[2].f[s->nextSubParticle]
153 					= info->spark[i]->color[2] * (1.0f
154 						+ RandBell(colorIncoherence));
155 				s->p[s->nextParticle].color[3].f[s->nextSubParticle]
156 					= 0.85f * (1.0f + RandBell(0.5f*colorIncoherence));
157 				s->p[s->nextParticle].time.f[s->nextSubParticle] = info->fTime;
158 				s->p[s->nextParticle].dead.i[s->nextSubParticle] = 0;
159 				s->p[s->nextParticle].animFrame.i[s->nextSubParticle]
160 					= (random() & 63);
161 				s->nextSubParticle++;
162 				if (s->nextSubParticle == 4) {
163 					s->nextParticle++;
164 					s->nextSubParticle = 0;
165 				}
166 				if (s->nextParticle >= NUMSMOKEPARTICLES / 4) {
167 					s->nextParticle = 0;
168 					s->nextSubParticle = 0;
169 				}
170 			}
171 
172 			s->lastParticleTime = info->fTime;
173 		}
174 	} else {
175 		s->lastParticleTime = info->fTime;
176 		s->firstTime = 0;
177 	}
178 
179 	for(int i = 0; i < 3; i++)
180 		s->old[i] = info->star->position[i];
181 
182 	frameRate = ((double) info->dframe) / (info->fTime);
183 	frameRateModifier = 42.5f / frameRate;
184 
185 	for(int i = 0; i < NUMSMOKEPARTICLES / 4; i++) {
186 		for(int k = 0; k < 4; k++) {
187 			float dx;
188 			float dy;
189 			float dz;
190 			float f;
191 			float rsquared;
192 			float mag;
193 			float deltax;
194 			float deltay;
195 			float deltaz;
196 
197 			if (s->p[i].dead.i[k])
198 				continue;
199 
200 			deltax = s->p[i].delta[0].f[k];
201 			deltay = s->p[i].delta[1].f[k];
202 			deltaz = s->p[i].delta[2].f[k];
203 
204 			for(int j = 0; j < info->numStreams; j++) {
205 				dx = s->p[i].position[0].f[k] - info->spark[j]->position[0];
206 				dy = s->p[i].position[1].f[k] - info->spark[j]->position[1];
207 				dz = s->p[i].position[2].f[k] - info->spark[j]->position[2];
208 				rsquared = (dx * dx + dy * dy + dz * dz);
209 
210 				f = (gravity/rsquared) * frameRateModifier;
211 
212 				if ((((i * 4) + k) % info->numStreams) == j)
213 					f *= 1.0f + streamBias;
214 
215 				mag = f / (float) sqrt(rsquared);
216 
217 				deltax -= (dx * mag);
218 				deltay -= (dy * mag);
219 				deltaz -= (dz * mag);
220 			}
221 
222 			// slow this particle down by info->drag
223 			deltax *= info->drag;
224 			deltay *= info->drag;
225 			deltaz *= info->drag;
226 
227 			if ((deltax * deltax + deltay * deltay + deltaz * deltaz)
228 					>= 25000000.0f) {
229 				s->p[i].dead.i[k] = 1;
230 				continue;
231 			}
232 
233 			// update the position
234 			s->p[i].delta[0].f[k] = deltax;
235 			s->p[i].delta[1].f[k] = deltay;
236 			s->p[i].delta[2].f[k] = deltaz;
237 			for(int j = 0; j < 3; j++) {
238 				s->p[i].oldposition[j].f[k] = s->p[i].position[j].f[k];
239 				s->p[i].position[j].f[k]
240 					+= (s->p[i].delta[j].f[k]) * info->fDeltaTime;
241 			}
242 		}
243 	}
244 }
245 
246 
247 void
248 DrawSmoke_Scalar(flurry_info_t* info, SmokeV* s, float brightness)
249 {
250 	int svi = 0;
251 	int sci = 0;
252 	int sti = 0;
253 	int si = 0;
254 	float width;
255 	float sx;
256 	float sy;
257 	float u0;
258 	float v0;
259 	float u1;
260 	float v1;
261 	float w;
262 	float z;
263 	float screenRatio = info->sys_glWidth / 1024.0f;
264 	float hslash2 = info->sys_glHeight * 0.5f;
265 	float wslash2 = info->sys_glWidth * 0.5f;
266 
267 	width = (streamSize + 2.5f * info->streamExpansion) * screenRatio;
268 
269 	for (int i = 0; i < NUMSMOKEPARTICLES / 4; i++) {
270 		for (int k = 0; k < 4; k++) {
271 			float thisWidth;
272 			float oldz;
273 
274 			if (s->p[i].dead.i[k])
275 				continue;
276 
277 			thisWidth = (streamSize + (info->fTime - s->p[i].time.f[k])
278 				* info->streamExpansion) * screenRatio;
279 			if (thisWidth >= width) {
280 				s->p[i].dead.i[k] = 1;
281 				continue;
282 			}
283 			z = s->p[i].position[2].f[k];
284 			sx = s->p[i].position[0].f[k] * info->sys_glWidth / z + wslash2;
285 			sy = s->p[i].position[1].f[k] * info->sys_glWidth / z + hslash2;
286 			oldz = s->p[i].oldposition[2].f[k];
287 			if (sx > info->sys_glWidth + 50.0f || sx < -50.0f
288 				|| sy > info->sys_glHeight + 50.0f || sy < -50.0f || z < 25.0f
289 				|| oldz < 25.0f) {
290 				continue;
291 			}
292 
293 			w = MAX_(1.0f, thisWidth / z);
294 			{
295 				float oldx = s->p[i].oldposition[0].f[k];
296 				float oldy = s->p[i].oldposition[1].f[k];
297 				float oldscreenx = (oldx * info->sys_glWidth / oldz) + wslash2;
298 				float oldscreeny = (oldy * info->sys_glWidth / oldz) + hslash2;
299 				float dx = (sx - oldscreenx);
300 				float dy = (sy - oldscreeny);
301 
302 				float d = FastDistance2D(dx, dy);
303 
304 				float sm, os, ow;
305 				if (d)
306 					sm = w / d;
307 				else
308 					sm = 0.0f;
309 
310 				ow = MAX_(1.0f, thisWidth / oldz);
311 				if (d)
312 					os = ow / d;
313 				else
314 					os = 0.0f;
315 
316 				{
317 					floatToVector cmv;
318 					float cm;
319 					float m = 1.0f + sm;
320 
321 					float dxs = dx * sm;
322 					float dys = dy * sm;
323 					float dxos = dx * os;
324 					float dyos = dy * os;
325 					float dxm = dx * m;
326 					float dym = dy * m;
327 
328 					s->p[i].animFrame.i[k]++;
329 					if (s->p[i].animFrame.i[k] >= 64)
330 						s->p[i].animFrame.i[k] = 0;
331 
332 					u0 = (s->p[i].animFrame.i[k] & 7) * 0.125f;
333 					v0 = (s->p[i].animFrame.i[k] >> 3) * 0.125f;
334 					u1 = u0 + 0.125f;
335 					v1 = v0 + 0.125f;
336 					u1 = u0 + 0.125f;
337 					v1 = v0 + 0.125f;
338 					cm = (1.375f - thisWidth / width);
339 					if (s->p[i].dead.i[k] == 3) {
340 						cm *= 0.125f;
341 						s->p[i].dead.i[k] = 1;
342 					}
343 					si++;
344 					cm *= brightness;
345 					cmv.f[0] = s->p[i].color[0].f[k] * cm;
346 					cmv.f[1] = s->p[i].color[1].f[k] * cm;
347 					cmv.f[2] = s->p[i].color[2].f[k] * cm;
348 					cmv.f[3] = s->p[i].color[3].f[k] * cm;
349 
350 #if 0
351 					// MDT we can't use vectors in the Scalar routine
352 					s->seraphimColors[sci++].v = cmv.v;
353 					s->seraphimColors[sci++].v = cmv.v;
354 					s->seraphimColors[sci++].v = cmv.v;
355 					s->seraphimColors[sci++].v = cmv.v;
356 #else
357 					{
358 						for (int jj = 0; jj < 4; jj++) {
359 							for (int ii = 0; ii < 4; ii++)
360 								s->seraphimColors[sci].f[ii] = cmv.f[ii];
361 							sci += 1;
362 						}
363 					}
364 #endif
365 
366 					s->seraphimTextures[sti++] = u0;
367 					s->seraphimTextures[sti++] = v0;
368 					s->seraphimTextures[sti++] = u0;
369 					s->seraphimTextures[sti++] = v1;
370 
371 					s->seraphimTextures[sti++] = u1;
372 					s->seraphimTextures[sti++] = v1;
373 					s->seraphimTextures[sti++] = u1;
374 					s->seraphimTextures[sti++] = v0;
375 
376 					s->seraphimVertices[svi].f[0] = sx + dxm - dys;
377 					s->seraphimVertices[svi].f[1] = sy + dym + dxs;
378 					s->seraphimVertices[svi].f[2] = sx + dxm + dys;
379 					s->seraphimVertices[svi].f[3] = sy + dym - dxs;
380 					svi++;
381 
382 					s->seraphimVertices[svi].f[0] = oldscreenx - dxm + dyos;
383 					s->seraphimVertices[svi].f[1] = oldscreeny - dym - dxos;
384 					s->seraphimVertices[svi].f[2] = oldscreenx - dxm - dyos;
385 					s->seraphimVertices[svi].f[3] = oldscreeny - dym + dxos;
386 					svi++;
387 				}
388 			}
389 		}
390 	}
391 
392 	glColorPointer(4, GL_FLOAT, 0, s->seraphimColors);
393 	glVertexPointer(2, GL_FLOAT, 0, s->seraphimVertices);
394 	glTexCoordPointer(2, GL_FLOAT, 0, s->seraphimTextures);
395 	glDrawArrays(GL_QUADS, 0, si * 4);
396 }
397