This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/client/cl_effects.c

1393 lines
28 KiB
C

//=======================================================================
// Copyright XashXT Group 2008 ©
// cl_effects.c - entity effects parsing and management
//=======================================================================
#include "common.h"
#include "client.h"
#include "const.h"
/*
==============================================================
LIGHT STYLE MANAGEMENT
==============================================================
*/
typedef struct
{
int length;
float value[3];
float map[MAX_STRING];
} clightstyle_t;
clightstyle_t cl_lightstyle[MAX_LIGHTSTYLES];
static int lastofs;
/*
================
CL_ClearLightStyles
================
*/
void CL_ClearLightStyles( void )
{
Mem_Set( cl_lightstyle, 0, sizeof( cl_lightstyle ));
lastofs = -1;
}
/*
================
CL_RunLightStyles
================
*/
void CL_RunLightStyles( void )
{
int i, ofs;
clightstyle_t *ls;
ofs = cl.time / 100;
if( ofs == lastofs ) return;
lastofs = ofs;
for( i = 0, ls = cl_lightstyle; i < MAX_LIGHTSTYLES; i++, ls++ )
{
if( !ls->length )
{
VectorSet( ls->value, 1.0f, 1.0f, 1.0f );
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];
}
}
void CL_SetLightstyle( int i )
{
char *s;
int j, k;
s = cl.configstrings[i+CS_LIGHTSTYLES];
j = com.strlen( s );
if( j >= MAX_STRING )
Host_Error("CL_SetLightStyle: lightstyle %s is too long\n", s );
cl_lightstyle[i].length = j;
for( k = 0; k < j; k++ )
cl_lightstyle[i].map[k] = (float)(s[k]-'a') / (float)('m'-'a');
}
/*
================
CL_AddLightStyles
================
*/
void CL_AddLightStyles( void )
{
int i;
clightstyle_t *ls;
for( i = 0, ls = cl_lightstyle; i < MAX_LIGHTSTYLES; i++, ls++ )
re->AddLightStyle( i, ls->value );
}
/*
==============================================================
DLIGHT MANAGEMENT
==============================================================
*/
typedef struct
{
// these values common with dlight_t so don't move them
vec3_t origin;
union
{
vec3_t color; // dlight color
vec3_t angles; // spotlight angles
};
float intensity;
shader_t texture; // light image e.g. for flashlight
vec2_t cone; // spotlight cone
// cdlight_t private starts here
int key; // so entities can reuse same entry
int start; // stop lighting after this time
int end; // drop this each second
float radius; // radius (not an intensity)
bool fade;
bool free; // this light is unused at current time
} cdlight_t;
cdlight_t cl_dlights[MAX_DLIGHTS];
/*
================
CL_ClearDlights
================
*/
void CL_ClearDlights( void )
{
Mem_Set( cl_dlights, 0, sizeof( cl_dlights ));
}
/*
===============
CL_AllocDlight
===============
*/
cdlight_t *CL_AllocDlight( int key )
{
int i, time, index;
cdlight_t *dl;
// first look for an exact key match
if( key )
{
for( i = 0, dl = cl_dlights; i < MAX_DLIGHTS; i++, dl++ )
{
if( dl->key == key )
{
// reuse this light
Mem_Set( dl, 0, sizeof( *dl ));
dl->key = key;
return dl;
}
}
}
else
{
for( i = 0, dl = cl_dlights; i < MAX_DLIGHTS; i++, dl++ )
{
if( dl->free )
{
Mem_Set( dl, 0, sizeof( *dl ));
return dl;
}
}
}
// find the oldest light
time = cl.time;
index = 0;
for( i = 0, dl = cl_dlights; i < MAX_DLIGHTS; i++, dl++ )
{
if( dl->start < time )
{
time = dl->start;
index = i;
}
}
dl = &cl_dlights[index];
Mem_Set( dl, 0, sizeof( *dl ));
dl->key = key;
return dl;
}
/*
===============
CL_AddDLight
===============
*/
void CL_AddDLight( const float *org, const float *rgb, float radius, float time, int flags, int key )
{
cdlight_t *dl;
if( radius <= 0 )
{
MsgDev( D_WARN, "CL_AddDLight: ignore light with radius <= 0\n" );
return;
}
dl = CL_AllocDlight( key );
dl->free = false;
dl->texture = -1; // dlight
VectorCopy( org, dl->origin );
VectorCopy( rgb, dl->color );
dl->radius = radius;
dl->start = cl.time;
dl->end = dl->start + (time * 1000);
dl->fade = (flags & DLIGHT_FADE) ? true : false;
}
/*
===============
CL_AddSLight
===============
*/
void CL_AddSLight( const float *org, float *dir, float rad, float *cone, HSPRITE hLight, int key )
{
// FIXME: implement
}
/*
===============
CL_AddDLights
===============
*/
void CL_AddDLights( void )
{
cdlight_t *dl;
int i;
for( i = 0, dl = cl_dlights; i < MAX_DLIGHTS; i++, dl++ )
{
if( dl->free ) continue;
if( cl.time >= dl->end )
{
dl->free = true;
continue;
}
if( dl->fade )
{
dl->intensity = (float)(cl.time - dl->start) / (dl->end - dl->start);
dl->intensity = dl->radius * (1.0 - dl->intensity);
}
else dl->intensity = dl->radius; // const
re->AddDynLight( dl );
}
}
/*
================
CL_TestLights
if cl_testlights is set, create 32 lights models
================
*/
void CL_TestLights( void )
{
int i, j;
float f, r;
cdlight_t dl;
edict_t *ed = CL_GetLocalPlayer();
if( !cl_testlights->integer ) return;
Mem_Set( &dl, 0, sizeof( cdlight_t ));
for( i = 0; i < bound( 1, cl_testlights->integer, MAX_DLIGHTS ); i++ )
{
r = 64 * ( (i%4) - 1.5 );
f = 64 * (i/4) + 128;
for( j = 0; j < 3; j++ )
dl.origin[j] = cl.refdef.vieworg[j] + cl.refdef.forward[j] * f + cl.refdef.right[j] * r;
dl.color[0] = ((i%6)+1) & 1;
dl.color[1] = (((i%6)+1) & 2)>>1;
dl.color[2] = (((i%6)+1) & 4)>>2;
dl.intensity = 200;
dl.texture = -1;
if( !re->AddDynLight( &dl ))
break;
}
}
/*
==============================================================
PARTICLE MANAGEMENT
==============================================================
*/
struct particle_s
{
// this part are shared both client and engine
byte color; // colorIndex for R_GetPaletteColor
vec3_t org;
vec3_t vel;
float die;
float ramp;
int type;
// this part are private for engine
particle_t *next;
poly_t poly;
vec3_t pVerts[4];
vec2_t pStcoords[4];
rgba_t pColor[4];
};
float cl_avertexnormals[NUMVERTEXNORMALS][3] =
{
#include "anorms.h"
};
// particle ramps
int ramp1[8] = { 0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61 };
int ramp2[8] = { 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66 };
int ramp3[8] = { 0x6d, 0x6b, 6, 5, 4, 3 };
particle_t *cl_active_particles, *cl_free_particles;
static vec3_t cl_particle_avelocities[NUMVERTEXNORMALS];
static particle_t cl_particle_list[MAX_PARTICLES];
static rgb_t cl_particlePalette[256];
void CL_GetPaletteColor( int colorIndex, vec3_t outColor )
{
if( !outColor ) return;
colorIndex = bound( 0, colorIndex, 255 );
VectorCopy( cl_particlePalette[colorIndex], outColor );
}
/*
=================
CL_AllocParticle
=================
*/
particle_t *CL_AllocParticle( void )
{
particle_t *p;
if( !cl_free_particles )
return NULL;
p = cl_free_particles;
cl_free_particles = p->next;
p->next = cl_active_particles;
cl_active_particles = p;
return p;
}
/*
===============
CL_ClearParticles
===============
*/
void CL_ClearParticles( void )
{
int i;
particle_t *p;
rgbdata_t *pic;
byte buf[1]; // fake stub
pic = FS_LoadImage( "#quake.pal", buf, 768 );
cl_active_particles = NULL;
cl_free_particles = cl_particle_list;
for( i = 0; i < MAX_PARTICLES; i++ )
cl_particle_list[i].next = &cl_particle_list[i+1];
cl_particle_list[MAX_PARTICLES-1].next = NULL;
for( i = 0; i < NUMVERTEXNORMALS; i++ )
{
cl_particle_avelocities[i][0] = (rand() & 255) * 0.01f;
cl_particle_avelocities[i][1] = (rand() & 255) * 0.01f;
cl_particle_avelocities[i][2] = (rand() & 255) * 0.01f;
}
for( i = 0, p = cl_particle_list; i < MAX_PARTICLES; i++, p++ )
{
p->pStcoords[0][0] = p->pStcoords[2][1] = p->pStcoords[1][0] = p->pStcoords[1][1] = 0;
p->pStcoords[0][1] = p->pStcoords[2][0] = p->pStcoords[3][0] = p->pStcoords[3][1] = 1;
}
// Xash3D have built-in Quake1 palette - use it
for( i = 0; pic && i < 256; i++ )
{
cl_particlePalette[i][0] = pic->palette[i*4+0];
cl_particlePalette[i][1] = pic->palette[i*4+1];
cl_particlePalette[i][2] = pic->palette[i*4+2];
}
if( pic ) FS_FreeImage( pic );
else MsgDev( D_WARN, "SV_InitParticles: palette not installed\n" );
}
/*
===============
CL_AddParticles
===============
*/
void CL_AddParticles( void )
{
particle_t *p, *kill;
float time1, time2, time3;
float grav, dvel, frametime;
vec3_t up, right, point1;
float scale = 3.0f; // FIXME: make cvar
rgba_t modulate;
int i;
VectorScale( cl.refdef.right, scale, right );
VectorScale( cl.refdef.up, scale, up );
frametime = cls.frametime;
time3 = frametime * 15;
time2 = frametime * 10; // 15;
time1 = frametime * 5;
grav = frametime * clgame.movevars.gravity * 0.05f;
dvel = frametime * 4;
while( 1 )
{
// free time-expired particles
kill = cl_active_particles;
if( kill && kill->die < clgame.globals->time )
{
cl_active_particles = kill->next;
kill->next = cl_free_particles;
cl_free_particles = kill;
continue;
}
break;
}
for( p = cl_active_particles; p; p = p->next )
{
while( 1 )
{
kill = p->next;
if( kill && kill->die < clgame.globals->time )
{
p->next = kill->next;
kill->next = cl_free_particles;
cl_free_particles = kill;
continue;
}
break;
}
scale = 0.0f;
// hack a scale up to keep particles from disapearing
scale += (p->org[0] - cl.refdef.vieworg[0]) * cl.refdef.forward[0];
scale += (p->org[1] - cl.refdef.vieworg[1]) * cl.refdef.forward[1];
scale += (p->org[2] - cl.refdef.vieworg[2]) * cl.refdef.forward[2];
if( scale < 20.0f ) scale = 1.0f;
else scale = 1.0f + scale * 0.004f;
// setup color
modulate[0] = cl_particlePalette[p->color][0];
modulate[1] = cl_particlePalette[p->color][1];
modulate[2] = cl_particlePalette[p->color][2];
modulate[3] = 0xFF; // quake particles doesn't have transparency
*(int *)p->pColor[0] = *(int *)modulate;
*(int *)p->pColor[1] = *(int *)modulate;
*(int *)p->pColor[2] = *(int *)modulate;
*(int *)p->pColor[3] = *(int *)modulate;
VectorMA( p->org, 1.0f, up, point1 );
VectorMA( point1, 0.0f, right, p->pVerts[0] );
VectorMA( p->org, 0.0f, up, point1 );
VectorMA( point1, 0.0f, right, p->pVerts[1] );
VectorMA( p->org, 0.0f, up, point1 );
VectorMA( point1, 1.0f, right, p->pVerts[2] );
VectorMA( p->org, 1.0f, up, point1 );
VectorMA( point1, 1.0f, right, p->pVerts[3] );
p->poly.numverts = 4;
p->poly.verts = p->pVerts;
p->poly.stcoords = p->pStcoords;
p->poly.colors = p->pColor;
p->poly.shadernum = cls.particle;
p->poly.fognum = -1;
// send the particle to the renderer
re->AddPolygon( &p->poly );
// evaluate particle
p->org[0] += p->vel[0] * frametime;
p->org[1] += p->vel[1] * frametime;
p->org[2] += p->vel[2] * frametime;
switch( p->type )
{
case pt_static:
break;
case pt_fire:
p->ramp += time1;
if( p->ramp >= 6 )
p->die = -1;
else p->color = ramp3[(int)p->ramp];
p->vel[2] += grav;
break;
case pt_explode:
p->ramp += time2;
if( p->ramp >=8 )
p->die = -1;
else p->color = ramp1[(int)p->ramp];
for( i = 0; i < 3; i++ )
p->vel[i] += p->vel[i] * dvel;
p->vel[2] -= grav;
break;
case pt_explode2:
p->ramp += time3;
if( p->ramp >=8 )
p->die = -1;
else p->color = ramp2[(int)p->ramp];
for( i = 0; i < 3; i++ )
p->vel[i] -= p->vel[i] * frametime;
p->vel[2] -= grav;
break;
case pt_blob:
for( i = 0; i < 3; i++ )
p->vel[i] += p->vel[i] * dvel;
p->vel[2] -= grav;
break;
case pt_blob2:
for( i = 0; i < 2; i++ )
p->vel[i] -= p->vel[i] * dvel;
p->vel[2] -= grav;
break;
case pt_grav:
p->vel[2] -= grav * 20;
break;
case pt_slowgrav:
p->vel[2] -= grav;
break;
}
}
}
/*
===============
CL_ParticleEffect
old good quake1 particles
===============
*/
void CL_ParticleEffect( const vec3_t org, const vec3_t dir, int color, int count )
{
int i, j;
particle_t *p;
for( i = 0; i < count; i++ )
{
p = CL_AllocParticle();
if( !p ) return;
if( count == 1024 )
{
// rocket explosion
p->die = clgame.globals->time + 5.0f;
p->color = ramp1[0];
p->ramp = rand() & 3;
if( i & 1 )
{
p->type = pt_explode;
for( j = 0; j < 3; j++ )
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
else
{
p->type = pt_explode2;
for( j = 0; j < 3; j++ )
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
}
else
{
p->die = clgame.globals->time + 0.1f * (rand() % 5);
p->color = (color & ~7) + (rand() & 7);
p->type = pt_slowgrav;
for( j = 0; j < 3; j++ )
{
p->org[j] = org[j] + ((rand() & 15 ) - 8);
p->vel[j] = dir[j] * 15;// + (rand() % 300) - 150;
}
}
}
}
/*
===============
CL_EntityParticles
EF_BRIGHTFIELD effect
===============
*/
void CL_EntityParticles( edict_t *ent )
{
int i, count;
float angle;
float sr, sp, sy, cr, cp, cy;
float dist, beamlength = 16;
vec3_t forward;
particle_t *p;
dist = 64;
count = 50;
for( i = 0; i < NUMVERTEXNORMALS; i++ )
{
angle = clgame.globals->time * cl_particle_avelocities[i][0];
com.sincos( angle, &sy, &cy );
angle = clgame.globals->time * cl_particle_avelocities[i][1];
com.sincos( angle, &sp, &cp );
angle = clgame.globals->time * cl_particle_avelocities[i][2];
com.sincos( angle, &sr, &cr );
forward[0] = cp * cy;
forward[1] = cp * sy;
forward[2] = -sp;
p = CL_AllocParticle();
if( !p ) return;
p->die = clgame.globals->time + 0.01f;
p->color = 0x6f;
p->type = pt_explode;
p->org[0] = ent->v.origin[0] + cl_avertexnormals[i][0] * dist + forward[0] * beamlength;
p->org[1] = ent->v.origin[1] + cl_avertexnormals[i][1] * dist + forward[1] * beamlength;
p->org[2] = ent->v.origin[2] + cl_avertexnormals[i][2] * dist + forward[2] * beamlength;
}
}
/*
===============
CL_ParticleExplosion
===============
*/
void CL_ParticleExplosion( const vec3_t org )
{
int i, j;
particle_t *p;
for( i = 0; i < 1024; i++ )
{
p = CL_AllocParticle();
if( !p ) return;
p->die = clgame.globals->time + 5.0f;
p->color = ramp1[0];
p->ramp = rand() & 3;
if( i & 1 )
{
p->type = pt_explode;
for( j = 0; j < 3; j++ )
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
else
{
p->type = pt_explode2;
for( j = 0; j < 3; j++ )
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
}
}
/*
===============
CL_ParticleExplosion2
===============
*/
void CL_ParticleExplosion2( const vec3_t org, int colorStart, int colorLength)
{
int i, j;
int colorMod = 0;
particle_t *p;
for( i = 0; i < 512; i++ )
{
p = CL_AllocParticle();
if( !p ) return;
p->die = clgame.globals->time + 0.3f;
p->color = colorStart + (colorMod % colorLength);
colorMod++;
p->type = pt_blob;
for( j = 0; j < 3; j++ )
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
}
/*
===============
CL_BlobExplosion
===============
*/
void CL_BlobExplosion( const vec3_t org )
{
int i, j;
particle_t *p;
for( i = 0; i < 1024; i++ )
{
p = CL_AllocParticle();
if( !p ) return;
p->die = clgame.globals->time + 1.0f + (rand() & 8) * 0.05f;
if( i & 1 )
{
p->type = pt_blob;
p->color = 66 + rand() % 6;
for( j = 0; j < 3; j++ )
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
else
{
p->type = pt_blob2;
p->color = 150 + rand() % 6;
for( j = 0; j < 3; j++ )
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
}
}
/*
===============
CL_LavaSplash
===============
*/
void CL_LavaSplash( const vec3_t org )
{
int i, j, k;
particle_t *p;
float vel;
vec3_t dir;
for( i = -16; i < 16; i++ )
{
for( j = -16; j <16; j++ )
{
for( k = 0; k < 1; k++ )
{
p = CL_AllocParticle();
if( !p ) return;
p->die = clgame.globals->time + 2.0f + (rand() & 31) * 0.02f;
p->color = 224 + (rand() & 7);
p->type = pt_slowgrav;
dir[0] = j * 8 + (rand() & 7);
dir[1] = i * 8 + (rand() & 7);
dir[2] = 256;
p->org[0] = org[0] + dir[0];
p->org[1] = org[1] + dir[1];
p->org[2] = org[2] + (rand() & 63);
VectorNormalize( dir );
vel = 50 + (rand() & 63);
VectorScale( dir, vel, p->vel );
}
}
}
}
/*
===============
CL_TeleportSplash
===============
*/
void CL_TeleportSplash( const vec3_t org )
{
int i, j, k;
particle_t *p;
float vel;
vec3_t dir;
for( i = -16; i < 16; i+=4 )
{
for( j =-16; j < 16; j += 4 )
{
for( k = -24; k < 32; k += 4 )
{
p = CL_AllocParticle();
if( !p ) return;
p->die = clgame.globals->time + 0.2f + (rand() & 7) * 0.02f;
p->color = 7 + (rand() & 7);
p->type = pt_slowgrav;
dir[0] = j * 8;
dir[1] = i * 8;
dir[2] = k * 8;
p->org[0] = org[0] + i + (rand() & 3);
p->org[1] = org[1] + j + (rand() & 3);
p->org[2] = org[2] + k + (rand() & 3);
VectorNormalize( dir );
vel = 50.0f + (rand() & 63);
VectorScale( dir, vel, p->vel );
}
}
}
}
/*
===============
CL_RocketTrail
===============
*/
void CL_RocketTrail( const vec3_t org, const vec3_t end, int type )
{
vec3_t vec, start;
float len;
particle_t *p;
int j, dec;
static int tracercount;
VectorCopy( org, start );
VectorSubtract( end, start, vec );
len = VectorNormalizeLength( vec );
if( type < 128 )
{
dec = 3;
}
else
{
dec = 1;
type -= 128;
}
while( len > 0 )
{
len -= dec;
p = CL_AllocParticle();
if( !p ) return;
VectorClear( p->vel );
p->die = clgame.globals->time + 2.0f;
switch( type )
{
case 0: // rocket trail
p->ramp = (rand() & 3);
p->color = ramp3[(int)p->ramp];
p->type = pt_fire;
for( j = 0; j < 3; j++ )
p->org[j] = start[j] + ((rand()%6)-3);
break;
case 1: // smoke smoke
p->ramp = (rand() & 3) + 2;
p->color = ramp3[(int)p->ramp];
p->type = pt_fire;
for( j = 0; j < 3; j++ )
p->org[j] = start[j] + ((rand() % 6) - 3);
break;
case 2: // blood
p->type = pt_grav;
p->color = 67 + (rand()&3);
for( j = 0; j < 3; j++ )
p->org[j] = start[j] + ((rand() % 6) - 3);
break;
case 3:
case 5: // tracer
p->die = clgame.globals->time + 0.5f;
p->type = pt_static;
if( type == 3 ) p->color = 52 + ((tracercount & 4)<<1);
else p->color = 230 + ((tracercount & 4)<<1);
tracercount++;
VectorCopy( start, p->org );
if( tracercount & 1 )
{
p->vel[0] = 30 * vec[1];
p->vel[1] = 30 * -vec[0];
}
else
{
p->vel[0] = 30 * -vec[1];
p->vel[1] = 30 * vec[0];
}
break;
case 4: // slight blood
p->type = pt_grav;
p->color = 67 + (rand() & 3);
for( j = 0; j < 3; j++ )
p->org[j] = start[j] + ((rand() % 6) - 3);
len -= 3;
break;
case 6: // voor trail
p->color = 9 * 16 + 8 + (rand() & 3);
p->type = pt_static;
p->die = clgame.globals->time + 0.3f;
for( j = 0; j < 3; j++ )
p->org[j] = start[j] + ((rand() & 15) - 8);
break;
}
VectorAdd( start, vec, start );
}
}
/*
==============================================================
DECALS MANAGEMENT
==============================================================
*/
#define MAX_DRAWDECALS 1024
#define MAX_DECAL_VERTS 128 // per one decal
#define MAX_DECAL_FRAGMENTS 64
typedef struct cdecal_s
{
struct cdecal_s *prev;
struct cdecal_s *next;
edict_t *pent; // parent entity
poly_t *poly;
} cdecal_t;
static cdecal_t cl_decals[MAX_DRAWDECALS];
static cdecal_t cl_decals_headnode, *cl_free_decals;
static poly_t cl_decal_polys[MAX_DRAWDECALS];
static vec3_t cl_decal_verts[MAX_DRAWDECALS][MAX_DECAL_VERTS];
static vec2_t cl_decal_stcoords[MAX_DRAWDECALS][MAX_DECAL_VERTS];
static rgba_t cl_decal_colors[MAX_DRAWDECALS][MAX_DECAL_VERTS];
/*
=================
CL_ClearDecals
=================
*/
void CL_ClearDecals( void )
{
int i;
Mem_Set( cl_decals, 0, sizeof( cl_decals ));
Mem_Set( cl_decal_polys, 0, sizeof( cl_decal_polys ));
// link decals
cl_free_decals = cl_decals;
cl_decals_headnode.prev = &cl_decals_headnode;
cl_decals_headnode.next = &cl_decals_headnode;
for( i = 0; i < MAX_DRAWDECALS; i++ )
{
if( i < MAX_DRAWDECALS - 1 )
cl_decals[i].next = &cl_decals[i+1];
cl_decals[i].poly = &cl_decal_polys[i];
cl_decals[i].poly->verts = cl_decal_verts[i];
cl_decals[i].poly->stcoords = cl_decal_stcoords[i];
cl_decals[i].poly->colors = cl_decal_colors[i];
}
}
/*
=================
CL_AllocDecal
Returns either a free decal or the oldest one
=================
*/
cdecal_t *CL_AllocDecal( void )
{
cdecal_t *dl;
if( cl_free_decals )
{
// take a free decal if possible
dl = cl_free_decals;
cl_free_decals = dl->next;
}
else
{
// grab the oldest one otherwise
dl = cl_decals_headnode.prev;
dl->prev->next = dl->next;
dl->next->prev = dl->prev;
}
// put the decal at the start of the list
dl->prev = &cl_decals_headnode;
dl->next = cl_decals_headnode.next;
dl->next->prev = dl;
dl->prev->next = dl;
return dl;
}
/*
=================
CL_FreeDecal
=================
*/
void CL_FreeDecal( cdecal_t *dl )
{
// remove from linked active list
dl->prev->next = dl->next;
dl->next->prev = dl->prev;
// insert into linked free list
dl->next = cl_free_decals;
cl_free_decals = dl;
}
/*
=================
CL_SpawnDecal
=================
*/
int CL_SpawnDecal( HSPRITE m_hDecal, edict_t *pEnt, const vec3_t pos, int colorIndex, float roll, float scale )
{
cdecal_t *dl;
poly_t *poly;
vec3_t axis[3];
int i, j, numfragments;
vec3_t dir, verts[MAX_DECAL_VERTS];
fragment_t *fr, fragments[MAX_DECAL_FRAGMENTS];
rgba_t color;
// invalid decal
if( scale <= 0.0f || !m_hDecal )
return false;
CL_FindExplosionPlane( pos, scale, dir );
// calculate orientation matrix
VectorNormalize2( dir, axis[0] );
PerpendicularVector( axis[1], axis[0] );
RotatePointAroundVector( axis[2], axis[0], axis[1], roll );
CrossProduct( axis[0], axis[2], axis[1] );
numfragments = re->GetFragments( pos, scale, axis, MAX_DECAL_VERTS, verts, MAX_DECAL_FRAGMENTS, fragments );
// no valid fragments
if( !numfragments ) return false;
// setup decal color
if( colorIndex )
{
colorIndex = bound( 0, colorIndex, 255 );
color[0] = cl_particlePalette[colorIndex][0];
color[1] = cl_particlePalette[colorIndex][1];
color[2] = cl_particlePalette[colorIndex][2];
color[3] = 0xFF;
}
else Vector4Set( color, 255, 255, 255, 255 );
scale = 0.5f / scale;
VectorScale( axis[1], scale, axis[1] );
VectorScale( axis[2], scale, axis[2] );
for( i = 0, fr = fragments; i < numfragments; i++, fr++ )
{
if( fr->numverts >= MAX_DECAL_VERTS )
return -1; // in case we partially failed
else if( fr->numverts <= 0 )
continue;
// allocate decal
dl = CL_AllocDecal();
dl->pent = pEnt;
// setup polygon for drawing
poly = dl->poly;
poly->shadernum = m_hDecal;
poly->numverts = fr->numverts;
poly->fognum = fr->fognum;
for( j = 0; j < fr->numverts; j++ )
{
vec3_t v;
VectorCopy( verts[fr->firstvert+j], poly->verts[j] );
VectorSubtract( poly->verts[j], pos, 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;
}
}
return true; // all done
}
/*
=================
CL_AddDecals
=================
*/
void CL_AddDecals( void )
{
cdecal_t *dl, *next, *hnode;
// add decals in first-spawned - first-drawn order
hnode = &cl_decals_headnode;
for( dl = hnode->prev; dl != hnode; dl = next )
{
next = dl->prev;
if( dl->pent && dl->pent->free )
{
// remove attached decals
CL_FreeDecal( dl );
continue;
}
re->AddPolygon( dl->poly );
}
}
/*
===============
CL_SpawnStaticDecal
===============
*/
void CL_SpawnStaticDecal( vec3_t origin, int decalIndex, int entityIndex, int modelIndex )
{
edict_t *pEnt;
pEnt = CL_GetEdictByIndex( entityIndex );
decalIndex = bound( 0, decalIndex, MAX_DECALS - 1 );
CL_SpawnDecal( cl.decal_shaders[decalIndex], pEnt, origin, 0, 90.0f, 32.0f );
}
void CL_FindExplosionPlane( const vec3_t origin, float radius, vec3_t result )
{
static vec3_t planes[6] = {{0, 0, 1}, {0, 1, 0}, {1, 0, 0}, {0, 0, -1}, {0, -1, 0}, {-1, 0, 0}};
float best = 1.0f;
vec3_t point, dir;
trace_t trace;
int i;
if( !result ) return;
VectorClear( dir );
for( i = 0; i < 6; i++ )
{
VectorMA( origin, radius, planes[i], point );
trace = CL_Move( origin, vec3_origin, vec3_origin, point, MOVE_NOMONSTERS, NULL );
if( trace.fAllSolid || trace.flFraction == 1.0f )
continue;
if( trace.flFraction < best )
{
best = trace.flFraction;
VectorCopy( trace.vecPlaneNormal, dir );
}
}
VectorCopy( dir, result );
}
void CL_LightForPoint( const vec3_t point, vec3_t ambientLight )
{
if( re ) re->LightForPoint( point, ambientLight );
else VectorClear( ambientLight );
}
/*
===============
CL_ResetEvent
===============
*/
void CL_ResetEvent( event_info_t *ei )
{
Mem_Set( ei, 0, sizeof( *ei ));
}
word CL_PrecacheEvent( const char *name )
{
int i;
if( !name || !name[0] ) return 0;
for( i = 1; i < MAX_EVENTS && cl.configstrings[CS_EVENTS+i][0]; i++ )
if( !com.strcmp( cl.configstrings[CS_EVENTS+i], name ))
return i;
return 0;
}
bool CL_FireEvent( event_info_t *ei )
{
user_event_t *ev;
const char *name;
int i;
if( !ei || !ei->index )
return false;
// get the func pointer
for( i = 0; i < MAX_EVENTS; i++ )
{
ev = clgame.events[i];
if( !ev ) break;
if( ev->index == ei->index )
{
if( ev->func )
{
ev->func( &ei->args );
return true;
}
name = cl.configstrings[CS_EVENTS+ei->index];
MsgDev( D_ERROR, "CL_FireEvent: %s not hooked\n", name );
break;
}
}
return false;
}
void CL_FireEvents( void )
{
int i;
event_state_t *es;
event_info_t *ei;
bool success;
es = &cl.events;
for( i = 0; i < MAX_EVENT_QUEUE; i++ )
{
ei = &es->ei[i];
if( ei->index == 0 )
continue;
if( cls.state == ca_disconnected )
{
CL_ResetEvent( ei );
continue;
}
// delayed event!
if( ei->fire_time && ( ei->fire_time > clgame.globals->time ))
continue;
success = CL_FireEvent( ei );
// zero out the remaining fields
CL_ResetEvent( ei );
}
}
event_info_t *CL_FindEmptyEvent( void )
{
int i;
event_state_t *es;
event_info_t *ei;
es = &cl.events;
// look for first slot where index is != 0
for( i = 0; i < MAX_EVENT_QUEUE; i++ )
{
ei = &es->ei[i];
if( ei->index != 0 )
continue;
return ei;
}
// no slots available
return NULL;
}
event_info_t *CL_FindUnreliableEvent( void )
{
int i;
event_state_t *es;
event_info_t *ei;
es = &cl.events;
for ( i = 0; i < MAX_EVENT_QUEUE; i++ )
{
ei = &es->ei[i];
if( ei->index != 0 )
{
// it's reliable, so skip it
if( ei->flags & FEV_RELIABLE )
continue;
}
return ei;
}
// this should never happen
return NULL;
}
void CL_QueueEvent( int flags, int index, float delay, event_args_t *args )
{
bool unreliable = (flags & FEV_RELIABLE) ? false : true;
event_info_t *ei;
// find a normal slot
ei = CL_FindEmptyEvent();
if( !ei && unreliable )
{
return;
}
// okay, so find any old unreliable slot
if( !ei )
{
ei = CL_FindUnreliableEvent();
if( !ei ) return;
}
ei->index = index;
ei->fire_time = delay ? (clgame.globals->time + delay) : 0.0f;
ei->flags = flags;
// copy in args event data
Mem_Copy( &ei->args, args, sizeof( ei->args ));
}
/*
==============
CL_ClearEffects
==============
*/
void CL_ClearEffects( void )
{
CL_ClearParticles ();
CL_ClearDlights ();
CL_ClearLightStyles ();
CL_ClearDecals ();
}