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