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
FastDistance2D(float x,float y)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
InitSmoke(SmokeV * s)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
UpdateSmoke_ScalarBase(flurry_info_t * info,SmokeV * s)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
DrawSmoke_Scalar(flurry_info_t * info,SmokeV * s,float brightness)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 cm = (1.375f - thisWidth / width);
337 if (s->p[i].dead.i[k] == 3) {
338 cm *= 0.125f;
339 s->p[i].dead.i[k] = 1;
340 }
341 si++;
342 cm *= brightness;
343 cmv.f[0] = s->p[i].color[0].f[k] * cm;
344 cmv.f[1] = s->p[i].color[1].f[k] * cm;
345 cmv.f[2] = s->p[i].color[2].f[k] * cm;
346 cmv.f[3] = s->p[i].color[3].f[k] * cm;
347
348 #if 0
349 // MDT we can't use vectors in the Scalar routine
350 s->seraphimColors[sci++].v = cmv.v;
351 s->seraphimColors[sci++].v = cmv.v;
352 s->seraphimColors[sci++].v = cmv.v;
353 s->seraphimColors[sci++].v = cmv.v;
354 #else
355 {
356 for (int jj = 0; jj < 4; jj++) {
357 for (int ii = 0; ii < 4; ii++)
358 s->seraphimColors[sci].f[ii] = cmv.f[ii];
359 sci += 1;
360 }
361 }
362 #endif
363
364 s->seraphimTextures[sti++] = u0;
365 s->seraphimTextures[sti++] = v0;
366 s->seraphimTextures[sti++] = u0;
367 s->seraphimTextures[sti++] = v1;
368
369 s->seraphimTextures[sti++] = u1;
370 s->seraphimTextures[sti++] = v1;
371 s->seraphimTextures[sti++] = u1;
372 s->seraphimTextures[sti++] = v0;
373
374 s->seraphimVertices[svi].f[0] = sx + dxm - dys;
375 s->seraphimVertices[svi].f[1] = sy + dym + dxs;
376 s->seraphimVertices[svi].f[2] = sx + dxm + dys;
377 s->seraphimVertices[svi].f[3] = sy + dym - dxs;
378 svi++;
379
380 s->seraphimVertices[svi].f[0] = oldscreenx - dxm + dyos;
381 s->seraphimVertices[svi].f[1] = oldscreeny - dym - dxos;
382 s->seraphimVertices[svi].f[2] = oldscreenx - dxm - dyos;
383 s->seraphimVertices[svi].f[3] = oldscreeny - dym + dxos;
384 svi++;
385 }
386 }
387 }
388 }
389
390 glColorPointer(4, GL_FLOAT, 0, s->seraphimColors);
391 glVertexPointer(2, GL_FLOAT, 0, s->seraphimVertices);
392 glTexCoordPointer(2, GL_FLOAT, 0, s->seraphimTextures);
393 glDrawArrays(GL_QUADS, 0, si * 4);
394 }
395