/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // cg_effects.c -- entity effects parsing and management #include "cg_local.h" /* ============================================================== LIGHT STYLE MANAGEMENT ============================================================== */ typedef struct { int length; float value[3]; float map[MAX_QPATH]; } cg_lightStyle_t; cg_lightStyle_t cg_lightStyle[MAX_LIGHTSTYLES]; static int lastofs; /* ================ CG_ClearLightStyles ================ */ void CG_ClearLightStyles( void ) { memset( cg_lightStyle, 0, sizeof( cg_lightStyle ) ); lastofs = -1; } /* ================ CG_RunLightStyles ================ */ void CG_RunLightStyles( void ) { int i; int ofs; cg_lightStyle_t *ls; ofs = cg.time / 100; if( ofs == lastofs ) return; lastofs = ofs; for( i = 0, ls = cg_lightStyle; i < MAX_LIGHTSTYLES; i++, ls++ ) { if( !ls->length ) { ls->value[0] = ls->value[1] = ls->value[2] = 1.0; continue; } if( ls->length == 1 ) ls->value[0] = ls->value[1] = ls->value[2] = ls->map[0]; else ls->value[0] = ls->value[1] = ls->value[2] = ls->map[ofs % ls->length]; } } /* ================ CG_SetLightStyle ================ */ void CG_SetLightStyle( int i ) { int j, k; char *s; s = cgs.configStrings[i+CS_LIGHTS]; j = strlen( s ); if( j >= MAX_QPATH ) CG_Error( "CL_SetLightstyle length = %i", j ); cg_lightStyle[i].length = j; for( k = 0; k < j; k++ ) cg_lightStyle[i].map[k] = (float)(s[k]-'a') / (float)('m'-'a'); } /* ================ CG_AddLightStyles ================ */ void CG_AddLightStyles( void ) { int i; cg_lightStyle_t *ls; for( i = 0, ls = cg_lightStyle; i < MAX_LIGHTSTYLES; i++, ls++ ) trap_R_AddLightStyleToScene( i, ls->value[0], ls->value[1], ls->value[2] ); } /* ============================================================== DLIGHT MANAGEMENT ============================================================== */ typedef struct cdlight_s { struct cdlight_s *prev, *next; vec3_t color; vec3_t origin; float radius; struct shader_s *shader; } cdlight_t; cdlight_t cg_dlights[MAX_DLIGHTS]; cdlight_t cg_dlights_headnode, *cg_free_dlights; /* ================ CG_ClearDlights ================ */ void CG_ClearDlights( void ) { int i; memset( cg_dlights, 0, sizeof( cg_dlights ) ); // link dynamic lights cg_free_dlights = cg_dlights; cg_dlights_headnode.prev = &cg_dlights_headnode; cg_dlights_headnode.next = &cg_dlights_headnode; for( i = 0; i < MAX_DLIGHTS - 1; i++ ) cg_dlights[i].next = &cg_dlights[i+1]; } /* =============== CG_AllocDlight =============== */ void CG_AllocDlight( vec3_t origin, float radius, float r, float g, float b, struct shader_s *shader ) { cdlight_t *dl; if( radius <= 0 ) return; if( cg_free_dlights ) { // take a free light if possible dl = cg_free_dlights; cg_free_dlights = dl->next; } else { // grab the oldest one otherwise dl = cg_dlights_headnode.prev; dl->prev->next = dl->next; dl->next->prev = dl->prev; } dl->radius = radius; VectorCopy( origin, dl->origin ); //VectorCopy( color, dl->color ); dl->color[0] = r; dl->color[1] = g; dl->color[2] = b; dl->shader = shader; // put the light at the start of the list dl->prev = &cg_dlights_headnode; dl->next = cg_dlights_headnode.next; dl->next->prev = dl; dl->prev->next = dl; } void CG_AddLightToScene( vec3_t org, float intensity, float r, float g, float b, struct shader_s *shader ) { CG_AllocDlight( org, intensity, r, g, b, shader ); } /* ================= CG_FreeDlight ================= */ static void CG_FreeDlight( cdlight_t *dl ) { // remove from linked active list dl->prev->next = dl->next; dl->next->prev = dl->prev; // insert into linked free list dl->next = cg_free_dlights; cg_free_dlights = dl; } /* =============== CG_AddDlights =============== */ void CG_AddDlights( void ) { cdlight_t *dl, *hnode, *next; hnode = &cg_dlights_headnode; for( dl = hnode->next; dl != hnode; dl = next ) { next = dl->next; trap_R_AddLightToScene( dl->origin, dl->radius, dl->color[0], dl->color[1], dl->color[2], dl->shader ); CG_FreeDlight( dl ); } } /* ============================================================== BLOB SHADOWS MANAGEMENT ============================================================== */ #ifdef CGAMEGETLIGHTORIGIN // I don't think I will really need a linked list for this, but // copy/pasting the dlights code is the easier thing right now #define MAX_CGSHADEBOXES MAX_EDICTS typedef struct cgshadebox_s { struct cgshadebox_s *prev, *next; vec3_t origin; vec3_t mins; vec3_t maxs; int entNum; struct shader_s *shader; } cgshadebox_t; cgshadebox_t cg_shadeboxes[MAX_CGSHADEBOXES]; cgshadebox_t cg_shadeboxes_headnode, *cg_free_shadeboxes; // ok, to not use decals space we need these arrays to store // the polygons info. We do not need the linked list nor // registration, tho, since they are linked in the renderer as // they are created and they aren't reused in the next frame #define MAX_BLOBSHADOW_VERTS 128 #define MAX_BLOBSHADOW_FRAGMENTS 64 static int numBlobShadows = 0; // cleared each frame static vec3_t cg_blobshadow_verts[MAX_CGSHADEBOXES][MAX_BLOBSHADOW_VERTS]; static vec2_t cg_blobshadow_stcoords[MAX_CGSHADEBOXES][MAX_BLOBSHADOW_VERTS]; static byte_vec4_t cg_blobshadow_colors[MAX_CGSHADEBOXES][MAX_BLOBSHADOW_VERTS]; void CG_AddBlobShadow( vec3_t origin, vec3_t dir, float orient, float radius, float r, float g, float b, float a, struct shader_s *shader ) { int i, j; poly_t poly; vec3_t axis[3]; vec3_t verts[MAX_BLOBSHADOW_VERTS]; byte_vec4_t color; fragment_t *fr, fragments[MAX_BLOBSHADOW_FRAGMENTS]; int numfragments; if( numBlobShadows >= MAX_CGSHADEBOXES ) return; // invalid if( radius <= 0 || VectorCompare( dir, vec3_origin ) ) return; // calculate orientation matrix VectorNormalize2( dir, axis[0] ); PerpendicularVector( axis[1], axis[0] ); RotatePointAroundVector( axis[2], axis[0], axis[1], orient ); CrossProduct( axis[0], axis[2], axis[1] ); numfragments = trap_R_GetClippedFragments( origin, radius, axis, // clip it MAX_BLOBSHADOW_VERTS, verts, MAX_BLOBSHADOW_FRAGMENTS, fragments ); // no valid fragments if( !numfragments ) return; // clamp and scale colors if( r < 0 ) r = 0; else if( r > 1 ) r = 255; else r *= 255; if( g < 0 ) g = 0; else if( g > 1 ) g = 255; else g *= 255; if( b < 0 ) b = 0; else if( b > 1 ) b = 255; else b *= 255; if( a < 0 ) a = 0; else if( a > 1 ) a = 255; else a *= 255; color[0] = ( qbyte )( r ); color[1] = ( qbyte )( g ); color[2] = ( qbyte )( b ); color[3] = ( qbyte )( a ); radius = 0.5f / radius; VectorScale( axis[1], radius, axis[1] ); VectorScale( axis[2], radius, axis[2] ); for( i = 0, fr = fragments; i < numfragments; i++, fr++ ) { if( fr->numverts > MAX_BLOBSHADOW_VERTS ) return; else if( fr->numverts <= 0 ) continue; // link the poly to its arrays & setup for drawing memset( &poly, 0, sizeof(poly) ); poly.verts = cg_blobshadow_verts[numBlobShadows]; poly.stcoords = cg_blobshadow_stcoords[numBlobShadows]; poly.colors = cg_blobshadow_colors[numBlobShadows]; poly.shader = shader; poly.numverts = fr->numverts; for( j = 0; j < fr->numverts; j++ ) { vec3_t v; VectorCopy( verts[fr->firstvert+j], poly.verts[j] ); VectorSubtract( poly.verts[j], origin, v ); poly.stcoords[j][0] = DotProduct( v, axis[1] ) + 0.5f; poly.stcoords[j][1] = DotProduct( v, axis[2] ) + 0.5f; *( int * )poly.colors[j] = *( int * )color; } trap_R_AddPolyToScene( &poly ); numBlobShadows++; } } /* ================ CG_ClearShadeBoxes ================ */ void CG_ClearShadeBoxes( void ) { int i; memset( cg_shadeboxes, 0, sizeof( cg_shadeboxes ) ); // link shadeboxes cg_free_shadeboxes = cg_shadeboxes; cg_shadeboxes_headnode.prev = &cg_shadeboxes_headnode; cg_shadeboxes_headnode.next = &cg_shadeboxes_headnode; for( i = 0; i < MAX_CGSHADEBOXES - 1; i++ ) cg_shadeboxes[i].next = &cg_shadeboxes[i+1]; } /* =============== CG_AllocShadeBox =============== */ void CG_AllocShadeBox( int entNum, const vec3_t origin, const vec3_t mins, const vec3_t maxs, struct shader_s *shader ) { cgshadebox_t *sb; float dist; vec3_t dir; if( cg_shadows->integer != 1 ) return; // Kill if behind the view or if too far away VectorSubtract( origin, cg.refdef.vieworg, dir ); dist = VectorNormalize2( dir, dir ) * cg.view_fracDistFOV; if( dist > 1024 ) return; if( DotProduct( dir, cg.v_forward ) < 0 ) return; if( cg_free_shadeboxes ) { // take a free light if possible sb = cg_free_shadeboxes; cg_free_shadeboxes = sb->next; } else { // grab the oldest one otherwise sb = cg_shadeboxes_headnode.prev; sb->prev->next = sb->next; sb->next->prev = sb->prev; } VectorCopy( origin, sb->origin ); VectorCopy( mins, sb->mins ); VectorCopy( maxs, sb->maxs ); sb->entNum = entNum; sb->shader = shader; if( !sb->shader ) sb->shader = CG_MediaShader( cgs.media.shaderPlayerShadow ); // put the light at the start of the list sb->prev = &cg_shadeboxes_headnode; sb->next = cg_shadeboxes_headnode.next; sb->next->prev = sb; sb->prev->next = sb; } /* ================= CG_FreeShadeBox ================= */ static void CG_FreeShadeBox( cgshadebox_t *sb ) { // remove from linked active list sb->prev->next = sb->next; sb->next->prev = sb->prev; // insert into linked free list sb->next = cg_free_shadeboxes; cg_free_shadeboxes = sb; } /* =============== CG_AddShadeBoxes - Which in reality means CalcBlobShadows Note: This function should be called after every dynamic light has been added to the rendering list. ShadeBoxes exist for the solely reason of waiting until all dlights are sent before doing the shadows. =============== */ #define SHADOW_PROJECTION_DISTANCE 96 #define SHADOW_MAX_SIZE 100 #define SHADOW_MIN_SIZE 24 void CG_AddShadeBoxes( void ) { // ok, what we have to do here is finding the light direction of each of the shadeboxes origins cgshadebox_t *sb, *hnode, *next; vec3_t lightdir, end, sborigin; trace_t trace; if( cg_shadows->integer != 1 ) return; // clean up the polygons list from old frames numBlobShadows = 0; hnode = &cg_shadeboxes_headnode; for( sb = hnode->next; sb != hnode; sb = next ) { next = sb->next; VectorClear( lightdir ); trap_R_LightGridForOrigin( sb->origin, lightdir, NULL, NULL, RadiusFromBounds( sb->mins, sb->maxs ) ); // move the point we will project close to the bottom of the bbox (so shadow doesn't dance much to the sides) VectorSet( sborigin, sb->origin[0], sb->origin[1], sb->origin[2] + sb->mins[2] + 8 ); VectorMA( sborigin, -SHADOW_PROJECTION_DISTANCE, lightdir, end ); //CG_DrawTestLine( sb->origin, end ); // lightdir testline CG_Trace( &trace, sborigin, vec3_origin, vec3_origin, end, sb->entNum, MASK_OPAQUE ); if( trace.fraction < 1.0f ) { // we have a shadow float blobradius; float alpha, maxalpha = 0.95f; vec3_t shangles; VecToAngles( lightdir, shangles ); blobradius = SHADOW_MIN_SIZE + trace.fraction * (SHADOW_MAX_SIZE-SHADOW_MIN_SIZE); alpha = (1.0f - trace.fraction) * maxalpha; CG_AddBlobShadow( trace.endpos, trace.plane.normal, shangles[YAW], blobradius, 1, 1, 1, alpha, sb->shader ); } CG_FreeShadeBox( sb ); // remove box from list } } #endif /* ============================================================== PARTICLE MANAGEMENT ============================================================== */ typedef struct particle_s { float time; vec3_t org; vec3_t vel; vec3_t accel; vec3_t color; float alpha; float alphavel; float scale; poly_t poly; vec3_t pVerts[4]; vec2_t pStcoords[4]; byte_vec4_t pColor[4]; struct shader_s *shader; } cparticle_t; #define PARTICLE_GRAVITY 40 #define MAX_PARTICLES 2048 static vec3_t avelocities [NUMVERTEXNORMALS]; cparticle_t particles[MAX_PARTICLES]; int cg_numparticles; /* =============== CG_ClearParticles =============== */ void CG_ClearParticles( void ) { int i; cparticle_t *p; cg_numparticles = 0; memset( particles, 0, sizeof(cparticle_t) * MAX_PARTICLES ); for( i = 0, p = particles; i < MAX_PARTICLES; i++, p++ ) { p->pStcoords[0][0] = 0; p->pStcoords[0][1] = 1; p->pStcoords[1][0] = 0; p->pStcoords[1][1] = 0; p->pStcoords[2][0] = 1; p->pStcoords[2][1] = 0; p->pStcoords[3][0] = 1; p->pStcoords[3][1] = 1; } } /* #define CG_InitParticle(p,s,a,r,g,b) \ ( \ (p)->time = cg.time, \ (p)->scale = (s), \ (p)->alpha = (a), \ (p)->color[0] = (r), \ (p)->color[1] = (g), \ (p)->color[2] = (b) \ )*/ static void CG_InitParticle( cparticle_t *p, float s, float a, float r, float g, float b, struct shader_s *shader ) { p->time = cg.time; p->scale = (s); p->alpha = (a); p->color[0] = (r); p->color[1] = (g); p->color[2] = (b); p->shader = shader; } extern cvar_t *cg_playerTrailsColor; void CG_AddLinearTrail( centity_t *cent, float lifetime ) { cparticle_t *p; float r,g,b; // only allow trail in race mode or if watching a demos if(!(cg.demoPlaying || cg.frame.playerState.stats[STAT_GAMETYPE]==GAMETYPE_RACE) ) return; if( cg_numparticles + 1 > MAX_PARTICLES ) return; if(cg_playerTrailsColor->string!=NULL && sscanf(cg_playerTrailsColor->string,"%f %f %f",&r,&g,&b)==3) { if(r<0.0f) r=0.0f; if(r>1.0f) r=1.0f; if(g<0.0f) g=0.0f; if(g>1.0f) g=1.0f; if(b<0.0f) b=0.0f; if(b>1.0f) b=1.0f; } else { r=0.0f; g=1.0f; b=0.0f; } p = &particles[cg_numparticles++]; CG_InitParticle( p, 1.0f, 1.0f, r, g, b, NULL ); VectorCopy( cent->ent.origin, p->org ); p->alphavel = -(1.0f / lifetime); } /* =============== CG_ParticleEffect Wall impact puffs =============== */ void CG_ParticleEffect( vec3_t org, vec3_t dir, float r, float g, float b, int count ) { int j; cparticle_t *p; float d; if( !cg_particles->integer ) return; if( cg_numparticles + count > MAX_PARTICLES ) count = MAX_PARTICLES - cg_numparticles; for( p = &particles[cg_numparticles], cg_numparticles += count; count > 0; count--, p++ ) { CG_InitParticle( p, 1, 1, r + random()*0.1, g + random()*0.1, b + random()*0.1, NULL ); d = rand () & 31; for( j = 0; j < 3; j++ ) { p->org[j] = org[j] + ((rand()&7) - 4) + d * dir[j]; p->vel[j] = crandom() * 20; } p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY; p->alphavel = -1.0 / (0.5 + random() * 0.3); } } /* =============== CG_ParticleEffect2 =============== */ void CG_ParticleEffect2( vec3_t org, vec3_t dir, float r, float g, float b, int count ) { int j; float d; cparticle_t *p; if( !cg_particles->integer ) return; if( cg_numparticles + count > MAX_PARTICLES ) count = MAX_PARTICLES - cg_numparticles; for( p = &particles[cg_numparticles], cg_numparticles += count; count > 0; count--, p++ ) { CG_InitParticle( p, 1, 1, r, g, b, NULL ); d = rand()&7; for( j = 0; j < 3; j++ ) { p->org[j] = org[j] + ((rand()&7) - 4) + d * dir[j]; p->vel[j] = crandom() * 20; } p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY; p->alphavel = -1.0 / (0.5 + random() * 0.3); } } /* =============== CG_BlasterParticles Wall impact puffs =============== */ void CG_BlasterParticles( vec3_t org, vec3_t dir ) { int j, count = 40; float d; cparticle_t *p; if( !cg_particles->integer ) return; if( cg_numparticles + count > MAX_PARTICLES ) count = MAX_PARTICLES - cg_numparticles; for( p = &particles[cg_numparticles], cg_numparticles += count; count > 0; count--, p++ ) { CG_InitParticle( p, 1, 1, 1.0f, 0.8f, 0, NULL ); d = rand() & 15; for( j = 0; j < 3; j++ ) { p->org[j] = org[j] + ((rand()&7) - 4) + d * dir[j]; p->vel[j] = dir[j] * 30 + crandom() * 40; } p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY; p->alphavel = -1.0 / (0.5 + random() * 0.3); } } /* =============== CG_BlasterTrail =============== */ void CG_BlasterTrail( vec3_t start, vec3_t end ) { int j, count; vec3_t move, vec; float len; //const float dec = 5.0f; const float dec = 3.0f; cparticle_t *p; if( !cg_particles->integer ) return; VectorCopy( start, move ); VectorSubtract( end, start, vec ); len = VectorNormalize( vec ); VectorScale( vec, dec, vec ); count = (int)(len / dec) + 1; if( cg_numparticles + count > MAX_PARTICLES ) count = MAX_PARTICLES - cg_numparticles; for( p = &particles[cg_numparticles], cg_numparticles += count; count > 0; count--, p++ ) { CG_InitParticle( p, 2.5f, 0.25f, 1.0f, 0.85f, 0, NULL ); p->alphavel = -1.0 / (0.1 + random() * 0.2); for( j = 0; j < 3; j++ ) { p->org[j] = move[j] + crandom(); p->vel[j] = crandom() * 5; } VectorClear( p->accel ); VectorAdd( move, vec, move ); } } /* =============== CG_ElectroWeakTrail =============== */ void CG_ElectroWeakTrail( vec3_t start, vec3_t end ) { int j, count; vec3_t move, vec; float len; const float dec = 5; cparticle_t *p; if( !cg_particles->integer ) return; VectorCopy( start, move ); VectorSubtract( end, start, vec ); len = VectorNormalize( vec ); VectorScale( vec, dec, vec ); count = (int)(len / dec) + 1; if( cg_numparticles + count > MAX_PARTICLES ) count = MAX_PARTICLES - cg_numparticles; for( p = &particles[cg_numparticles], cg_numparticles += count; count > 0; count--, p++ ) { CG_InitParticle( p, 2.0f, 0.8f, 1.0f, 1.0f, 1.0f, NULL ); p->alphavel = -1.0 / (0.2 + random() * 0.1); for( j = 0; j < 3; j++ ) { p->org[j] = move[j] + random();/* + crandom();*/ p->vel[j] = crandom() * 2; } VectorClear( p->accel ); VectorAdd( move, vec, move ); } } /* =============== CG_ImpactPufParticles Wall impact puffs =============== */ void CG_ImpactPufParticles( vec3_t org, vec3_t dir, int count, float scale, float r, float g, float b, float a, struct shader_s *shader ) { int j; float d; cparticle_t *p; if( !cg_particles->integer ) return; if( cg_numparticles + count > MAX_PARTICLES ) count = MAX_PARTICLES - cg_numparticles; for( p = &particles[cg_numparticles], cg_numparticles += count; count > 0; count--, p++ ) { CG_InitParticle( p, scale, a, r, g, b, shader ); d = rand() & 15; for( j = 0; j < 3; j++ ) { p->org[j] = org[j] + ((rand()&7) - 4) + d * dir[j]; p->vel[j] = dir[j] * 30 + crandom() * 40; } p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY; p->alphavel = -1.0 / (0.5 + random() * 0.3); } } /* =============== CG_ElectroIonsTrail =============== */ void CG_ElectroIonsTrail( vec3_t start, vec3_t end ) { int i, count; vec3_t move, vec; float len; const float dec2 = 8.0f; cparticle_t *p; if( !cg_particles->integer ) return; VectorSubtract( end, start, vec ); len = VectorNormalize( vec ); VectorScale( vec, dec2, vec ); VectorCopy( start, move ); count = (int)(len / dec2) + 1; if( cg_numparticles + count > MAX_PARTICLES ) count = MAX_PARTICLES - cg_numparticles; for( p = &particles[cg_numparticles], cg_numparticles += count; count > 0; count--, p++ ) { CG_InitParticle( p, 1.2f, 1, 0.8f + crandom()*0.1, 0.8f + crandom()*0.1, 0.8f + crandom()*0.1, NULL ); for( i = 0; i < 3; i++ ) { p->org[i] = move[i]; p->vel[i] = crandom()*4; } p->alphavel = -1.0 / (0.6 + random()*0.6); VectorClear( p->accel ); VectorAdd( move, vec, move ); } } #define BEAMLENGTH 16 /* =============== CG_FlyParticles =============== */ void CG_FlyParticles( vec3_t origin, int count ) { int i; float angle, sr, sp, sy, cr, cp, cy; vec3_t forward, dir; float dist, ltime; cparticle_t *p; if( !cg_particles->integer ) return; if( count > NUMVERTEXNORMALS ) count = NUMVERTEXNORMALS; if( !avelocities[0][0] ) { for( i = 0; i < NUMVERTEXNORMALS*3; i++ ) avelocities[0][i] = (rand()&255) * 0.01; } i = 0; ltime = (float)cg.time / 1000.0; count /= 2; if( cg_numparticles + count > MAX_PARTICLES ) count = MAX_PARTICLES - cg_numparticles; for( p = &particles[cg_numparticles], cg_numparticles += count; count > 0; count--, p++ ) { CG_InitParticle( p, 1, 1, 0, 0, 0, NULL ); angle = ltime * avelocities[i][0]; sy = sin( angle ); cy = cos( angle ); angle = ltime * avelocities[i][1]; sp = sin( angle ); cp = cos( angle ); angle = ltime * avelocities[i][2]; sr = sin( angle ); cr = cos( angle ); forward[0] = cp*cy; forward[1] = cp*sy; forward[2] = -sp; dist = sin( ltime + i ) * 64; ByteToDir( i, dir ); p->org[0] = origin[0] + dir[0]*dist + forward[0]*BEAMLENGTH; p->org[1] = origin[1] + dir[1]*dist + forward[1]*BEAMLENGTH; p->org[2] = origin[2] + dir[2]*dist + forward[2]*BEAMLENGTH; VectorClear( p->vel ); VectorClear( p->accel ); p->alphavel = -100; i += 2; } } /* =============== CG_FlyEffect =============== */ void CG_FlyEffect( centity_t *ent, vec3_t origin ) { int n; int count; int starttime; if( !cg_particles->integer ) return; if( ent->fly_stoptime < cg.time ) { starttime = cg.time; ent->fly_stoptime = cg.time + 60000; } else { starttime = ent->fly_stoptime - 60000; } n = cg.time - starttime; if( n < 20000 ) { count = n * 162 / 20000.0; } else { n = ent->fly_stoptime - cg.time; if( n < 20000 ) count = n * 162 / 20000.0; else count = 162; } CG_FlyParticles( origin, count ); } // wsw specific particle effects below here //============================================================ //=============== //CG_BloodDamageParticles //=============== /* void CG_BloodDamageParticles( vec3_t org, vec3_t dir, int damage ) { int j; float d; cparticle_t *p; int count; if( !cg_particles->integer ) return; count = max( damage, 20 ); if( cg_numparticles + count > MAX_PARTICLES ) count = MAX_PARTICLES - cg_numparticles; for( p = &particles[cg_numparticles], cg_numparticles += count; count > 0; count--, p++ ) { //CG_InitParticle( p, 2, 1, r + random()*0.1, g + random()*0.1, b + random()*0.1 ); CG_InitParticle( p, 2.0f, 1, 0.9 + crandom() * 0.5, 0, 0, NULL ); d = rand() & 15; for( j = 0; j < 3; j++ ) { p->org[j] = org[j] + ((rand()&7) - 4) + d * dir[j]; p->vel[j] = dir[j] * 30 + crandom() * 40; } p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY; p->alphavel = -1.0 / (0.5 + random() * 0.3); } } */ /* =============== CG_AddParticles =============== */ void CG_AddParticles( void ) { int i, j; float alpha; float time, time2; vec3_t org; vec3_t corner; byte_vec4_t color; //struct shader_s *shader; int maxparticle, activeparticles; float alphaValues[MAX_PARTICLES]; cparticle_t *p, *free_particles[MAX_PARTICLES]; if( !cg_numparticles ) return; j = 0; maxparticle = -1; activeparticles = 0; //shader = CG_MediaShader( cgs.media.shaderParticle ); for( i = 0, p = particles; i < cg_numparticles; i++, p++ ) { time = (cg.time - p->time) * 0.001f; alpha = alphaValues[i] = p->alpha + time * p->alphavel; if( alpha <= 0 ) { // faded out free_particles[j++] = p; continue; } maxparticle = i; activeparticles++; time2 = time * time * 0.5f; org[0] = p->org[0] + p->vel[0]*time + p->accel[0]*time2; org[1] = p->org[1] + p->vel[1]*time + p->accel[1]*time2; org[2] = p->org[2] + p->vel[2]*time + p->accel[2]*time2; color[0] = (qbyte)( bound( 0, p->color[0], 1.0f ) * 255 ); color[1] = (qbyte)( bound( 0, p->color[1], 1.0f ) * 255 ); color[2] = (qbyte)( bound( 0, p->color[2], 1.0f ) * 255 ); color[3] = (qbyte)( bound( 0, alpha, 1.0f ) * 255 ); *(int *)p->pColor[0] = *(int *)color; *(int *)p->pColor[1] = *(int *)color; *(int *)p->pColor[2] = *(int *)color; *(int *)p->pColor[3] = *(int *)color; corner[0] = org[0]; corner[1] = org[1] - 0.5f * p->scale; corner[2] = org[2] - 0.5f * p->scale; VectorSet( p->pVerts[0], corner[0], corner[1] + p->scale, corner[2] + p->scale ); VectorSet( p->pVerts[1], corner[0], corner[1], corner[2] + p->scale ); VectorSet( p->pVerts[2], corner[0], corner[1], corner[2] ); VectorSet( p->pVerts[3], corner[0], corner[1] + p->scale, corner[2] ); p->poly.numverts = 4; p->poly.verts = p->pVerts; p->poly.stcoords = p->pStcoords; p->poly.colors = p->pColor; p->poly.shader = (p->shader == NULL) ? CG_MediaShader( cgs.media.shaderParticle ) : p->shader; trap_R_AddPolyToScene( &p->poly ); } i = 0; while( maxparticle >= activeparticles ) { *free_particles[i++] = particles[maxparticle--]; while( maxparticle >= activeparticles ) { if( alphaValues[maxparticle] <= 0 ) maxparticle--; else break; } } cg_numparticles = activeparticles; } /* ============== CG_ClearEffects ============== */ void CG_ClearEffects( void ) { CG_ClearParticles(); CG_ClearDlights(); CG_ClearLightStyles(); #ifdef CGAMEGETLIGHTORIGIN CG_ClearShadeBoxes(); #endif } // ======================================================= // Unused code below // ======================================================= #ifdef THIS_IS_DISABLED /* =============== CG_BfgParticles =============== */ void CG_BfgParticles( vec3_t origin ) { int i, count; float angle, sr, sp, sy, cr, cp, cy; vec3_t v, forward, dir; float dist, ltime; cparticle_t *p; if( !cg_particles->integer ) return; if( !avelocities[0][0] ) { for( i = 0; i < NUMVERTEXNORMALS*3; i++ ) avelocities[0][i] = (rand()&255) * 0.01; } i = 0; ltime = (float)cg.time / 1000.0; count = NUMVERTEXNORMALS; if( cg_numparticles + count > MAX_PARTICLES ) count = MAX_PARTICLES - cg_numparticles; for( p = &particles[cg_numparticles], cg_numparticles += count; count > 0; count--, p++ ) { angle = ltime * avelocities[i][0]; sy = sin( angle ); cy = cos( angle ); angle = ltime * avelocities[i][1]; sp = sin( angle ); cp = cos( angle ); angle = ltime * avelocities[i][2]; sr = sin( angle ); cr = cos( angle ); forward[0] = cp*cy; forward[1] = cp*sy; forward[2] = -sp; dist = sin( ltime + i ) * 64; ByteToDir( i, dir ); p->org[0] = origin[0] + dir[0]*dist + forward[0]*BEAMLENGTH; p->org[1] = origin[1] + dir[1]*dist + forward[1]*BEAMLENGTH; p->org[2] = origin[2] + dir[2]*dist + forward[2]*BEAMLENGTH; VectorClear( p->vel ); VectorClear( p->accel ); VectorSubtract( p->org, origin, v ); dist = VectorLength( v ) / 90.0; CG_InitParticle( p, 1.5f, 1.0f - dist, 0, 1.5f * dist, 0, NULL ); if( p->color[1] > 1 ) p->color[1] = 1; p->alphavel = -100; i++; } } //=============== //CG_BigTeleportParticles //=============== void CG_BigTeleportParticles( vec3_t org ) { int count = 4096; float angle, dist; static float colortable[4] = {0.0625, 0.40625, 0.65625, 0.5625}; cparticle_t *p; if( cg_numparticles + count > MAX_PARTICLES ) count = MAX_PARTICLES - cg_numparticles; for( p = &particles[cg_numparticles], cg_numparticles += count; count > 0; count--, p++ ) { CG_InitParticle( p, 1, 1, colortable[rand()&3], 0, 0 ); angle = M_TWOPI * (rand()&1023)/1023.0; dist = rand() & 31; p->org[0] = org[0] + cos( angle ) * dist; p->vel[0] = cos( angle ) * (70 + (rand() & 63)); p->accel[0] = -cos( angle ) * 100; p->org[1] = org[1] + sin( angle ) * dist; p->vel[1] = sin( angle ) * (70 + (rand() & 63)); p->accel[1] = -sin( angle ) * 100; p->org[2] = org[2] + 8 + (rand()%90); p->vel[2] = -100 + (rand() & 31); p->accel[2] = PARTICLE_GRAVITY * 4; p->alphavel = -0.3 / (0.5 + random() * 0.3); } } /* =============== CG_RailTrail =============== */ void CG_RailTrail( vec3_t start, vec3_t end ) { int i, j, count; vec3_t move, vec; float d, c, s, len; vec3_t right, up, dir; const float dec1 = 3.2f, dec2 = 2.6f; cparticle_t *p; VectorSubtract( end, start, vec ); len = VectorNormalize( vec ); MakeNormalVectors( vec, right, up ); VectorScale( vec, dec1, vec ); VectorCopy( start, move ); i = 0; count = (int)(len / dec1) + 1; if( cg_numparticles + count > MAX_PARTICLES ) count = MAX_PARTICLES - cg_numparticles; for( p = &particles[cg_numparticles], cg_numparticles += count; count > 0; count--, p++ ) { d = i++ * 0.1; c = cos( d ); s = sin( d ); VectorScale( right, c, dir ); VectorMA( dir, s, up, dir ); if( random() > 0.9 ) CG_InitParticle( p, 2.0f, 1, 0.7 + crandom()*0.1, 0.3, 0.4 + crandom()*0.1 ); else CG_InitParticle( p, 2.0f, 1, 0.3 + crandom()*0.1, 0.3, 0.8 + crandom()*0.1 ); for( j = 0; j < 3; j++ ) { p->org[j] = move[j] + dir[j]*3; p->vel[j] = dir[j]*6; } p->alphavel = -1.0 / (1 + random()*0.2); VectorClear( p->accel ); VectorAdd ( move, vec, move ); } VectorSubtract( end, start, vec ); len = VectorNormalize( vec ); VectorScale( vec, dec2, vec ); VectorCopy( start, move ); count = (int)(len / dec2) + 1; if( cg_numparticles + count > MAX_PARTICLES ) count = MAX_PARTICLES - cg_numparticles; for( p = &particles[cg_numparticles], cg_numparticles += count; count > 0; count--, p++ ) { CG_InitParticle( p, 1.2f, 1, 0.7f + crandom()*0.1, 0.7f + crandom()*0.1, 0.7f + crandom()*0.1 ); for( j = 0; j < 3; j++ ) { p->org[j] = move[j]; p->vel[j] = crandom()*3; } p->alphavel = -1.0 / (0.6 + random()*0.2); VectorClear( p->accel ); VectorAdd( move, vec, move ); } } /* =============== CG_BFGExplosionParticles =============== */ void CG_BFGExplosionParticles( vec3_t org ) { int j, count = 256; cparticle_t *p; if( cg_numparticles + count > MAX_PARTICLES ) count = MAX_PARTICLES - cg_numparticles; for( p = &particles[cg_numparticles], cg_numparticles += count; count > 0; count--, p++ ) { CG_InitParticle( p, 1.5f, 1.0f, 0, 0.8f, 0 ); for( j = 0; j < 3; j++ ) { p->org[j] = org[j] + ((rand() & 31) - 16); p->vel[j] = (rand() & 383) - 192; } p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY; p->alphavel = -0.8 / (0.5 + random() * 0.3); } } #endif