xref: /haiku/src/add-ons/screen_savers/flurry/Smoke.cpp (revision 4720c31bb08f5c6d1c8ddb616463c6fba9b350a1)
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 #include "Smoke.h"
33 
34 #include "Shared.h"
35 #include "Star.h"
36 #include "Spark.h"
37 
38 #define MAXANGLES 16384
39 #define NOT_QUITE_DEAD 3
40 
41 #define streamBias 7.0f
42 #define incohesion 0.07f
43 #define streamSpeed 450.0
44 #define gravity 1500000.0f
45 #define intensity 75000.0f;
46 #define streamSize 25000.0f
47 #define colorIncoherence 0.15f
48 
49 static float FastDistance2D(float x, float y)
50 {
51 	/* this function computes the distance from 0,0 to x,y with ~3.5% error */
52 	float mn;
53 
54 	/* first compute the absolute value of x,y */
55 	x = (x < 0.0f) ? -x : x;
56 	y = (y < 0.0f) ? -y : y;
57 
58 	/* compute the minimum of x,y */
59 	mn = x<y?x:y;
60 
61 	/* return the distance */
62 	return(x+y-(mn*0.5f)-(mn*0.25f)+(mn*0.0625f));
63 }
64 
65 void InitSmoke(SmokeV *s)
66 {
67 	int i;
68 	s->nextParticle = 0;
69 	s->nextSubParticle = 0;
70 	s->lastParticleTime = 0.25f;
71 	s->firstTime = 1;
72 	s->frame = 0;
73 	for (i=0;i<3;i++) {
74 		s->old[i] = RandFlt(-100.0, 100.0);
75 	}
76 }
77 
78 void UpdateSmoke_ScalarBase(flurry_info_t *info, SmokeV *s)
79 {
80 	int i,j,k;
81 	float sx = info->star->position[0];
82 	float sy = info->star->position[1];
83 	float sz = info->star->position[2];
84 	double frameRate;
85 	double frameRateModifier;
86 
87 
88 	s->frame++;
89 
90 	if(!s->firstTime) {
91 		/* release 12 puffs every frame */
92 		if(info->fTime - s->lastParticleTime >= 1.0f / 121.0f) {
93 			float dx,dy,dz,deltax,deltay,deltaz;
94 			float f;
95 			float rsquared;
96 			float mag;
97 
98 			dx = s->old[0] - sx;
99 			dy = s->old[1] - sy;
100 			dz = s->old[2] - sz;
101 			mag = 5.0f;
102 			deltax = (dx * mag);
103 			deltay = (dy * mag);
104 			deltaz = (dz * mag);
105 			for(i=0;i<info->numStreams;i++) {
106 				float streamSpeedCoherenceFactor;
107 
108 				s->p[s->nextParticle].delta[0].f[s->nextSubParticle] = deltax;
109 				s->p[s->nextParticle].delta[1].f[s->nextSubParticle] = deltay;
110 				s->p[s->nextParticle].delta[2].f[s->nextSubParticle] = deltaz;
111 				s->p[s->nextParticle].position[0].f[s->nextSubParticle] = sx;
112 				s->p[s->nextParticle].position[1].f[s->nextSubParticle] = sy;
113 				s->p[s->nextParticle].position[2].f[s->nextSubParticle] = sz;
114 				s->p[s->nextParticle].oldposition[0].f[s->nextSubParticle] = sx;
115 				s->p[s->nextParticle].oldposition[1].f[s->nextSubParticle] = sy;
116 				s->p[s->nextParticle].oldposition[2].f[s->nextSubParticle] = sz;
117 				streamSpeedCoherenceFactor = MAX_(0.0f,1.0f + RandBell(0.25f*incohesion));
118 				dx = s->p[s->nextParticle].position[0].f[s->nextSubParticle] - info->spark[i]->position[0];
119 				dy = s->p[s->nextParticle].position[1].f[s->nextSubParticle] - info->spark[i]->position[1];
120 				dz = s->p[s->nextParticle].position[2].f[s->nextSubParticle] - info->spark[i]->position[2];
121 				rsquared = (dx*dx+dy*dy+dz*dz);
122 				f = streamSpeed * streamSpeedCoherenceFactor;
123 
124 				mag = f / (float) sqrt(rsquared);
125 
126 				s->p[s->nextParticle].delta[0].f[s->nextSubParticle] -= (dx * mag);
127 				s->p[s->nextParticle].delta[1].f[s->nextSubParticle] -= (dy * mag);
128 				s->p[s->nextParticle].delta[2].f[s->nextSubParticle] -= (dz * mag);
129 				s->p[s->nextParticle].color[0].f[s->nextSubParticle] = info->spark[i]->color[0] * (1.0f + RandBell(colorIncoherence));
130 				s->p[s->nextParticle].color[1].f[s->nextSubParticle] = info->spark[i]->color[1] * (1.0f + RandBell(colorIncoherence));
131 				s->p[s->nextParticle].color[2].f[s->nextSubParticle] = info->spark[i]->color[2] * (1.0f + RandBell(colorIncoherence));
132 				s->p[s->nextParticle].color[3].f[s->nextSubParticle] = 0.85f * (1.0f + RandBell(0.5f*colorIncoherence));
133 				s->p[s->nextParticle].time.f[s->nextSubParticle] = info->fTime;
134 				s->p[s->nextParticle].dead.i[s->nextSubParticle] = 0;
135 				s->p[s->nextParticle].animFrame.i[s->nextSubParticle] = random()&63;
136 				s->nextSubParticle++;
137 				if (s->nextSubParticle==4) {
138 					s->nextParticle++;
139 					s->nextSubParticle=0;
140 				}
141 				if (s->nextParticle >= NUMSMOKEPARTICLES/4) {
142 					s->nextParticle = 0;
143 					s->nextSubParticle = 0;
144 				}
145 			}
146 
147 			s->lastParticleTime = info->fTime;
148 		}
149 	} else {
150 		s->lastParticleTime = info->fTime;
151 		s->firstTime = 0;
152 	}
153 
154 	for(i=0;i<3;i++) {
155 		s->old[i] = info->star->position[i];
156 	}
157 
158 	frameRate = ((double) info->dframe)/(info->fTime);
159 	frameRateModifier = 42.5f / frameRate;
160 
161 	for(i=0;i<NUMSMOKEPARTICLES/4;i++) {
162 		for(k=0; k<4; k++) {
163 			float dx,dy,dz;
164 			float f;
165 			float rsquared;
166 			float mag;
167 			float deltax;
168 			float deltay;
169 			float deltaz;
170 
171 			if (s->p[i].dead.i[k]) {
172 				continue;
173 			}
174 
175 			deltax = s->p[i].delta[0].f[k];
176 			deltay = s->p[i].delta[1].f[k];
177 			deltaz = s->p[i].delta[2].f[k];
178 
179 			for(j=0;j<info->numStreams;j++) {
180 				dx = s->p[i].position[0].f[k] - info->spark[j]->position[0];
181 				dy = s->p[i].position[1].f[k] - info->spark[j]->position[1];
182 				dz = s->p[i].position[2].f[k] - info->spark[j]->position[2];
183 				rsquared = (dx*dx+dy*dy+dz*dz);
184 
185 				f = (gravity/rsquared) * frameRateModifier;
186 
187 				if ((((i*4)+k) % info->numStreams) == j) {
188 					f *= 1.0f + streamBias;
189 				}
190 
191 				mag = f / (float) sqrt(rsquared);
192 
193 				deltax -= (dx * mag);
194 				deltay -= (dy * mag);
195 				deltaz -= (dz * mag);
196 			}
197 
198 			/* slow this particle down by info->drag */
199 			deltax *= info->drag;
200 			deltay *= info->drag;
201 			deltaz *= info->drag;
202 
203 			if((deltax*deltax+deltay*deltay+deltaz*deltaz) >= 25000000.0f) {
204 				s->p[i].dead.i[k] = 1;
205 				continue;
206 			}
207 
208 			/* update the position */
209 			s->p[i].delta[0].f[k] = deltax;
210 			s->p[i].delta[1].f[k] = deltay;
211 			s->p[i].delta[2].f[k] = deltaz;
212 			for(j=0;j<3;j++) {
213 				s->p[i].oldposition[j].f[k] = s->p[i].position[j].f[k];
214 				s->p[i].position[j].f[k] += (s->p[i].delta[j].f[k])*info->fDeltaTime;
215 			}
216 		}
217 	}
218 }
219 
220 void DrawSmoke_Scalar(flurry_info_t *info, SmokeV *s, float brightness)
221 {
222 	int svi = 0;
223 	int sci = 0;
224 	int sti = 0;
225 	int si = 0;
226 	float width;
227 	float sx,sy;
228 	float u0,v0,u1,v1;
229 	float w,z;
230 	float screenRatio = info->sys_glWidth / 1024.0f;
231 	float hslash2 = info->sys_glHeight * 0.5f;
232 	float wslash2 = info->sys_glWidth * 0.5f;
233 	int i,k;
234 
235 	width = (streamSize+2.5f*info->streamExpansion) * screenRatio;
236 
237 	for (i=0;i<NUMSMOKEPARTICLES/4;i++)
238 	{
239 		for (k=0; k<4; k++) {
240 			float thisWidth;
241 			float oldz;
242 
243 			if (s->p[i].dead.i[k]) {
244 				continue;
245 			}
246 			thisWidth = (streamSize + (info->fTime - s->p[i].time.f[k])*info->streamExpansion) * screenRatio;
247 			if (thisWidth >= width)
248 			{
249 				s->p[i].dead.i[k] = 1;
250 				continue;
251 			}
252 			z = s->p[i].position[2].f[k];
253 			sx = s->p[i].position[0].f[k] * info->sys_glWidth / z + wslash2;
254 			sy = s->p[i].position[1].f[k] * info->sys_glWidth / z + hslash2;
255 			oldz = s->p[i].oldposition[2].f[k];
256 			if (sx > info->sys_glWidth+50.0f || sx < -50.0f || sy > info->sys_glHeight+50.0f || sy < -50.0f || z < 25.0f || oldz < 25.0f)
257 			{
258 				continue;
259 			}
260 
261 			w = MAX_(1.0f,thisWidth/z);
262 			{
263 				float oldx = s->p[i].oldposition[0].f[k];
264 				float oldy = s->p[i].oldposition[1].f[k];
265 				float oldscreenx = (oldx * info->sys_glWidth / oldz) + wslash2;
266 				float oldscreeny = (oldy * info->sys_glWidth / oldz) + hslash2;
267 				float dx = (sx-oldscreenx);
268 				float dy = (sy-oldscreeny);
269 
270 				float d = FastDistance2D(dx, dy);
271 
272 				float sm, os, ow;
273 				if (d)
274 				{
275 					sm = w/d;
276 				}
277 				else
278 				{
279 					sm = 0.0f;
280 				}
281 				ow = MAX_(1.0f,thisWidth/oldz);
282 				if (d)
283 				{
284 					os = ow/d;
285 				}
286 				else
287 				{
288 					os = 0.0f;
289 				}
290 
291 				{
292 					floatToVector cmv;
293 					float cm;
294 					float m = 1.0f + sm;
295 
296 					float dxs = dx*sm;
297 					float dys = dy*sm;
298 					float dxos = dx*os;
299 					float dyos = dy*os;
300 					float dxm = dx*m;
301 					float dym = dy*m;
302 
303 					s->p[i].animFrame.i[k]++;
304 					if (s->p[i].animFrame.i[k] >= 64)
305 					{
306 						s->p[i].animFrame.i[k] = 0;
307 					}
308 
309 					u0 = (s->p[i].animFrame.i[k]&&7) * 0.125f;
310 					v0 = (s->p[i].animFrame.i[k]>>3) * 0.125f;
311 					u1 = u0 + 0.125f;
312 					v1 = v0 + 0.125f;
313 					u1 = u0 + 0.125f;
314 					v1 = v0 + 0.125f;
315 					cm = (1.375f - thisWidth/width);
316 					if (s->p[i].dead.i[k] == 3)
317 					{
318 						cm *= 0.125f;
319 						s->p[i].dead.i[k] = 1;
320 					}
321 					si++;
322 					cm *= brightness;
323 					cmv.f[0] = s->p[i].color[0].f[k]*cm;
324 					cmv.f[1] = s->p[i].color[1].f[k]*cm;
325 					cmv.f[2] = s->p[i].color[2].f[k]*cm;
326 					cmv.f[3] = s->p[i].color[3].f[k]*cm;
327 
328 #if 0
329 					/* MDT we can't use vectors in the Scalar routine */
330 					s->seraphimColors[sci++].v = cmv.v;
331 					s->seraphimColors[sci++].v = cmv.v;
332 					s->seraphimColors[sci++].v = cmv.v;
333 					s->seraphimColors[sci++].v = cmv.v;
334 #else
335 					{
336 						int ii, jj;
337 						for (jj = 0; jj < 4; jj++) {
338 							for (ii = 0; ii < 4; ii++) {
339 								s->seraphimColors[sci].f[ii] = cmv.f[ii];
340 							}
341 							sci += 1;
342 						}
343 					}
344 #endif
345 
346 					s->seraphimTextures[sti++] = u0;
347 					s->seraphimTextures[sti++] = v0;
348 					s->seraphimTextures[sti++] = u0;
349 					s->seraphimTextures[sti++] = v1;
350 
351 					s->seraphimTextures[sti++] = u1;
352 					s->seraphimTextures[sti++] = v1;
353 					s->seraphimTextures[sti++] = u1;
354 					s->seraphimTextures[sti++] = v0;
355 
356 					s->seraphimVertices[svi].f[0] = sx+dxm-dys;
357 					s->seraphimVertices[svi].f[1] = sy+dym+dxs;
358 					s->seraphimVertices[svi].f[2] = sx+dxm+dys;
359 					s->seraphimVertices[svi].f[3] = sy+dym-dxs;
360 					svi++;
361 
362 					s->seraphimVertices[svi].f[0] = oldscreenx-dxm+dyos;
363 					s->seraphimVertices[svi].f[1] = oldscreeny-dym-dxos;
364 					s->seraphimVertices[svi].f[2] = oldscreenx-dxm-dyos;
365 					s->seraphimVertices[svi].f[3] = oldscreeny-dym+dxos;
366 					svi++;
367 				}
368 			}
369 		}
370 	}
371 	glColorPointer(4,GL_FLOAT,0,s->seraphimColors);
372 	glVertexPointer(2,GL_FLOAT,0,s->seraphimVertices);
373 	glTexCoordPointer(2,GL_FLOAT,0,s->seraphimTextures);
374 	glDrawArrays(GL_QUADS,0,si*4);
375 }
376