/*============================================================================= Name : clouds.c Purpose : dust/gas cloud resource data structure management stuff Created 2/9/1998 by khent Copyright Relic Entertainment, Inc. All rights reserved. =============================================================================*/ #ifndef SW_Render #include #endif #include #include #include #include "types.h" #include "memory.h" #include "clouds.h" #include "vector.h" #include "matrix.h" #include "randy.h" #include "glinc.h" #include "render.h" #include "linkedlist.h" #include "universe.h" #include "fastmath.h" #include "statscript.h" #include "mainrgn.h" #include "clipper.h" #include "shader.h" #include "glcaps.h" #include "autolod.h" #include "univupdate.h" #include "devstats.h" #ifndef M_PI_2 #define M_PI_2 3.14159265358979323846 / 2.0 #endif extern udword gDevcaps2; static ubyte gCloudColor[4]; static real32 fogColor[4]; static GLuint _counter = 0; bool8 dontNebulate; real32 g_FogSum = 0.0f; real32 DUSTCLOUD_RADIAL_SCALE = 9.0f; real32 DUSTCLOUD_RADII[NUM_DUSTCLOUDTYPES] = {350.0f, 500.0f, 800.0f, 1300.0f}; static real32 DUSTCLOUD_BG_SCALAR = 0.3f; static real32 DUSTCLOUD_MAX_DENSITY = 0.9f; static real32 DUSTCLOUD_DENSITY_THRESHOLD = 0.2f; static real32 DUSTCLOUD_RED = 0.67f; static real32 DUSTCLOUD_GREEN = 0.57f; static real32 DUSTCLOUD_BLUE = 0.38f; static real32 DUSTCLOUD_RED_DIST = 0.05f; static real32 DUSTCLOUD_GREEN_DIST = 0.05f; static real32 DUSTCLOUD_BLUE_DIST = 0.05f; static real32 DUSTCLOUD_FOG_RED = 0.67f; static real32 DUSTCLOUD_FOG_GREEN = 0.57f; static real32 DUSTCLOUD_FOG_BLUE = 0.38f; static real32 DUSTCLOUD_RAN_MULTIPLIER = 0.007f; //lightning static real32 CLOUD_LDT0 = 800.0f; static real32 CLOUD_LDT1 = 200.0f; static real32 CLOUD_LIGHTNING_MAXDIST = 500.0f; static udword CLOUD_LIGHTNING_BRIGHTEN = 40; static real32 CLOUD_DISCHARGE_CHARGE = 0.02f; static real32 CLOUD_RADIAL_DAMAGE_SCALAR = 1.0f; static real32 CLOUD_ZAP_SCALAR = 0.7f; static real32 CLOUD_LIGHTNING_WIDTH_UPCLOSE = 10.0f; static real32 CLOUD_LIGHTNING_WIDTH_FARAWAY = 13.0f; static real32 CLOUD_LIGHTNING_LINE_RED = 0.47f; static real32 CLOUD_LIGHTNING_LINE_GREEN = 0.47f; static real32 CLOUD_LIGHTNING_LINE_BLUE = 0.675f; static real32 CLOUD_LIGHTNING_FRINGE_SCALAR = 1.5f; static real32 CLOUD_LIGHTNING_MIDPOINT_SCALAR = 0.8f; static real32 CLOUD_LIGHTNING_FRINGE_RED = 0.0f; static real32 CLOUD_LIGHTNING_FRINGE_GREEN = 0.0f; static real32 CLOUD_LIGHTNING_FRINGE_BLUE = 0.824f; static real32 CLOUD_LIGHTNING_FRINGE_ALPHA = 0.294f; static real32 CLOUD_LIGHTNING_MAIN_RED = 0.47f; static real32 CLOUD_LIGHTNING_MAIN_GREEN = 0.47f; static real32 CLOUD_LIGHTNING_MAIN_BLUE = 0.51f; static real32 CLOUD_LIGHTNING_MAIN_ALPHA = 0.55f; //cloud tweakables. see ~/data/cloud.script scriptEntry CloudTweaks[] = { makeEntry(DUSTCLOUD_RADIAL_SCALE, scriptSetReal32CB), makeEntry(DUSTCLOUD_RADII[DustCloud0],scriptSetReal32CB), makeEntry(DUSTCLOUD_RADII[DustCloud1],scriptSetReal32CB), makeEntry(DUSTCLOUD_RADII[DustCloud2],scriptSetReal32CB), makeEntry(DUSTCLOUD_RADII[DustCloud3],scriptSetReal32CB), makeEntry(DUSTCLOUD_BG_SCALAR, scriptSetReal32CB), makeEntry(DUSTCLOUD_MAX_DENSITY, scriptSetReal32CB), makeEntry(DUSTCLOUD_DENSITY_THRESHOLD, scriptSetReal32CB), makeEntry(DUSTCLOUD_RED, scriptSetReal32CB), makeEntry(DUSTCLOUD_GREEN, scriptSetReal32CB), makeEntry(DUSTCLOUD_BLUE, scriptSetReal32CB), makeEntry(DUSTCLOUD_RED_DIST, scriptSetReal32CB), makeEntry(DUSTCLOUD_GREEN_DIST, scriptSetReal32CB), makeEntry(DUSTCLOUD_BLUE_DIST, scriptSetReal32CB), makeEntry(DUSTCLOUD_FOG_RED, scriptSetReal32CB), makeEntry(DUSTCLOUD_FOG_GREEN, scriptSetReal32CB), makeEntry(DUSTCLOUD_FOG_BLUE, scriptSetReal32CB), makeEntry(DUSTCLOUD_RAN_MULTIPLIER, scriptSetReal32CB), makeEntry(CLOUD_LDT0, scriptSetReal32CB), makeEntry(CLOUD_LDT1, scriptSetReal32CB), makeEntry(CLOUD_LIGHTNING_MAXDIST, scriptSetReal32CB), makeEntry(CLOUD_LIGHTNING_BRIGHTEN, scriptSetUdwordCB), makeEntry(CLOUD_RADIAL_DAMAGE_SCALAR, scriptSetReal32CB), makeEntry(CLOUD_ZAP_SCALAR, scriptSetReal32CB), makeEntry(CLOUD_LIGHTNING_WIDTH_UPCLOSE, scriptSetReal32CB), makeEntry(CLOUD_LIGHTNING_WIDTH_FARAWAY, scriptSetReal32CB), makeEntry(CLOUD_LIGHTNING_LINE_RED, scriptSetReal32CB), makeEntry(CLOUD_LIGHTNING_LINE_GREEN, scriptSetReal32CB), makeEntry(CLOUD_LIGHTNING_LINE_BLUE, scriptSetReal32CB), makeEntry(CLOUD_LIGHTNING_FRINGE_SCALAR, scriptSetReal32CB), makeEntry(CLOUD_LIGHTNING_MIDPOINT_SCALAR, scriptSetReal32CB), makeEntry(CLOUD_LIGHTNING_FRINGE_RED, scriptSetReal32CB), makeEntry(CLOUD_LIGHTNING_FRINGE_GREEN, scriptSetReal32CB), makeEntry(CLOUD_LIGHTNING_FRINGE_BLUE, scriptSetReal32CB), makeEntry(CLOUD_LIGHTNING_FRINGE_ALPHA, scriptSetReal32CB), makeEntry(CLOUD_LIGHTNING_MAIN_RED, scriptSetReal32CB), makeEntry(CLOUD_LIGHTNING_MAIN_GREEN, scriptSetReal32CB), makeEntry(CLOUD_LIGHTNING_MAIN_BLUE, scriptSetReal32CB), makeEntry(CLOUD_LIGHTNING_MAIN_ALPHA, scriptSetReal32CB), endEntry }; /* * LIGHTNING DATA / FUNCTIONS */ lightning _lightnings[MAX_LIGHTNINGS]; //return a fresh lightning handle lhandle cloudGetFreshLightningHandle() { udword i; for (i = 1; i < MAX_LIGHTNINGS; i++) { if (!bitTest(_lightnings[i].flags, LIGHTNING_ACTIVE)) { return (lhandle)i; } } return 0; } //return a fresh lightning structure lightning* cloudGetFreshLightning() { lhandle handle = cloudGetFreshLightningHandle(); return (handle == 0) ? NULL : &_lightnings[handle]; } //clean up a lightning structure void cloudInitLightning(lightning* l) { udword i; vector origin = {0.0f, 0.0f, 0.0f}; dbgAssert(l != NULL); for (i = 0; i < MAX_LIGHTNING_PALS; i++) { l->pals[i] = 0; } l->pointA = origin; l->pointB = origin; l->distance = 0.0f; l->parent = NULL; l->countdown = 0; } //clean up a lightning structure given its handle void cloudInitLightningHandle(lhandle handle) { dbgAssert(handle != 0); cloudInitLightning(&_lightnings[handle]); } //return a structure given a handle lightning* cloudLightningFromHandle(lhandle handle) { return (handle == 0) ? NULL : &_lightnings[handle]; } //"kill" a lightning structure void cloudKillLightning(lightning* l) { udword i; dbgAssert(l != NULL); l->flags = 0; for (i = 0; i < MAX_LIGHTNING_PALS; i++) { if (l->pals[i] != 0) { lightning* light = cloudLightningFromHandle(l->pals[i]); light->flags = 0; } } } //"live" a lightning structure void cloudLiveLightning(lightning* l) { dbgAssert(l != NULL); bitSet(l->flags, LIGHTNING_ACTIVE); } //main-ify a lightning structure void cloudMainLightning(lightning* l) { dbgAssert(l != NULL); bitSet(l->flags, LIGHTNING_MAINSEGMENT); } /* * ELLIPSE DATA / FUNCTIONS */ static bool8 g_NoBumpy = FALSE; //geometry for the cloud lods static ellipseObject ellipseLOD[5]; ellipseObject genericEllipse[4]; //prototypes void ellipsoid_init(sdword n); void ellipsoid_seq(ellipseObject* ellipsoid, sdword n, float a, float b, float c); typedef struct slot { float cos, sin; enum { None, Only, Done } flag; } slot; static sdword n_max = 0; static slot* table = NULL; static vertex* octant = NULL; #define SetP(p,px,py,pz) \ { \ (p).x = px; \ (p).y = py; \ (p).z = pz; \ } #if 0 #define SetV(v,px,py,pz,nx,ny,nz) \ { \ SetP((v)->p,px,py,pz); \ SetP((v)->n,nx,ny,nz); \ } #else void SetV(vertex* v, real32 x, real32 y, real32 z, real32 nx, real32 ny, real32 nz) { real32 rand = (real32)(ranRandom(RAN_Clouds) % 10) * DUSTCLOUD_RAN_MULTIPLIER; if (ranRandom(RAN_Clouds) & 1) { rand = -rand; } if (g_NoBumpy) { rand = 0.0f; } v->p.x = x + rand; v->p.y = y + rand; v->p.z = z + rand; v->n.x = nx; v->n.y = ny; v->n.z = nz; } #endif #define SetF(f,i0,i1,i2) \ { \ (f)->v0 = i0; \ (f)->v1 = i1; \ (f)->v2 = i2; \ } /*----------------------------------------------------------------------------- Name : ellipsoid_render Description : renders an ellipseObject structure at given radius Inputs : ellipse - the ellipse object to render radius - coordinate multiplier (unit sized ellipse scalar) Outputs : draws an appropriate shape Return : ----------------------------------------------------------------------------*/ void ellipsoid_render(ellipseObject* ellipse, real32 radius) { alodIncPolys(ellipse->nf / 2); if (RGL) { sdword f, i, vert[3]; real32 x, y, z; glShadeModel(GL_SMOOTH); glBegin(GL_TRIANGLES); for (f = 0; f < ellipse->nf; f++) { vert[0] = ellipse->f[f].v0; vert[1] = ellipse->f[f].v1; vert[2] = ellipse->f[f].v2; for (i = 0; i < 3; i++) { glNormal3fv((GLfloat*)&ellipse->v[vert[i]].n); x = ellipse->v[vert[i]].p.x * radius; y = ellipse->v[vert[i]].p.y * radius; z = ellipse->v[vert[i]].p.z * radius; glVertex3f(x, y, z); #if RND_POLY_STATS rndNumberPolys++; rndNumberSmoothed++; #endif } } glEnd(); } else { sdword f, i, vert[3]; real32 x, y, z; real32 modelview[16], modelviewInv[16]; vector vertex; sdword lightOn; GLubyte colour[4]; lightOn = rndLightingEnable(FALSE); glGetFloatv(GL_MODELVIEW_MATRIX, modelview); shInvertMatrix(modelviewInv, modelview); if (bitTest(gDevcaps2, DEVSTAT2_NO_IALPHA) && glIsEnabled(GL_BLEND)) { glShadeModel(GL_FLAT); } else { glShadeModel(GL_SMOOTH); } glBegin(GL_TRIANGLES); for (f = 0; f < ellipse->nf; f++) { vert[0] = ellipse->f[f].v0; vert[1] = ellipse->f[f].v1; vert[2] = ellipse->f[f].v2; for (i = 0; i < 3; i++) { glNormal3fv((GLfloat*)&ellipse->v[vert[i]].n); x = ellipse->v[vert[i]].p.x * radius; y = ellipse->v[vert[i]].p.y * radius; z = ellipse->v[vert[i]].p.z * radius; vertex.x = x; vertex.y = y; vertex.z = z; colour[0] = gCloudColor[0]; colour[1] = gCloudColor[1]; colour[2] = gCloudColor[2]; colour[3] = gCloudColor[3]; shSpecularColour(0, 0, &vertex, (vector*)&ellipse->v[vert[i]].n, colour, modelview, modelviewInv); glColor4ub(colour[0], colour[1], colour[2], colour[3]); glVertex3f(x, y, z); #if RND_POLY_STATS rndNumberPolys++; rndNumberSmoothed++; #endif } } glEnd(); rndLightingEnable(lightOn); } } /*----------------------------------------------------------------------------- Name : ellipsoid_render_as Description : renders an ellipseObject structure at given radius as points at provided translation Inputs : ellipse - the ellipse object to render what - GL_TRIANGLES or GL_POINTS offset - add this to vertex position Outputs : draws an appropriate shape Return : ----------------------------------------------------------------------------*/ void ellipsoid_render_as(ellipseObject* ellipse, int what, vector* offset) { sdword f, i, vert[3]; real32 x, y, z; glBegin(what); for (f = 0; f < ellipse->nf; f++) { vert[0] = ellipse->f[f].v0; vert[1] = ellipse->f[f].v1; vert[2] = ellipse->f[f].v2; for (i = 0; i < 3; i++) { glNormal3fv((GLfloat*)&ellipse->v[vert[i]].n); x = ellipse->v[vert[i]].p.x + offset->x; y = ellipse->v[vert[i]].p.y + offset->y; z = ellipse->v[vert[i]].p.z + offset->z; glVertex3f(x, y, z); #if RND_POLY_STATS rndNumberPolys++; rndNumberSmoothed++; #endif } } glEnd(); } void ellipsoid_render_as_radii(ellipseObject* ellipse, int what, vector* offset, real32 ra, real32 rb, real32 rc) { sdword f, i, vert[3]; real32 x, y, z; glBegin(what); for (f = 0; f < ellipse->nf; f++) { vert[0] = ellipse->f[f].v0; vert[1] = ellipse->f[f].v1; vert[2] = ellipse->f[f].v2; for (i = 0; i < 3; i++) { glNormal3fv((GLfloat*)&ellipse->v[vert[i]].n); x = ra * ellipse->v[vert[i]].p.x + offset->x; y = rb * ellipse->v[vert[i]].p.y + offset->y; z = rc * ellipse->v[vert[i]].p.z + offset->z; glVertex3f(x, y, z); #if RND_POLY_STATS rndNumberPolys++; rndNumberSmoothed++; #endif } } glEnd(); } /*----------------------------------------------------------------------------- Name : ellipsoid_free Description : frees the memory allocated for all ellipse stuff Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void ellipsoid_free() { udword i; if (table) { memFree(table); table = NULL; } if (octant) { memFree(octant); octant = NULL; } for (i = 0; i < 4; i++) { if (ellipseLOD[i].v) { memFree(ellipseLOD[i].v); memFree(ellipseLOD[i].f); ellipseLOD[i].v = NULL; ellipseLOD[i].f = NULL; } if (genericEllipse[i].v) { memFree(genericEllipse[i].v); memFree(genericEllipse[i].f); genericEllipse[i].v = NULL; genericEllipse[i].f = NULL; } } if (ellipseLOD[4].v) { memFree(ellipseLOD[4].v); memFree(ellipseLOD[4].f); ellipseLOD[4].v = NULL; ellipseLOD[4].f = NULL; } } /*----------------------------------------------------------------------------- Name : ellipsoid_init Description : allocates and generates ellipse luts Inputs : n - max depth Outputs : Return : ----------------------------------------------------------------------------*/ void ellipsoid_init(sdword n) { sdword n_table, i, j, k, l, m, h, d; slot* t0; slot* t1; slot* t2; real32 theta; if (n > n_max) { n_max = n; if (table) { memFree(table); } if ((n_table = ((n-1)*n)/2) == 0) { table = NULL; } else { table = (slot*)memAlloc(n_table * sizeof(slot), "ellipse table", NonVolatile); } if (octant) { memFree(octant); } octant = (vertex*)memAlloc(((n+1)*(n+2))/2 * sizeof(vertex), "ellipse octant", NonVolatile); for (t0 = table, k = n_table; k > 0; k--, t0++) { t0->flag = None; } for (t0 = table, k = 0, l = 1, m = 3, i = 2; i <= n_max; i++) { l += m; m += 2; h = n_max / i - 1; for (t1 = t0+i - 2, j = 1; j < i; j++, k++, t0++, t1--) { if (t0->flag == None) { theta = (real32)((M_PI_2 * j) / i); t0->cos = t1->sin = (real32)cos((double)theta); t0->sin = t1->cos = (real32)sin((double)theta); t0->flag = t1->flag = Only; } if (t0->flag == Only) { t0->flag = Done; for (d = k+l, t2 = t0; h > 0; h--) { t2 += d; d += l; t2->cos = t0->cos; t2->sin = t0->sin; t2->flag = Done; } } } } } } /*----------------------------------------------------------------------------- Name : ellipsoid_octant Description : generates a single ellipse octant Inputs : n - depth a, b, c - radii Outputs : sets verts and norms of global octant structure Return : ----------------------------------------------------------------------------*/ static void ellipsoid_octant(sdword n, real32 a, real32 b, real32 c) { sdword i, j; real32 a_1, b_1, c_1; real32 cos_ph, sin_ph, px, py, pz, nx, ny, nz, nznz, rnorm, tmp; vertex* o = octant; slot* table_th; slot* table_ph; a_1 = 1.0f / a; b_1 = 1.0f / b; c_1 = 1.0f / c; o = octant; table_th = table; table_ph = table + ((n-1)*(n-2))/2; SetV(o, 0.0f, 0.0f, c, 0.0f, 0.0f, 1.0f); o++; for (i = 1; i < n; i++, table_ph++) { cos_ph = table_ph->cos; sin_ph = table_ph->sin; pz = cos_ph * c; nz = cos_ph * c_1; nznz = nz * nz; px = sin_ph * a; nx = sin_ph * a_1; rnorm = 1.0f / fsqrt(nx*nx + nznz); SetV(o, px, 0.0f, pz, nx*rnorm, 0.0f, nz*rnorm); o++; for (j = i; --j > 0; table_th++) { tmp = table_th->cos * sin_ph; px = tmp * a; nx = tmp * a_1; tmp = table_th->sin * sin_ph; py = tmp * b; ny = tmp * b_1; rnorm = 1.0f / fsqrt(nx*nx + ny*ny + nznz); SetV(o, px, py, pz, nx*rnorm, ny*rnorm, nz*rnorm); o++; } py = sin_ph * b; ny = sin_ph * b_1; rnorm = 1.0f / fsqrt(ny*ny + nznz); SetV(o, 0.0f, py, pz, 0.0f, ny*rnorm, nz*rnorm); o++; } SetV(o, a, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f); o++; for (j = i; --j > 0; table_th++) { tmp = table_th->cos; px = tmp * a; nx = tmp * a_1; tmp = table_th->sin; py = tmp * b; ny = tmp * b_1; rnorm = 1.0f / fsqrt(nx*nx + ny*ny); SetV(o, px, py, 0.0f, nx*rnorm, ny*rnorm, 0.0f); o++; } SetV(o, 0.0f, b, 0.0f, 0.0f, 1.0f, 0.0f); } /*----------------------------------------------------------------------------- Name : ellipsoid_seq Description : generates an entire ellipsoid Inputs : ellipsoid - ellipse structure to fill in n - depth a, b, c - radii Outputs : a generated ellipse object Return : ----------------------------------------------------------------------------*/ void ellipsoid_seq(ellipseObject* ellipsoid, sdword n, real32 a, real32 b, real32 c) { vertex *v, *o; face *f; sdword i, j, ko, kv, kw, kv0, kw0; /* Check parameters for validity. */ if (n <= 0 || n_max < n || a <= 0.0 || b <= 0.0 || c <= 0.0) { ellipsoid->nv = 0; ellipsoid->v = NULL; ellipsoid->nf = 0; ellipsoid->f = NULL; return; } /* Initialize the base octant. */ ellipsoid_octant(n, a, b, c); /* Allocate memories for vertices and faces. */ ellipsoid->nv = 4*n*n + 2; ellipsoid->nf = 8*n*n; ellipsoid->v = (vertex*)memAlloc(ellipsoid->nv * sizeof(vertex), "ellipse verts", NonVolatile); ellipsoid->f = (face*)memAlloc(ellipsoid->nf * sizeof(face), "ellipse faces", NonVolatile); /* Generate vertices of the ellipsoid from octant[]. */ v = ellipsoid->v; o = octant; #define op o->p #define on o->n SetV(v, op.x, op.y, op.z, on.x, on.y, on.z); /* the north pole */ v++; for (i = 0; ++i <= n;) { o += i; for (j = i; --j >= 0; o++, v++) /* 1st octant */ { SetV(v, op.x, op.y, op.z, on.x, on.y, on.z); } for (j = i; --j >= 0; o--, v++) /* 2nd octant */ { SetV(v, -op.x, op.y, op.z, -on.x, on.y, on.z); } for (j = i; --j >= 0; o++, v++) /* 3rd octant */ { SetV(v, -op.x, -op.y, op.z, -on.x, -on.y, on.z); } for (j = i; --j >= 0; o--, v++) /* 4th octant */ { SetV(v, op.x, -op.y, op.z, on.x, -on.y, on.z); } } for (; --i > 1;) { o -= i; for (j = i; --j > 0; o++, v++) /* 5th octant */ { SetV(v, op.x, op.y, -op.z, on.x, on.y, -on.z); } for (j = i; --j > 0; o--, v++) /* 6th octant */ { SetV(v, -op.x, op.y, -op.z, -on.x, on.y, -on.z); } for (j = i; --j > 0; o++, v++) /* 7th octant */ { SetV(v, -op.x, -op.y, -op.z, -on.x, -on.y, -on.z); } for (j = i; --j > 0; o--, v++) /* 8th octant */ { SetV(v, op.x, -op.y, -op.z, on.x, -on.y, -on.z); } } o--; SetV(v, -op.x, -op.y, -op.z, -on.x, -on.y, -on.z); /* the south pole */ #undef op #undef on /* Generate triangular faces of the ellipsoid. */ f = ellipsoid->f; kv = 0, kw = 1; for (i = 0; i < n; i++) { kv0 = kv, kw0 = kw; for (ko = 1; ko <= 3; ko++) /* the 1st, 2nd, 3rd octants */ { for (j = i;; j--) { SetF(f, kv, kw, ++kw); f++; if (j == 0) break; SetF(f, kv, kw, ++kv); f++; } } for (j = i;; j--) /* the 4th octant */ { if (j == 0) { SetF(f, kv0, kw, kw0); kv++; kw++; f++; break; } SetF(f, kv, kw, ++kw); f++; if (j == 1) { SetF(f, kv, kw, kv0); f++; } else { SetF(f, kv, kw, ++kv); f++; } } } for (; --i >= 0;) { kv0 = kv, kw0 = kw; for (ko = 5; ko <= 7; ko++) /* the 5th, 6th, 7th octants */ { for (j = i;; j--) { SetF(f, kv, kw, ++kv); f++; if (j == 0) break; SetF(f, kv, kw, ++kw); f++; } } for (j = i;; j--) /* the 8th octant */ { if (j == 0) { SetF(f, kv, kw0, kv0); kv++; kw++; f++; break; } SetF(f, kv, kw, ++kv); f++; if (j == 1) { SetF(f, kv, kw, kw0); f++; } else { SetF(f, kv, kw, ++kw); f++; } } } } /* * LITTLE HELPERS */ static real64 randomAngle(udword a) { udword r = ranRandom(RAN_Clouds) % a; return DEG_TO_RAD((real64)r); } static void _homogenize(vector* v, hvector* h) { real32 oneOverW; vecGrabVecFromHVec(*v, *h); if (h->w == 0.0f || h->w == 1.0f) return; oneOverW = 1.0f / h->w; v->x *= oneOverW; v->y *= oneOverW; v->z *= oneOverW; } real32 cloudRealDist(real32 n, real32 d) { real32 r = (real32)((real64)(ranRandom(RAN_Clouds) % 1000000) / 1000000.0); real32 sign = (ranRandom(RAN_Clouds) % 2 == 0) ? -1.0f : 1.0f; if (d < 0.0f) sign = -1.0f; return n + sign*r*d; } /* * FUNCTIONS */ /*----------------------------------------------------------------------------- Name : cloudStartup Description : allocates cloud data, generates spheres, sets up fog Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void cloudStartup() { fogColor[0] = DUSTCLOUD_FOG_RED; fogColor[1] = DUSTCLOUD_FOG_GREEN; fogColor[2] = DUSTCLOUD_FOG_BLUE; fogColor[3] = 0.5f; dontNebulate = FALSE; glFogi(GL_FOG_MODE, GL_LINEAR); ellipsoid_init(5); ellipsoid_seq(&ellipseLOD[0], 5, 1.0f, 1.0f, 1.0f); ellipsoid_seq(&ellipseLOD[1], 4, 1.0f, 1.0f, 1.0f); ellipsoid_seq(&ellipseLOD[2], 3, 1.0f, 1.0f, 1.0f); ellipsoid_seq(&ellipseLOD[3], 2, 1.0f, 1.0f, 1.0f); ellipsoid_seq(&ellipseLOD[4], 1, 1.0f, 1.0f, 1.0f); g_NoBumpy = TRUE; ellipsoid_seq(&genericEllipse[0], 5, 1.0f, 1.0f, 1.0f); ellipsoid_seq(&genericEllipse[1], 4, 1.0f, 1.0f, 1.0f); ellipsoid_seq(&genericEllipse[2], 3, 1.0f, 1.0f, 1.0f); ellipsoid_seq(&genericEllipse[3], 2, 1.0f, 1.0f, 1.0f); g_NoBumpy = FALSE; } //convenience void _clearcolorUniverse() { color c = universe.backgroundColor; if (!smSensorsActive) { rndSetClearColor(c); } } /*----------------------------------------------------------------------------- Name : cloudReset Description : resets cloud things Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void cloudReset() { _clearcolorUniverse(); rndFogOn = FALSE; dontNebulate = FALSE; } /*----------------------------------------------------------------------------- Name : cloudShutdown Description : shuts down the cloud stuff, frees memory &c Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void cloudShutdown() { cloudReset(); ellipsoid_free(); } /*----------------------------------------------------------------------------- Name : cloudCreateSystem Description : creates a new cloud system Inputs : radius - radius of the sphere variance - randomness parameter Outputs : Return : new cloudSystem, with not much initialized ----------------------------------------------------------------------------*/ cloudSystem* cloudCreateSystem(real32 radius, real32 variance) { udword i; cloudSystem* system = memAlloc(sizeof(cloudSystem), "cloud system", NonVolatile); system->flags = 0; system->radius = radius; system->position = NULL; system->rotation = NULL; system->healthFactor = 1.0f; system->cloudColor = colRGB((ubyte)(cloudRealDist(DUSTCLOUD_RED, DUSTCLOUD_RED_DIST) * 255.0f), (ubyte)(cloudRealDist(DUSTCLOUD_GREEN, DUSTCLOUD_GREEN_DIST) * 255.0f), (ubyte)(cloudRealDist(DUSTCLOUD_BLUE, DUSTCLOUD_BLUE_DIST) * 255.0f)); system->charge = 0.0f; for (i = 0; i < MAX_BURSTS; i++) { system->bursts[i] = 0; } return system; } /*----------------------------------------------------------------------------- Name : cloudDeleteSystem Description : frees the memory used by the given system Inputs : system - the cloud system Outputs : Return : ----------------------------------------------------------------------------*/ void cloudDeleteSystem(cloudSystem* system) { udword i; for (i = 0; i < MAX_BURSTS; i++) { if (system->bursts[i] != 0) { lightning* l = cloudLightningFromHandle(system->bursts[i]); cloudKillLightning(l); } } //free the container memFree(system); } /*----------------------------------------------------------------------------- Name : cloudRandomSphericalPoint Description : distributes a point randomly on a unit sphere Inputs : vec - the vector to hold the output point Outputs : vec - contains the output point Return : ----------------------------------------------------------------------------*/ void cloudRandomSphericalPoint(vector* vec) { hvector hvec, rvec; hmatrix result; real64 a = randomAngle(360); real32 sinTheta = (real32)sin(a); real32 cosTheta = (real32)cos(a); vecZeroVector(*vec); vec->x = 1.0f; vecMakeHVecFromVec(hvec, *vec); hmatMakeRotAboutZ(&result, sinTheta, cosTheta); hmatMultiplyHMatByHVec(&rvec, &result, &hvec); a = randomAngle(360); sinTheta = (real32)sin(a); cosTheta = (real32)cos(a); hmatMakeRotAboutX(&result, sinTheta, cosTheta); hmatMultiplyHMatByHVec(&hvec, &result, &rvec); rvec = hvec; hmatMultiplyHMatByHVec(&rvec, &result, &hvec); _homogenize(vec, &rvec); vecNormalize(vec); } /*----------------------------------------------------------------------------- Name : cloudPointInSphere Description : scales a point representing a point on a unit sphere by a permutation of the provided radius Inputs : point - the point radius - base radius Outputs : point is multiplied by a scalar Return : ----------------------------------------------------------------------------*/ void cloudPointInSphere(vector* point, real32 radius) { vecMultiplyByScalar(*point, cloudRealDist(0.75f * radius, 0.4f * radius)); } real32 cloudLightningDeviation() { return(0.54f * ((real32)(ranRandom(RAN_Clouds) % 100) - 50.0f)); } void cloudGetVcross(vector* from, vector* to, vector* vcross) { vector veye, vseg; vecSub(veye, *from, mrCamera->eyeposition); vecSub(vseg, *to, *from); vecNormalize(&veye); vecNormalize(&vseg); vecCrossProduct(*vcross, vseg, veye); vecNormalize(vcross); } /*----------------------------------------------------------------------------- Name : cloudRenderLightning Description : renders a lightning effect between 2 points Inputs : pa - start point pb - end point depth - depth of recursion lod - the lod number Outputs : Return : ----------------------------------------------------------------------------*/ void cloudRenderLightning(vector* pa, vector* pb, udword depth, sdword lod, vector* vcross) { vector from, to, mid; real32 width; switch (lod) { case 0: case 1: width = CLOUD_LIGHTNING_WIDTH_UPCLOSE; break; case 2: case 3: width = CLOUD_LIGHTNING_WIDTH_FARAWAY; break; default: width = 0.0f; } from = *pa; to = *pb; mid.x = (from.x + to.x)*0.5f + cloudLightningDeviation(); mid.y = (from.y + to.y)*0.5f + cloudLightningDeviation(); mid.z = (from.z + to.z)*0.5f + cloudLightningDeviation(); if (depth == 0) { if (width == 0.0f) { glColor3f(CLOUD_LIGHTNING_LINE_RED, CLOUD_LIGHTNING_LINE_GREEN, CLOUD_LIGHTNING_LINE_BLUE); glBegin(GL_LINES); glVertex3fv((real32*)&from); glVertex3fv((real32*)&mid); glVertex3fv((real32*)&mid); glVertex3fv((real32*)&to); glEnd(); } else { vector fromHi, midHi, toHi; vector _from, _mid, _to; vector subAmount; // ---- vecScalarMultiply(fromHi, *vcross, CLOUD_LIGHTNING_FRINGE_SCALAR * width); vecAddTo(fromHi, from); vecScalarMultiply(midHi, *vcross, CLOUD_LIGHTNING_FRINGE_SCALAR * CLOUD_LIGHTNING_MIDPOINT_SCALAR * width); vecAddTo(midHi, mid); vecScalarMultiply(toHi, *vcross, CLOUD_LIGHTNING_FRINGE_SCALAR * width); vecAddTo(toHi, to); _from = from; _mid = mid; _to = to; vecScalarMultiply(subAmount, *vcross, (CLOUD_LIGHTNING_FRINGE_SCALAR - 1.0f) * width); vecSubFrom(_from, subAmount); vecSubFrom(_mid, subAmount); vecSubFrom(_to, subAmount); glColor4f(CLOUD_LIGHTNING_FRINGE_RED, CLOUD_LIGHTNING_FRINGE_GREEN, CLOUD_LIGHTNING_FRINGE_BLUE, CLOUD_LIGHTNING_FRINGE_ALPHA); glEnable(GL_BLEND); glDisable(GL_CULL_FACE); glBegin(GL_QUADS); glVertex3fv((GLfloat*)&_from); glVertex3fv((GLfloat*)&fromHi); glVertex3fv((GLfloat*)&midHi); glVertex3fv((GLfloat*)&_mid); glVertex3fv((GLfloat*)&_mid); glVertex3fv((GLfloat*)&midHi); glVertex3fv((GLfloat*)&toHi); glVertex3fv((GLfloat*)&_to); glEnd(); // ---- vecScalarMultiply(fromHi, *vcross, width); vecAddTo(fromHi, from); vecScalarMultiply(midHi, *vcross, CLOUD_LIGHTNING_MIDPOINT_SCALAR * width); vecAddTo(midHi, mid); vecScalarMultiply(toHi, *vcross, width); vecAddTo(toHi, to); glColor4f(CLOUD_LIGHTNING_MAIN_RED, CLOUD_LIGHTNING_MAIN_GREEN, CLOUD_LIGHTNING_MAIN_BLUE, CLOUD_LIGHTNING_MAIN_ALPHA); glBegin(GL_QUADS); glVertex3fv((GLfloat*)&from); glVertex3fv((GLfloat*)&fromHi); glVertex3fv((GLfloat*)&midHi); glVertex3fv((GLfloat*)&mid); glVertex3fv((GLfloat*)&mid); glVertex3fv((GLfloat*)&midHi); glVertex3fv((GLfloat*)&toHi); glVertex3fv((GLfloat*)&to); glEnd(); // ---- glEnable(GL_CULL_FACE); glDisable(GL_BLEND); } } else { cloudRenderLightning(&from, &mid, depth-1, lod, vcross); cloudRenderLightning(&mid, &to, depth-1, lod, vcross); } } bool8 cloudHasLightning(cloudSystem* system) { udword i; for (i = 0; i < MAX_BURSTS; i++) { if (system->bursts[i] != 0) { return TRUE; } } return FALSE; } color cloudBrighten(color c, real32 charge) { sdword red = colRed(c); sdword green = colGreen(c); sdword blue = colBlue(c); real32 adjCharge = 2.0f * charge; sdword sBRIGHTER; if (adjCharge > 1.0f) adjCharge = 1.0f; sBRIGHTER = (sdword)((real32)CLOUD_LIGHTNING_BRIGHTEN * adjCharge); //FIXME: add slight randomization (flitter) red += sBRIGHTER; green += sBRIGHTER; blue += sBRIGHTER; if (red > 255) red = 255; if (green > 255) green = 255; if (blue > 255) blue = 255; return colRGB(red, green, blue); } /*----------------------------------------------------------------------------- Name : cloudRenderSystem Description : renders (displays) a cloud system Inputs : mesh - no longer used system - the cloud system lod - lod to render at Outputs : Return : ----------------------------------------------------------------------------*/ void cloudRenderSystem(void* mesh, cloudSystem* system, sdword lod) { GLfloat radius; vector origin = {0.0f, 0.0f, 0.0f}; color cloudColor = system->cloudColor; udword i; bool fogOn; extern vector g_RndPosition; if (system == NULL) { return; } fogOn = glIsEnabled(GL_FOG); if (fogOn) glDisable(GL_FOG); radius = system->radius * system->healthFactor; if (RGL) { if (rglIsClipped((GLfloat*)&origin, radius, radius, radius)) { return; } } if (clipBBoxIsClipped((real32*)&origin, radius, radius, radius)) { return; } glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE); rndTextureEnable(FALSE); if (RGL) { rglFeature(RGL_SPECULAR_RENDER); if (glIsEnabled(GL_POLYGON_STIPPLE)) { glShadeModel(GL_FLAT); } else { glShadeModel(GL_SMOOTH); } } else { glShadeModel(GL_SMOOTH); } glEnable(GL_BLEND); rndAdditiveBlends(FALSE); glDepthMask(GL_FALSE); if (cloudHasLightning(system)) { cloudColor = cloudBrighten(cloudColor, system->charge); } { GLubyte alpha; if (glCapFastFeature(GL_BLEND) || glIsEnabled(GL_POLYGON_STIPPLE)) { alpha = 127; } else { alpha = 192; } gCloudColor[0] = colRed(cloudColor); gCloudColor[1] = colGreen(cloudColor); gCloudColor[2] = colBlue(cloudColor); gCloudColor[3] = alpha; glColor4ub(gCloudColor[0], gCloudColor[1], gCloudColor[2], gCloudColor[3]); } if (glCapFastFeature(GL_BLEND)) { switch (lod) { case 0: // ellipsoid_render(&ellipseLOD[0], radius); // break; case 1: // ellipsoid_render(&ellipseLOD[1], radius); // break; case 2: ellipsoid_render(&ellipseLOD[2], radius); break; default: ellipsoid_render(&ellipseLOD[3], radius); } } else { ellipsoid_render(&ellipseLOD[3], radius); } glDisable(GL_BLEND); if (RGL) rglFeature(RGL_NORMAL_RENDER); rndLightingEnable(FALSE); //lightning effect in effect for (i = 0; i < MAX_BURSTS; i++) { udword depth; real32 distance; vector pointA, pointB, distvec; lightning* l; if (system->bursts[i] == 0) { continue; } l = cloudLightningFromHandle(system->bursts[i]); pointA = l->pointA; pointB = l->pointB; vecSub(distvec, pointB, pointA); distance = fsqrt(vecMagnitudeSquared(distvec)); //lod for lightning jaggies if (distance > CLOUD_LDT0) depth = 3; else if (distance > CLOUD_LDT1) depth = 2; else depth = 1; { vector vcross; cloudGetVcross(&pointA, &pointB, &vcross); rndAdditiveBlends(TRUE); cloudRenderLightning(&pointA, &pointB, depth, lod, &vcross); } } glDepthMask(GL_TRUE); rndLightingEnable(TRUE); rndAdditiveBlends(FALSE); if (fogOn) glEnable(GL_FOG); } //ship lightning render & think fn void cloudRenderAndUpdateLightning(lightning* l, sdword lod) { vector pointA, pointB, distvec; real32 distance; udword depth; pointA = l->pointA; pointB = l->pointB; vecSub(distvec, pointB, pointA); distance = vecMagnitudeSquared(distvec); distance = fsqrt(distance); if (distance > CLOUD_LDT0) depth = 3; else if (distance > CLOUD_LDT1) depth = 2; else depth = 1; rndTextureEnable(FALSE); glShadeModel(GL_SMOOTH); rndLightingEnable(FALSE); glDepthMask(GL_FALSE); { vector vcross; cloudGetVcross(&pointA, &pointB, &vcross); cloudRenderLightning(&pointA, &pointB, depth, lod, &vcross); } glDepthMask(GL_TRUE); rndLightingEnable(TRUE); } real32 cloudLightningDistance(lightning* l) { vector dist; vecSub(dist, l->pointB, l->pointA); return fsqrt(vecMagnitudeSquared(dist)); } void cloudCapLightningDistance(lightning* l) { real32 dist = cloudLightningDistance(l); if (dist > CLOUD_LIGHTNING_MAXDIST) { vector diff; //get direction vecSub(diff, l->pointB, l->pointA); vecNormalize(&diff); //make direction a scaled vector vecMultiplyByScalar(diff, CLOUD_LIGHTNING_MAXDIST); vecAddTo(diff, l->pointA); //assign as point b l->pointB = diff; } } void cloudCapLightningDistanceWithSuppliedRadius(lightning* l, real32 radius) { real32 dist = cloudLightningDistance(l); if (dist > radius) { vector diff; //get direction vecSub(diff, l->pointB, l->pointA); vecNormalize(&diff); //make direction a scaled vector vecMultiplyByScalar(diff, radius); vecAddTo(diff, l->pointA); //assign as point b l->pointB = diff; } } /*----------------------------------------------------------------------------- Name : cloudApplyDamage Description : actually applies damage to a ship Inputs : target - the ship damagetaken - the damage to take Outputs : Return : ----------------------------------------------------------------------------*/ void cloudApplyDamage(SpaceObjRotImpTarg* target, real32 damagetaken) { ApplyDamageToTarget(target, damagetaken, GS_SmallIonCannon,DEATH_Killed_By_Cloud,99); } /*----------------------------------------------------------------------------- Name : cloudNewLightning Description : creates a new lightning effect for attachment to a ship Inputs : system - the parent structure (generic type) radius - radius of the target, for sizing the lightning Outputs : Return : a fresh lightning structure ----------------------------------------------------------------------------*/ lightning* cloudNewLightning(void* system, real32 radius) { lhandle handle; lightning* l; handle = cloudGetFreshLightningHandle(); l = cloudLightningFromHandle(handle); cloudInitLightning(l); cloudLiveLightning(l); cloudMainLightning(l); l->parent = (cloudSystem*)system; //not necessarily a cloudSystem, mind you //frame, not tick-based l->countdown = (ubyte)((ranRandom(RAN_Clouds) % 18) + 14); cloudRandomSphericalPoint(&l->pointA); cloudPointInSphere(&l->pointA, radius); cloudRandomSphericalPoint(&l->pointB); cloudPointInSphere(&l->pointB, radius); l->distance = 5.0f * cloudLightningDistance(l); cloudCapLightningDistanceWithSuppliedRadius(l, 0.5f * radius); return l; } /*----------------------------------------------------------------------------- Name : cloudZapLoop Description : zaps nearby ships with electric death Inputs : system - the cloud system Outputs : Return : ----------------------------------------------------------------------------*/ void cloudZapLoop(cloudSystem* system) { Node* node; SpaceObj* spaceobj; real32 radius, radiusSquared; udword hits; radius = CLOUD_RADIAL_DAMAGE_SCALAR * system->radius * system->healthFactor; radiusSquared = radius * radius; hits = 0; //gather loop node = universe.SpaceObjList.head; while (node != NULL) { spaceobj = (SpaceObj*)listGetStructOfNode(node); if (spaceobj->objtype == OBJ_ShipType) { vector diff; vecSub(diff, *system->position, spaceobj->posinfo.position); if (vecMagnitudeSquared(diff) <= radiusSquared) { hits++; } } node = node->next; } //damage loop if (hits > 0) { real32 damage; damage = (CLOUD_ZAP_SCALAR * ((real32)system->maxhealth / (real32)hits)); node = universe.SpaceObjList.head; while (node != NULL) { spaceobj = (SpaceObj*)listGetStructOfNode(node); if (spaceobj->objtype == OBJ_ShipType) { vector diff; vecSub(diff, *system->position, spaceobj->posinfo.position); if (vecMagnitudeSquared(diff) <= radiusSquared) { Ship* ship = (Ship*)spaceobj; udword i; cloudApplyDamage((SpaceObjRotImpTarg*)spaceobj, damage); for (i = 0; i < 2; i++) { if (ship->lightning[i] != NULL) { cloudKillLightning(ship->lightning[i]); } ship->lightning[i] = cloudNewLightning( (void*)ship, ship->staticinfo->staticheader.staticCollInfo.collspheresize); } } } node = node->next; } } } /*----------------------------------------------------------------------------- Name : cloudNewBurst Description : create a long-lived burst of lightning in the system Inputs : system - the cloud system i - burst slot in system Outputs : Return : ----------------------------------------------------------------------------*/ void cloudNewBurst(cloudSystem* system, udword i) { lhandle handle; lightning* l; real32 radius; radius = 0.9f * system->radius * system->healthFactor; handle = cloudGetFreshLightningHandle(); system->bursts[i] = handle; l = cloudLightningFromHandle(handle); cloudInitLightning(l); cloudLiveLightning(l); cloudMainLightning(l); l->parent = system; l->countdown = (ubyte)((ranRandom(RAN_Clouds) % 2) + 1); cloudRandomSphericalPoint(&l->pointA); cloudPointInSphere(&l->pointA, radius); cloudRandomSphericalPoint(&l->pointB); cloudPointInSphere(&l->pointB, radius); l->distance = 10.0f * cloudLightningDistance(l); cloudCapLightningDistance(l); } /*----------------------------------------------------------------------------- Name : cloudUpdateSystem Description : determines whether randomized lightning should be displayed. maintains the state of associated lightning objects Inputs : system - the cloud system structure Outputs : possibly alters the cloud state Return : ----------------------------------------------------------------------------*/ void cloudUpdateSystem(cloudSystem* system) { udword i, thresh; thresh = (udword)(system->charge * 20.0f); if (bitTest(system->flags, LIGHTNING_DISCHARGING)) { //system is discharging, clear the flag and reset the charge bitClear(system->flags, LIGHTNING_DISCHARGING); system->charge = CLOUD_DISCHARGE_CHARGE; //zap internal and nearby external ships cloudZapLoop(system); //max out bursts on system for (i = 0; i < MAX_BURSTS; i++) { if (system->bursts[i] == 0) { cloudNewBurst(system, i); } } } for (i = 0; i < MAX_BURSTS; i++) { lightning* l; if (system->bursts[i] != 0) { l = cloudLightningFromHandle(system->bursts[i]); l->countdown--; if (l->countdown == 0) { real32 dist = cloudLightningDistance(l); //countdown expired, have we traveled far enough? if (dist >= l->distance) { //done, kill system->bursts[i] = 0; cloudKillLightning(l); } else { real32 radius = 0.9f * system->radius * system->healthFactor; //decrement our distance counter l->distance -= dist; //reset the countdown l->countdown = (ubyte)((ranRandom(RAN_Clouds) % 2) + 1); //endpoint -> startpoint memcpy(&l->pointA, &l->pointB, sizeof(vector)); //new endpoint cloudRandomSphericalPoint(&l->pointB); cloudPointInSphere(&l->pointB, radius); //cap distance cloudCapLightningDistance(l); } } else { #if 1 real32 dist = cloudLightningDistance(l); //maybe spawn a new guy right now to cover more of the distance if (dist < l->distance) { udword j, hit; for (j = hit = 0; j < MAX_BURSTS; j++) { if (system->bursts[j] == 0) { hit = 1; break; } } if (hit != 0) { //found a slot, use it real32 radius; lightning* light; lhandle handle; //get a new lightning set up handle = cloudGetFreshLightningHandle(); system->bursts[j] = handle; light = cloudLightningFromHandle(handle); cloudInitLightning(light); cloudLiveLightning(light); cloudMainLightning(light); light->parent = system; radius = 0.9f * system->radius * system->healthFactor; light->countdown = (ubyte)((ranRandom(RAN_Clouds) % 2) + 1); //startpoint is previous lightning's endpoint memcpy(&light->pointA, &l->pointB, sizeof(vector)); //new endpoint cloudRandomSphericalPoint(&light->pointB); cloudPointInSphere(&light->pointB, radius); //cap distance traveled cloudCapLightningDistance(light); //set distance left to travel light->distance = l->distance - cloudLightningDistance(light); //previous guy's time is up l->distance = 0.0f; } } #endif } } else if (ranRandom(RAN_Clouds) % 1000 < thresh) { real32 radius; lhandle handle; lightning* l; //initialization handle = cloudGetFreshLightningHandle(); system->bursts[i] = handle; l = cloudLightningFromHandle(handle); cloudInitLightning(l); cloudLiveLightning(l); cloudMainLightning(l); l->parent = system; //timespan radius = 0.9f * system->radius * system->healthFactor; l->countdown = (ubyte)((ranRandom(RAN_Clouds) % 2) + 1); //point a { udword k, hit; //try to start at the endpoint of an existing burst for (k = hit = 0; k < MAX_BURSTS; k++) { if (system->bursts[k] != 0) { lightning* light = cloudLightningFromHandle(system->bursts[k]); memcpy(&l->pointA, &light->pointB, sizeof(vector)); hit = 1; break; } } if (hit == 0) { cloudRandomSphericalPoint(&l->pointA); cloudPointInSphere(&l->pointA, radius); } } //point b cloudRandomSphericalPoint(&l->pointB); cloudPointInSphere(&l->pointB, radius); //distance l->distance = 3.0f * cloudLightningDistance(l); //cap distance cloudCapLightningDistance(l); } } } /*----------------------------------------------------------------------------- Name : cloudSetFog Description : cloud "task" function, scans the renderlist and determines global fogginess &c Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void cloudSetFog() { Node* node; SpaceObj* spaceobj; real32 radius, sumOf = 0.0f, dist; if (++_counter & 1) { return; } node = universe.ResourceList.head; while (node != NULL) { spaceobj = (SpaceObj*)listGetStructOfNode(node); if (spaceobj->staticinfo == NULL) { //shots and such node = node->next; continue; } if (spaceobj->objtype == OBJ_DustType) { cloudUpdateSystem((cloudSystem*)((DustCloud*)spaceobj)->stub); } node = node->next; } node = universe.RenderList.head; while (node != NULL) { spaceobj = (SpaceObj*)listGetStructOfNode(node); if (spaceobj->staticinfo == NULL) { //shots and such node = node->next; continue; } if (spaceobj->objtype == OBJ_DustType) { dontNebulate = TRUE; //there's clouds here, don't do nebula fog radius = spaceobj->staticinfo->staticheader.staticCollInfo.collspheresize; dist = fsqrt(spaceobj->cameraDistanceSquared); if (dist != 0.0f) { sumOf += radius * (1.0f / dist); } } node = node->next; } sumOf *= 0.1f; g_FogSum = sumOf; if (sumOf <= 1e-2f) { //this is also the case if there are no clouds in the renderlist at all rndFogOn = FALSE; _clearcolorUniverse(); } else { int r, g, b; if (sumOf > DUSTCLOUD_DENSITY_THRESHOLD) { GLfloat sum = sumOf - DUSTCLOUD_DENSITY_THRESHOLD; rndFogOn = TRUE; if (sum > DUSTCLOUD_MAX_DENSITY) { sum = DUSTCLOUD_MAX_DENSITY; } glFogfv(GL_FOG_COLOR, fogColor); glFogf(GL_FOG_DENSITY, sum); } else { rndFogOn = FALSE; } if (sumOf > 1.0f) { sumOf = 1.0f; } if (!smSensorsActive) { r = (int)(DUSTCLOUD_FOG_RED * sumOf * DUSTCLOUD_BG_SCALAR * 255.0f); g = (int)(DUSTCLOUD_FOG_GREEN * sumOf * DUSTCLOUD_BG_SCALAR * 255.0f); b = (int)(DUSTCLOUD_FOG_BLUE * sumOf * DUSTCLOUD_BG_SCALAR * 255.0f); r += colRed(universe.backgroundColor); g += colGreen(universe.backgroundColor); b += colBlue(universe.backgroundColor); if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255; rndSetClearColor(colRGBA(r,g,b,255)); } } }