798 lines
17 KiB
C
798 lines
17 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
|
|
|
|
==============================================================
|
|
*/
|
|
|
|
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;
|
|
cdlight_t *dl;
|
|
|
|
// first look for an exact key match
|
|
if( key )
|
|
{
|
|
dl = cl_dlights;
|
|
for( i = 0; i < MAX_DLIGHTS; i++, dl++ )
|
|
{
|
|
if( dl->key == key )
|
|
{
|
|
Mem_Set( dl, 0, sizeof( *dl ));
|
|
dl->key = key;
|
|
return dl;
|
|
}
|
|
}
|
|
}
|
|
|
|
// then look for anything else
|
|
dl = cl_dlights;
|
|
for( i = 0; i < MAX_DLIGHTS; i++, dl++ )
|
|
{
|
|
if( dl->die < cl.time )
|
|
{
|
|
Mem_Set( dl, 0, sizeof( *dl ));
|
|
dl->key = key;
|
|
return dl;
|
|
}
|
|
}
|
|
|
|
dl = &cl_dlights[0];
|
|
Mem_Set( dl, 0, sizeof( *dl ));
|
|
dl->key = key;
|
|
return dl;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
pfnAddDLight
|
|
|
|
===============
|
|
*/
|
|
void pfnAddDLight( const float *org, const float *rgb, float radius, float decay, float time, int key )
|
|
{
|
|
cdlight_t *dl;
|
|
|
|
if( radius <= 0 )
|
|
{
|
|
MsgDev( D_WARN, "CL_AddDLight: ignore light with radius <= 0\n" );
|
|
return;
|
|
}
|
|
|
|
dl = CL_AllocDlight( key );
|
|
if( !dl )
|
|
{
|
|
MsgDev( D_ERROR, "CL_AddDLight: no free dlights\n" );
|
|
return;
|
|
}
|
|
|
|
VectorCopy( org, dl->origin );
|
|
VectorCopy( rgb, dl->color );
|
|
dl->die = cl.time + (time * 1000);
|
|
dl->decay = (decay * 1000);
|
|
dl->radius = radius;
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
CL_RunDLights
|
|
|
|
===============
|
|
*/
|
|
void CL_RunDLights( void )
|
|
{
|
|
cdlight_t *dl;
|
|
int i;
|
|
|
|
for( i = 0, dl = cl_dlights; i < MAX_DLIGHTS; i++, dl++ )
|
|
{
|
|
if( !dl->radius ) continue;
|
|
if( dl->die < cl.time )
|
|
{
|
|
dl->radius = 0;
|
|
return;
|
|
}
|
|
|
|
dl->radius -= cls.frametime * dl->decay;
|
|
if( dl->radius < 0 ) dl->radius = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CL_AddDLights
|
|
|
|
===============
|
|
*/
|
|
void CL_AddDLights( void )
|
|
{
|
|
int i;
|
|
cdlight_t *dl;
|
|
|
|
dl = cl_dlights;
|
|
for( i = 0; i < MAX_DLIGHTS; i++, dl++ )
|
|
{
|
|
if( dl->radius ) re->AddDynLight( dl->origin, dl->color, dl->radius );
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============================================================
|
|
|
|
DECALS MANAGEMENT
|
|
|
|
==============================================================
|
|
*/
|
|
#define MAX_DECAL_MARKS 2048
|
|
#define DECAL_FADETIME (30 * 1000) // 30 seconds
|
|
#define DECAL_STAYTIME (120 * 1000) // 120 seconds
|
|
|
|
typedef struct cdecal_s
|
|
{
|
|
struct cdecal_s *prev, *next;
|
|
int time;
|
|
vec4_t modulate;
|
|
bool alphaFade;
|
|
shader_t shader;
|
|
int numVerts;
|
|
polyVert_t verts[MAX_VERTS_ON_POLY];
|
|
vec3_t origin;
|
|
} cdecal_t;
|
|
|
|
static cdecal_t cl_activeDecals;
|
|
static cdecal_t *cl_freeDecals;
|
|
static cdecal_t cl_decalList[MAX_DECAL_MARKS];
|
|
|
|
/*
|
|
=================
|
|
CL_FreeDecal
|
|
=================
|
|
*/
|
|
static void CL_FreeDecal( cdecal_t *decal )
|
|
{
|
|
if( !decal->prev ) return;
|
|
|
|
decal->prev->next = decal->next;
|
|
decal->next->prev = decal->prev;
|
|
|
|
decal->next = cl_freeDecals;
|
|
cl_freeDecals = decal;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_AllocDecal
|
|
|
|
will always succeed, even if it requires freeing an old active mark
|
|
=================
|
|
*/
|
|
static cdecal_t *CL_AllocDecal( void )
|
|
{
|
|
cdecal_t *decal;
|
|
|
|
if( !cl_freeDecals )
|
|
CL_FreeDecal( cl_activeDecals.prev );
|
|
|
|
decal = cl_freeDecals;
|
|
cl_freeDecals = cl_freeDecals->next;
|
|
|
|
Mem_Set( decal, 0, sizeof( cdecal_t ));
|
|
|
|
decal->next = cl_activeDecals.next;
|
|
decal->prev = &cl_activeDecals;
|
|
cl_activeDecals.next->prev = decal;
|
|
cl_activeDecals.next = decal;
|
|
|
|
return decal;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_ClearDecals
|
|
=================
|
|
*/
|
|
void CL_ClearDecals( void )
|
|
{
|
|
int i;
|
|
|
|
Mem_Set( cl_decalList, 0, sizeof( cl_decalList ));
|
|
|
|
cl_activeDecals.next = &cl_activeDecals;
|
|
cl_activeDecals.prev = &cl_activeDecals;
|
|
cl_freeDecals = cl_decalList;
|
|
|
|
for( i = 0; i < MAX_DECAL_MARKS - 1; i++ )
|
|
cl_decalList[i].next = &cl_decalList[i+1];
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_AddDecal
|
|
|
|
called from render after clipping
|
|
=================
|
|
*/
|
|
void CL_AddDecal( vec3_t org, matrix3x3 m, shader_t s, vec4_t rgba, bool fade, decalFragment_t *df, const vec3_t *v )
|
|
{
|
|
cdecal_t *decal;
|
|
vec3_t delta;
|
|
int i;
|
|
|
|
decal = CL_AllocDecal();
|
|
VectorCopy( org, decal->origin );
|
|
decal->time = cl.time;
|
|
Vector4Copy( rgba, decal->modulate );
|
|
decal->alphaFade = fade;
|
|
decal->shader = s;
|
|
decal->numVerts = df->numVerts;
|
|
|
|
for( i = 0; i < df->numVerts; i++ )
|
|
{
|
|
VectorCopy( v[df->firstVert + i], decal->verts[i].point );
|
|
|
|
VectorSubtract( decal->verts[i].point, org, delta );
|
|
decal->verts[i].st[0] = 0.5 + DotProduct( delta, m[1] );
|
|
decal->verts[i].st[1] = 0.5 + DotProduct( delta, m[2] );
|
|
Vector4Copy( rgba, decal->verts[i].modulate );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_AddDecals
|
|
=================
|
|
*/
|
|
void CL_AddDecals( void )
|
|
{
|
|
cdecal_t *decal, *next;
|
|
int i, time, fadeTime;
|
|
float c;
|
|
|
|
fadeTime = DECAL_FADETIME; // 30 seconds
|
|
|
|
for( decal = cl_activeDecals.next; decal != &cl_activeDecals; decal = next )
|
|
{
|
|
// crab next now, so if the decal is freed we still have it
|
|
next = decal->next;
|
|
|
|
if( cl.time >= decal->time + DECAL_STAYTIME )
|
|
{
|
|
CL_FreeDecal( decal );
|
|
continue;
|
|
}
|
|
|
|
// HACKHACK: fade out glowing energy decals
|
|
if( decal->shader == re->RegisterShader( "particles/energy", SHADER_GENERIC ))
|
|
{
|
|
time = cl.time - decal->time;
|
|
|
|
if( time < fadeTime )
|
|
{
|
|
c = 1.0 - ((float)time / fadeTime);
|
|
|
|
for( i = 0; i < decal->numVerts; i++ )
|
|
{
|
|
decal->verts[i].modulate[0] = decal->modulate[0] * c;
|
|
decal->verts[i].modulate[1] = decal->modulate[1] * c;
|
|
decal->verts[i].modulate[2] = decal->modulate[2] * c;
|
|
}
|
|
}
|
|
}
|
|
|
|
// fade out with time
|
|
time = decal->time + DECAL_STAYTIME - cl.time;
|
|
|
|
if( time < fadeTime )
|
|
{
|
|
c = (float)time / fadeTime;
|
|
|
|
if( decal->alphaFade )
|
|
{
|
|
for( i = 0; i < decal->numVerts; i++ )
|
|
decal->verts[i].modulate[3] = decal->modulate[3] * c;
|
|
}
|
|
else
|
|
{
|
|
for( i = 0; i < decal->numVerts; i++ )
|
|
{
|
|
decal->verts[i].modulate[0] = decal->modulate[0] * c;
|
|
decal->verts[i].modulate[1] = decal->modulate[1] * c;
|
|
decal->verts[i].modulate[2] = decal->modulate[2] * c;
|
|
}
|
|
}
|
|
}
|
|
re->AddPolygon( decal->shader, decal->numVerts, decal->verts );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
pfnAddDecal
|
|
|
|
===============
|
|
*/
|
|
void pfnAddDecal( float *org, float *dir, float *rgba, float rot, float rad, HSPRITE hSpr, int flags )
|
|
{
|
|
bool fade, temp;
|
|
|
|
fade = (flags & DECAL_FADE) ? true : false;
|
|
temp = (flags & DECAL_TEMPORARY) ? true : false;
|
|
|
|
re->ImpactMark( org, dir, rot, rad, rgba, fade, hSpr, temp );
|
|
}
|
|
|
|
/*
|
|
==============================================================
|
|
|
|
PARTICLE MANAGEMENT
|
|
|
|
==============================================================
|
|
*/
|
|
struct cparticle_s
|
|
{
|
|
// this part are shared both client and engine
|
|
vec3_t origin;
|
|
vec3_t velocity;
|
|
vec3_t accel;
|
|
vec3_t color;
|
|
vec3_t colorVelocity;
|
|
float alpha;
|
|
float alphaVelocity;
|
|
float radius;
|
|
float radiusVelocity;
|
|
float length;
|
|
float lengthVelocity;
|
|
float rotation;
|
|
float bounceFactor;
|
|
|
|
// this part are private for engine
|
|
cparticle_t *next;
|
|
shader_t shader;
|
|
|
|
int time;
|
|
int flags;
|
|
|
|
vec3_t oldorigin;
|
|
};
|
|
|
|
cparticle_t *cl_active_particles, *cl_free_particles;
|
|
static cparticle_t cl_particle_list[MAX_PARTICLES];
|
|
static vec3_t cl_particle_velocities[NUMVERTEXNORMALS];
|
|
|
|
/*
|
|
=================
|
|
CL_FreeParticle
|
|
=================
|
|
*/
|
|
static void CL_FreeParticle( cparticle_t *p )
|
|
{
|
|
p->next = cl_free_particles;
|
|
cl_free_particles = p;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_AllocParticle
|
|
=================
|
|
*/
|
|
static cparticle_t *CL_AllocParticle( void )
|
|
{
|
|
cparticle_t *p;
|
|
|
|
if( !cl_free_particles )
|
|
return NULL;
|
|
|
|
if( cl_particlelod->integer > 1 )
|
|
{
|
|
if(!(Com_RandomLong( 0, 1 ) % cl_particlelod->integer))
|
|
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;
|
|
|
|
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_velocities[i][0] = (rand() & 255) * 0.01;
|
|
cl_particle_velocities[i][1] = (rand() & 255) * 0.01;
|
|
cl_particle_velocities[i][2] = (rand() & 255) * 0.01;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CL_AddParticles
|
|
===============
|
|
*/
|
|
void CL_AddParticles( void )
|
|
{
|
|
cparticle_t *p, *next;
|
|
cparticle_t *active = NULL, *tail = NULL;
|
|
dword modulate;
|
|
vec3_t origin, oldorigin, velocity, color;
|
|
vec3_t ambientLight;
|
|
float alpha, radius, length;
|
|
float time, time2, gravity, dot;
|
|
vec3_t mins, maxs;
|
|
int contents;
|
|
trace_t trace;
|
|
|
|
if( !cl_particles->integer ) return;
|
|
|
|
if( EDICT_NUM( cl.frame.ps.number )->pvClientData->current.gravity != 0 )
|
|
gravity = EDICT_NUM( cl.frame.ps.number )->pvClientData->current.gravity / clgame.gravity;
|
|
else gravity = 1.0f;
|
|
|
|
for( p = cl_active_particles; p; p = next )
|
|
{
|
|
// grab next now, so if the particle is freed we still have it
|
|
next = p->next;
|
|
|
|
time = (cl.time - p->time) * 0.001;
|
|
time2 = time * time;
|
|
|
|
alpha = p->alpha + p->alphaVelocity * time;
|
|
radius = p->radius + p->radiusVelocity * time;
|
|
length = p->length + p->lengthVelocity * time;
|
|
|
|
if( alpha <= 0 || radius <= 0 || length <= 0 )
|
|
{
|
|
// faded out
|
|
CL_FreeParticle( p );
|
|
continue;
|
|
}
|
|
|
|
color[0] = p->color[0] + p->colorVelocity[0] * time;
|
|
color[1] = p->color[1] + p->colorVelocity[1] * time;
|
|
color[2] = p->color[2] + p->colorVelocity[2] * time;
|
|
|
|
origin[0] = p->origin[0] + p->velocity[0] * time + p->accel[0] * time2;
|
|
origin[1] = p->origin[1] + p->velocity[1] * time + p->accel[1] * time2;
|
|
origin[2] = p->origin[2] + p->velocity[2] * time + p->accel[2] * time2 * gravity;
|
|
|
|
if( p->flags & PARTICLE_UNDERWATER )
|
|
{
|
|
// underwater particle
|
|
VectorSet( oldorigin, origin[0], origin[1], origin[2] + radius );
|
|
|
|
if(!(CL_PointContents( oldorigin ) & MASK_WATER ))
|
|
{
|
|
// not underwater
|
|
CL_FreeParticle( p );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
p->next = NULL;
|
|
if( !tail ) active = tail = p;
|
|
else
|
|
{
|
|
tail->next = p;
|
|
tail = p;
|
|
}
|
|
|
|
if( p->flags & PARTICLE_FRICTION )
|
|
{
|
|
// water friction affected particle
|
|
contents = CL_PointContents( origin );
|
|
if( contents & MASK_WATER )
|
|
{
|
|
// add friction
|
|
if( contents & CONTENTS_WATER )
|
|
{
|
|
VectorScale( p->velocity, 0.25, p->velocity );
|
|
VectorScale( p->accel, 0.25, p->accel );
|
|
}
|
|
if( contents & CONTENTS_SLIME )
|
|
{
|
|
VectorScale( p->velocity, 0.20, p->velocity );
|
|
VectorScale( p->accel, 0.20, p->accel );
|
|
}
|
|
if( contents & CONTENTS_LAVA )
|
|
{
|
|
VectorScale( p->velocity, 0.10, p->velocity );
|
|
VectorScale( p->accel, 0.10, p->accel );
|
|
}
|
|
|
|
// don't add friction again
|
|
p->flags &= ~PARTICLE_FRICTION;
|
|
length = 1;
|
|
|
|
// reset
|
|
p->time = cl.time;
|
|
VectorCopy( origin, p->origin );
|
|
VectorCopy( color, p->color );
|
|
p->alpha = alpha;
|
|
p->radius = radius;
|
|
|
|
// don't stretch
|
|
p->flags &= ~PARTICLE_STRETCH;
|
|
p->length = length;
|
|
p->lengthVelocity = 0;
|
|
}
|
|
}
|
|
|
|
if( p->flags & PARTICLE_BOUNCE )
|
|
{
|
|
edict_t *clent = EDICT_NUM( cl.frame.ps.number );
|
|
|
|
// bouncy particle
|
|
VectorSet(mins, -radius, -radius, -radius);
|
|
VectorSet(maxs, radius, radius, radius);
|
|
|
|
trace = CL_Trace( p->oldorigin, mins, maxs, origin, MOVE_NORMAL, clent, MASK_SOLID );
|
|
if( trace.fraction != 0.0 && trace.fraction != 1.0 )
|
|
{
|
|
// reflect velocity
|
|
time = cl.time - (cls.frametime + cls.frametime * trace.fraction) * 1000;
|
|
time = (time - p->time) * 0.001;
|
|
|
|
velocity[0] = p->velocity[0];
|
|
velocity[1] = p->velocity[1];
|
|
velocity[2] = p->velocity[2] + p->accel[2] * gravity * time;
|
|
VectorReflect( velocity, 0, trace.plane.normal, p->velocity );
|
|
VectorScale( p->velocity, p->bounceFactor, p->velocity );
|
|
|
|
// check for stop or slide along the plane
|
|
if( trace.plane.normal[2] > 0 && p->velocity[2] < 1 )
|
|
{
|
|
if( trace.plane.normal[2] == 1 )
|
|
{
|
|
VectorClear( p->velocity );
|
|
VectorClear( p->accel );
|
|
p->flags &= ~PARTICLE_BOUNCE;
|
|
}
|
|
else
|
|
{
|
|
// FIXME: check for new plane or free fall
|
|
dot = DotProduct( p->velocity, trace.plane.normal );
|
|
VectorMA( p->velocity, -dot, trace.plane.normal, p->velocity );
|
|
|
|
dot = DotProduct( p->accel, trace.plane.normal );
|
|
VectorMA( p->accel, -dot, trace.plane.normal, p->accel );
|
|
}
|
|
}
|
|
|
|
VectorCopy( trace.endpos, origin );
|
|
length = 1;
|
|
|
|
// reset
|
|
p->time = cl.time;
|
|
VectorCopy( origin, p->origin );
|
|
VectorCopy( color, p->color );
|
|
p->alpha = alpha;
|
|
p->radius = radius;
|
|
|
|
// don't stretch
|
|
p->flags &= ~PARTICLE_STRETCH;
|
|
p->length = length;
|
|
p->lengthVelocity = 0;
|
|
}
|
|
}
|
|
|
|
// save current origin if needed
|
|
if( p->flags & (PARTICLE_BOUNCE|PARTICLE_STRETCH))
|
|
{
|
|
VectorCopy( p->oldorigin, oldorigin );
|
|
VectorCopy( origin, p->oldorigin ); // FIXME: pause
|
|
}
|
|
|
|
if( p->flags & PARTICLE_VERTEXLIGHT )
|
|
{
|
|
// vertex lit particle
|
|
re->LightForPoint( origin, ambientLight );
|
|
VectorMultiply( color, ambientLight, color );
|
|
}
|
|
|
|
// bound color and alpha and convert to byte
|
|
modulate = PackRGBA( color[0], color[1], color[2], alpha );
|
|
|
|
if( p->flags & PARTICLE_INSTANT )
|
|
{
|
|
// instant particle
|
|
p->alpha = 0;
|
|
p->alphaVelocity = 0;
|
|
}
|
|
|
|
// send the particle to the renderer
|
|
re->AddParticle( p->shader, origin, oldorigin, radius, length, p->rotation, modulate );
|
|
}
|
|
|
|
cl_active_particles = active;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
pfnAddParticle
|
|
|
|
===============
|
|
*/
|
|
bool pfnAddParticle( cparticle_t *src, HSPRITE shader, int flags )
|
|
{
|
|
cparticle_t *p;
|
|
|
|
if( !src ) return false;
|
|
|
|
p = CL_AllocParticle();
|
|
if( !p )
|
|
{
|
|
MsgDev( D_ERROR, "CL_AddParticle: no free particles\n" );
|
|
return false;
|
|
}
|
|
|
|
p->shader = shader;
|
|
p->time = cl.time;
|
|
p->flags = flags;
|
|
|
|
// sizeof( src ) != sizeof( p ), we must copy params manually
|
|
VectorCopy( src->origin, p->origin );
|
|
VectorCopy( src->velocity, p->velocity );
|
|
VectorCopy( src->accel, p->accel );
|
|
VectorCopy( src->color, p->color );
|
|
VectorCopy( src->colorVelocity, p->colorVelocity );
|
|
p->alpha = src->alpha;
|
|
|
|
p->radius = src->radius;
|
|
p->length = src->length;
|
|
p->rotation = src->rotation;
|
|
p->alphaVelocity = src->alphaVelocity;
|
|
p->radiusVelocity = src->radiusVelocity;
|
|
p->lengthVelocity = src->lengthVelocity;
|
|
p->bounceFactor = src->bounceFactor;
|
|
|
|
// needs to save old origin
|
|
if( flags & (PARTICLE_BOUNCE|PARTICLE_FRICTION))
|
|
VectorCopy( p->origin, p->oldorigin );
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
CL_ClearEffects
|
|
==============
|
|
*/
|
|
void CL_ClearEffects( void )
|
|
{
|
|
CL_ClearParticles ();
|
|
CL_ClearDlights ();
|
|
CL_ClearLightStyles ();
|
|
CL_ClearDecals ();
|
|
}
|