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