1/*
2
3Copyright (c) 2002, Calum Robinson
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without
7modification, 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
20THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
24ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27ANY 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
29SOFTWARE, 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
53static float
54FastDistance2D(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
70void
71InitSmoke(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
84void
85UpdateSmoke_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
247void
248DrawSmoke_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