beam system overhaul

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@333 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
TimeServ 2004-10-15 00:00:15 +00:00
parent c9f4af34bf
commit fd5c54cbdc
4 changed files with 702 additions and 643 deletions

View file

@ -1068,7 +1068,15 @@ void CL_LinkPacketEntities (void)
}
if (i == cl_oldnumvisedicts)
{
cl.lerpents[s1->number].trailstate.lastdist = 0;
trailstate_t *t;
t = &cl.lerpents[s1->number].trailstate;
t->lastdist = 0;
if (t->lastbeam)
{
t->lastbeam->flags &= ~BS_LASTSEG;
t->lastbeam->flags |= BS_NODRAW;
}
t->lastbeam = NULL;
continue; // not in last message
}

View file

@ -73,6 +73,7 @@ int R_RunParticleEffectType (vec3_t org, vec3_t dir, float count, int typenum);
void D_DrawParticleTrans (particle_t *pparticle);
void D_DrawSparkTrans (particle_t *pparticle);
#define MAX_BEAMS 2048 // default max # of beam segments
#define MAX_PARTICLES 32768 // default max # of particles at one
// time
@ -85,6 +86,11 @@ particle_t *free_particles;
particle_t *particles; //contains the initial list of alloced particles.
int r_numparticles;
beamseg_t *free_beams;
beamseg_t *beams;
int r_numbeams;
vec3_t r_pright, r_pup, r_ppn;
extern cvar_t r_bouncysparks;
@ -191,6 +197,7 @@ typedef struct part_type_s {
int loaded;
particle_t *particles;
beamseg_t *beams;
skytris_t *skytris;
} part_type_t;
int numparticletypes;
@ -287,6 +294,7 @@ void R_ParticleEffect_f(void)
char *var, *value;
char *buf;
particle_t *parts;
beamseg_t *beamsegs;
skytris_t *st;
part_type_t *ptype;
@ -320,11 +328,13 @@ void R_ParticleEffect_f(void)
pnum = ptype-part_type;
parts = ptype->particles;
beamsegs = ptype->beams;
st = ptype->skytris;
if (ptype->ramp)
BZ_Free(ptype->ramp);
memset(ptype, 0, sizeof(*ptype));
ptype->particles = parts;
ptype->beams = beamsegs;
ptype->skytris = st;
strcpy(ptype->name, Cmd_Argv(1));
ptype->assoc=-1;
@ -767,6 +777,67 @@ void R_DefaultTrail (model_t *model)
model->particletrail = -1;
}
#if _DEBUG
// R_BeamInfo_f - debug junk
void R_BeamInfo_f (void)
{
beamseg_t *bs;
int i, j, k, l, m;
i = 0;
for (bs = free_beams; bs; bs = bs->next)
i++;
Con_Printf("%i free beams\n", i);
for (i = 0; i < numparticletypes; i++)
{
m = l = k = j = 0;
for (bs = part_type[i].beams; bs; bs = bs->next)
{
if (!bs->p)
k++;
if (bs->flags & BS_DEAD)
l++;
if (bs->flags & BS_LASTSEG)
m++;
j++;
}
if (j)
Con_Printf("Type %i = %i NULL p, %i DEAD, %i LASTSEG, %i total\n", i, k, l, m, j);
}
}
void R_PartInfo_f (void)
{
particle_t *p;
int i, j;
i = 0;
for (p = free_particles; p; p = p->next)
i++;
Con_Printf("%i free particles\n", i);
for (i = 0; i < numparticletypes; i++)
{
j = 0;
for (p = part_type[i].particles; p; p = p->next)
j++;
if (j)
Con_Printf("Type %i = %i total\n", i, j);
}
}
#endif
/*
===============
R_InitParticles
@ -791,13 +862,23 @@ void R_InitParticles (void)
r_numparticles = MAX_PARTICLES;
}
r_numbeams = MAX_BEAMS;
particles = (particle_t *)
Hunk_AllocName (r_numparticles * sizeof(particle_t), "particles");
beams = (beamseg_t *)
Hunk_AllocName (r_numbeams * sizeof(beamseg_t), "beams");
Cmd_AddCommand("r_part", R_ParticleEffect_f);
Cmd_AddCommand("r_effect", R_AssosiateEffect_f);
Cmd_AddCommand("r_trail", R_AssosiateTrail_f);
#if _DEBUG
Cmd_AddCommand("r_partinfo", R_PartInfo_f);
Cmd_AddCommand("r_beaminfo", R_BeamInfo_f);
#endif
//particles
Cvar_Register(&r_particlesdesc, particlecvargroupname);
Cvar_Register(&r_bouncysparks, particlecvargroupname);
@ -855,6 +936,16 @@ void R_ClearParticles (void)
particles[i].next = &particles[i+1];
particles[r_numparticles-1].next = NULL;
free_beams = &beams[0];
for (i=0 ;i<r_numbeams ; i++)
{
beams[i].p = NULL;
beams[i].flags = BS_DEAD;
beams[i].next = &beams[i+1];
}
beams[r_numbeams-1].next = NULL;
particletime = cl.time;
#ifdef RGLQUAKE
@ -875,6 +966,7 @@ void R_ClearParticles (void)
for (i = 0; i < numparticletypes; i++)
{
part_type[i].particles = NULL;
part_type[i].beams = NULL;
part_type[i].skytris = NULL;
}
}
@ -1399,89 +1491,6 @@ int R_RunParticleEffectType (vec3_t org, vec3_t dir, float count, int typenum)
return 1;
while(ptype)
{
if (ptype->isbeam)
{
vec3_t lastorg, lastdir;
float a;
switch (ptype->spawnmode)
{
default:
a = (2*M_PI);
lastorg[0] = org[0] + sin(a)*ptype->areaspread;
lastorg[1] = org[1] + cos(a)*ptype->areaspread;
lastorg[2] = org[2] + ptype->offsetup;
VectorNormalize(lastdir);
for (i = 0; i < count*ptype->count; i++)
{
if (!free_particles)
return 0;
p = free_particles;
free_particles = p->next;
p->next = ptype->particles;
ptype->particles = p;
p->die = ptype->randdie*frandom();
p->scale = ptype->scale+ptype->randscale*frandom();
p->alpha = ptype->alpha-p->die*(ptype->alpha/ptype->die)*ptype->alphachange;
p->color = 0;
if (ptype->colorindex >= 0)
{
int cidx;
cidx = ptype->colorrand > 0 ? rand() % ptype->colorrand : 0;
cidx = ptype->colorindex + cidx;
if (cidx > 255)
p->alpha = p->alpha / 2;
cidx = d_8to24rgbtable[cidx & 0xff];
p->rgb[0] = (cidx & 0xff) * (1/255.0);
p->rgb[1] = (cidx >> 8 & 0xff) * (1/255.0);
p->rgb[2] = (cidx >> 16 & 0xff) * (1/255.0);
}
else
VectorCopy(ptype->rgb, p->rgb);
// use org temporarily for rgbsync
p->org[2] = frandom();
p->org[0] = p->org[2]*ptype->rgbrandsync[0] + frandom()*(1-ptype->rgbrandsync[0]);
p->org[1] = p->org[2]*ptype->rgbrandsync[1] + frandom()*(1-ptype->rgbrandsync[1]);
p->org[2] = p->org[2]*ptype->rgbrandsync[2] + frandom()*(1-ptype->rgbrandsync[2]);
p->rgb[0] += p->org[0]*ptype->rgbrand[0] + ptype->rgbchange[0]*p->die;
p->rgb[1] += p->org[1]*ptype->rgbrand[1] + ptype->rgbchange[1]*p->die;
p->rgb[2] += p->org[2]*ptype->rgbrand[2] + ptype->rgbchange[2]*p->die;
p->org[0] = hrandom();
p->org[1] = hrandom();
if (ptype->areaspreadvert)
p->org[2] = hrandom();
else
p->org[2] = 0;
VectorNormalize(p->org);
if (ptype->spawnmode != SM_CIRCLE)
VectorScale(p->org, frandom(), p->org);
p->org[0] = org[0] + p->org[0]*ptype->areaspread;
p->org[1] = org[1] + p->org[1]*ptype->areaspread;
p->org[2] = org[2] + p->org[2]*ptype->areaspreadvert + ptype->offsetup;
p->die = particletime + ptype->die - p->die;
VectorCopy(lastorg, p->u.b.org2);
VectorCopy(lastdir, p->u.b.lastdir);
VectorCopy(p->org, lastorg);
VectorSubtract(p->org, p->u.b.org2, lastdir);
VectorNormalize(lastdir);
}
break;
}
}
else
{
switch (ptype->spawnmode)
{
@ -1823,8 +1832,6 @@ int R_RunParticleEffectType (vec3_t org, vec3_t dir, float count, int typenum)
p->die = particletime + ptype->die - p->die;
}
}
}
if (ptype->assoc < 0)
break;
@ -2039,6 +2046,8 @@ void R_RocketTrail (vec3_t start, vec3_t end, int type, trailstate_t *ts)
int tcount;
particle_t *p;
part_type_t *ptype = &part_type[type];
beamseg_t *b;
beamseg_t *bfirst;
float veladd = -ptype->veladd;
float randvel = ptype->randomvel;
@ -2086,37 +2095,7 @@ void R_RocketTrail (vec3_t start, vec3_t end, int type, trailstate_t *ts)
if (len/step > 1024)
return;
if (!len && ptype->isbeam)
{ //first particle of the trail
switch(ptype->spawnmode)
{
case SM_SPIRAL:
{
float tsin, tcos;
tcos = cos(len/50)*ptype->areaspread;
tsin = sin(len/50)*ptype->areaspread;
ts->lastorg[0] = start[0] + right[0]*tcos + up[0]*tsin;
ts->lastorg[1] = start[1] + right[1]*tcos + up[1]*tsin;
ts->lastorg[2] = start[2] + right[2]*tcos + up[2]*tsin;
}
break;
default:
ts->lastorg[0] = crandom();
ts->lastorg[1] = crandom();
ts->lastorg[2] = crandom();
ts->lastorg[0] = ts->lastorg[0]*ptype->areaspread + start[0];
ts->lastorg[1] = ts->lastorg[1]*ptype->areaspread + start[1];
ts->lastorg[2] = ts->lastorg[2]*ptype->areaspreadvert + start[2];
break;
}
VectorCopy(vec, ts->lastdir);
len += step;
stop += step;
}
b = bfirst = NULL;
while (len < stop)
{
@ -2124,10 +2103,34 @@ void R_RocketTrail (vec3_t start, vec3_t end, int type, trailstate_t *ts)
if (!free_particles)
{
ts->lastdist = stop;
return;
len = stop;
break;
}
p = free_particles;
if (ptype->isbeam)
{
if (!free_beams)
{
len = stop;
break;
}
if (b)
{
b = b->next = free_beams;
free_beams = free_beams->next;
}
else
{
b = bfirst = free_beams;
free_beams = free_beams->next;
}
b->flags = 0;
b->p = p;
VectorCopy(vec, b->dir);
}
free_particles = p->next;
p->next = ptype->particles;
ptype->particles = p;
@ -2168,41 +2171,6 @@ void R_RocketTrail (vec3_t start, vec3_t end, int type, trailstate_t *ts)
p->rgb[1] += p->org[1]*ptype->rgbrand[1] + ptype->rgbchange[1]*p->die;
p->rgb[2] += p->org[2]*ptype->rgbrand[2] + ptype->rgbchange[2]*p->die;
if (ptype->isbeam)
{
switch(ptype->spawnmode)
{
case SM_SPIRAL:
{
float tsin, tcos;
tcos = cos(len/50)*ptype->areaspread;
tsin = sin(len/50)*ptype->areaspread;
p->org[0] = start[0] + right[0]*tcos + up[0]*tsin;
p->org[1] = start[1] + right[1]*tcos + up[1]*tsin;
p->org[2] = start[2] + right[2]*tcos + up[2]*tsin;
}
break;
default:
p->org[0] = crandom();
p->org[1] = crandom();
p->org[2] = crandom();
p->org[0] = p->org[0]*ptype->areaspread + start[0];
p->org[1] = p->org[1]*ptype->areaspread + start[1];
p->org[2] = p->org[2]*ptype->areaspreadvert + start[2];
break;
}
VectorCopy(ts->lastorg, p->u.b.org2);
VectorCopy(ts->lastdir, p->u.b.lastdir);
VectorCopy(p->org, ts->lastorg);
VectorSubtract(p->org, p->u.b.org2, ts->lastdir);
VectorNormalize(ts->lastdir);
}
else
{
VectorCopy (vec3_origin, p->u.p.vel);
p->u.p.nextemit = particletime + ptype->emitstart - p->die;
@ -2268,7 +2236,6 @@ void R_RocketTrail (vec3_t start, vec3_t end, int type, trailstate_t *ts)
p->org[2] = p->org[2]*ptype->areaspreadvert + start[2];
break;
}
}
VectorMA (start, step, vec, start);
@ -2276,6 +2243,47 @@ void R_RocketTrail (vec3_t start, vec3_t end, int type, trailstate_t *ts)
}
ts->lastdist = len;
// update beamseg list
if (ptype->isbeam)
{
if (b)
{
if (ptype->beams)
{
if (ts->lastbeam)
{
b->next = ts->lastbeam->next;
ts->lastbeam->next = bfirst;
ts->lastbeam->flags &= ~BS_LASTSEG;
}
else
{
b->next = ptype->beams;
ptype->beams = bfirst;
}
}
else
{
ptype->beams = bfirst;
b->next = NULL;
}
b->flags |= BS_LASTSEG;
ts->lastbeam = b;
}
if (!free_particles || !free_beams)
{
if (ts->lastbeam)
{
b->flags &= ~BS_LASTSEG;
b->flags |= BS_NODRAW;
ts->lastbeam = NULL;
}
}
}
return; //distance the trail actually moved.
}
@ -2551,10 +2559,27 @@ void GL_DrawSparkedParticle(particle_t *p, part_type_t *type)
glVertex3f (p->org[0]-p->u.p.vel[0]/10, p->org[1]-p->u.p.vel[1]/10, p->org[2]-p->u.p.vel[2]/10);
}
void GL_DrawParticleBeam(particle_t *p, part_type_t *type)
void GL_DrawParticleBeam(beamseg_t *b, part_type_t *type)
{
vec3_t v, point;
vec3_t fwd, cr;
vec3_t cr;
beamseg_t *c;
particle_t *p;
particle_t *q;
// if (!b->next)
// return;
c = b->next;
q = c->p;
// if (!q)
// return;
p = b->p;
// if (!p)
// return;
if (lasttype != type)
{
lasttype = type;
@ -2570,33 +2595,36 @@ void GL_DrawParticleBeam(particle_t *p, part_type_t *type)
glShadeModel(GL_SMOOTH);
glBegin(GL_QUADS);
}
glColor4f(q->rgb[0],
q->rgb[1],
q->rgb[2],
q->alpha);
// glBegin(GL_LINE_LOOP);
VectorSubtract(r_refdef.vieworg, q->org, v);
VectorNormalize(v);
CrossProduct(c->dir, v, cr);
VectorMA(q->org, -q->scale, cr, point);
glVertex3fv(point);
VectorMA(q->org, q->scale, cr, point);
glVertex3fv(point);
glColor4f(p->rgb[0],
p->rgb[1],
p->rgb[2],
p->alpha);
// glBegin(GL_LINE_LOOP);
VectorSubtract(p->org, p->u.b.org2, fwd);
VectorNormalize(fwd);
VectorSubtract(r_refdef.vieworg, p->org, v);
VectorNormalize(v);
CrossProduct(fwd, v, cr);
CrossProduct(b->dir, v, cr); // replace with old p->dir?
VectorMA(p->org, -p->scale, cr, point);
glVertex3fv(point);
VectorMA(p->org, p->scale, cr, point);
glVertex3fv(point);
VectorSubtract(r_refdef.vieworg, p->u.b.org2, v);
VectorNormalize(v);
CrossProduct(p->u.b.lastdir, v, cr);
VectorMA(p->u.b.org2, p->scale, cr, point);
glVertex3fv(point);
VectorMA(p->u.b.org2, -p->scale, cr, point);
VectorMA(p->org, -p->scale, cr, point);
glVertex3fv(point);
// glEnd();
}
#endif
#ifdef SWQUAKE
void SWD_DrawParticleSpark(particle_t *p, part_type_t *type)
@ -2642,7 +2670,7 @@ void SWD_DrawParticleBlob(particle_t *p, part_type_t *type)
D_DrawParticleTrans(p);
}
#endif
void DrawParticleTypes (void texturedparticles(particle_t *,part_type_t*), void sparkparticles(particle_t*,part_type_t*), void beamparticles(particle_t*,part_type_t*))
void DrawParticleTypes (void texturedparticles(particle_t *,part_type_t*), void sparkparticles(particle_t*,part_type_t*), void beamparticles(beamseg_t*,part_type_t*))
{
qboolean (*tr) (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal);
@ -2655,6 +2683,8 @@ void DrawParticleTypes (void texturedparticles(particle_t *,part_type_t*), void
float grav;
vec3_t friction;
float dist;
particle_t *kill_list, *kill_first;
beamseg_t *b, *bkill;
int traces=r_particle_tracelimit.value;
@ -2679,6 +2709,7 @@ void DrawParticleTypes (void texturedparticles(particle_t *,part_type_t*), void
#endif
tr = TraceLineN;
kill_list = kill_first = NULL;
for (i = 0; i < numparticletypes; i++)
{
@ -2687,15 +2718,22 @@ void DrawParticleTypes (void texturedparticles(particle_t *,part_type_t*), void
if (!type->die)
{
while ((p=type->particles))
{
if (!type->isbeam)
{
if (*type->texname)
RQ_AddDistReorder((void*)texturedparticles, p, type, p->org);
else
RQ_AddDistReorder((void*)sparkparticles, p, type, p->org);
}
type->particles = p->next;
p->next = free_particles;
free_particles = p;
// p->next = free_particles;
// free_particles = p;
p->next = kill_list;
kill_list = p;
if (!kill_first) // branch here is probably faster than list traversal later
kill_first = p;
}
continue;
}
@ -2707,110 +2745,17 @@ void DrawParticleTypes (void texturedparticles(particle_t *,part_type_t*), void
if (kill && kill->die < particletime)
{
type->particles = kill->next;
kill->next = free_particles;
free_particles = kill;
// kill->next = free_particles;
// free_particles = kill;
kill->next = kill_list;
kill_list = kill;
if (!kill_first)
kill_first = kill;
continue;
}
break;
}
if (type->isbeam)
{ //beams do not:
//a: emit other particles
//b: move
//c: bounce
//d: stain
//e: obay the laws of physics in any way, shape or form.
//f: rotate
//g: work in SW.
//They do:
//1: change colour
//2: change alpha
//3: change scale
//4: follow ramps
//5: look better than a shitload of blobby particles.
//6: die
//quirks:
//q1: beams store point A in thier origin, and point B in thier velocity
//q2: depth 'testing' evaluates from thier central point.
//g
if (!beamparticles)
{
for (p=type->particles ; p ; p=p->next)
for ( ;; )
{
kill = p->next;
if (kill && kill->die < particletime)
{
p->next = kill->next;
kill->next = free_particles;
free_particles = kill;
continue;
}
break;
}
continue;
}
//6
for (p=type->particles ; p ; p=p->next)
{
for ( ;; )
{
kill = p->next;
if (kill && kill->die < particletime)
{
p->next = kill->next;
kill->next = free_particles;
free_particles = kill;
continue;
}
break;
}
//4
switch (type->rampmode)
{
case RAMP_ABSOLUTE:
ramp = type->ramp + (int)(type->rampindexes * (type->die - (p->die - particletime)) / type->die);
VectorCopy(ramp->rgb, p->rgb);
p->alpha = ramp->alpha;
p->scale = ramp->scale;
break;
case RAMP_DELTA: //particle ramps
ramp = type->ramp + (int)(type->rampindexes * (type->die - (p->die - particletime)) / type->die);
VectorMA(p->rgb, pframetime, ramp->rgb, p->rgb);
p->alpha -= pframetime*ramp->alpha;
p->scale += pframetime*ramp->scale;
break;
case RAMP_NONE: //particle changes acording to it's preset properties.
//1
if (particletime < (p->die-type->die+type->rgbchangetime))
{
p->rgb[0] += pframetime*type->rgbchange[0];
p->rgb[1] += pframetime*type->rgbchange[1];
p->rgb[2] += pframetime*type->rgbchange[2];
}
//2
p->alpha -= pframetime*(type->alpha/type->die)*type->alphachange;
//3
p->scale += pframetime*type->scaledelta;
}
//quirk 2
stop[0] = (p->org[0] + p->u.b.org2[0])/2;
stop[1] = (p->org[1] + p->u.b.org2[1])/2;
stop[2] = (p->org[2] + p->u.b.org2[2])/2;
//5
RQ_AddDistReorder((void*)beamparticles, p, type, stop);
}
continue;
}
grav = type->gravity*pframetime;
VectorScale(type->friction, pframetime, friction);
@ -2822,8 +2767,12 @@ void DrawParticleTypes (void texturedparticles(particle_t *,part_type_t*), void
if (kill && kill->die < particletime)
{
p->next = kill->next;
kill->next = free_particles;
free_particles = kill;
// kill->next = free_particles;
// free_particles = kill;
kill->next = kill_list;
kill_list = kill;
if (!kill_first)
kill_first = kill;
continue;
}
break;
@ -2921,6 +2870,8 @@ void DrawParticleTypes (void texturedparticles(particle_t *,part_type_t*), void
}
}
if (!type->isbeam)
{
if (*type->texname)
RQ_AddDistReorder((void*)texturedparticles, p, type, p->org);
else
@ -2928,8 +2879,92 @@ void DrawParticleTypes (void texturedparticles(particle_t *,part_type_t*), void
}
}
// beams are dealt with here
// kill early entries
for ( ;; )
{
bkill = type->beams;
if (bkill && (bkill->flags & BS_DEAD || bkill->p->die < particletime) && !(bkill->flags & BS_LASTSEG))
{
type->beams = bkill->next;
bkill->next = free_beams;
free_beams = bkill;
continue;
}
break;
}
b = type->beams;
if (!b)
continue;
for ( ;; )
{
if (b->next)
{
// mark dead entries
if (b->flags & (BS_LASTSEG|BS_DEAD|BS_NODRAW))
{
// kill some more dead entries
for ( ;; )
{
bkill = b->next;
if (bkill && (bkill->flags & BS_DEAD) && !(bkill->flags & BS_LASTSEG))
{
b->next = bkill->next;
bkill->next = free_beams;
free_beams = bkill;
continue;
}
break;
}
if (!bkill) // have to check so we don't hit NULL->next
continue;
}
else
{
if (!(b->next->flags & BS_DEAD))
{
VectorCopy(b->next->p->org, stop);
VectorCopy(b->p->org, oldorg);
VectorSubtract(stop, oldorg, b->next->dir);
VectorNormalize(b->next->dir);
VectorAdd(stop, oldorg, stop);
VectorScale(stop, 0.5, stop);
RQ_AddDistReorder((void*)beamparticles, b, type, stop);
}
// if (b->p->die < particletime)
// b->flags |= BS_DEAD;
}
}
else
{
if (b->p->die < particletime) // end of the list check
b->flags |= BS_DEAD;
break;
}
if (b->p->die < particletime)
b->flags |= BS_DEAD;
b = b->next;
}
}
RQ_RenderDistAndClear();
// lazy delete for particles is done here
if (kill_list)
{
kill_first->next = free_particles;
free_particles = kill_list;
}
particletime += pframetime;
}

View file

@ -192,10 +192,13 @@ void MediaSW_ShowFrameBGR_24_Flip(qbyte *framedata, int inwidth, int inheight);
void R_ParseParticleEffect (void);
void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count);
typedef struct beamseg_s beamseg_t;
typedef struct {
float lastdist;
vec3_t lastorg; //use only if lastdist
vec3_t lastdir; //use only if lastdist
beamseg_t *lastbeam; // last beam point
} trailstate_t;
void R_RocketTrail (vec3_t start, vec3_t end, int type, trailstate_t *oldpoint);
int R_RunParticleEffectType(vec3_t org, vec3_t dir, float count, int type);

View file

@ -62,10 +62,11 @@ typedef struct particle_s
float rotationspeed;
float nextemit;
} p; //point blob
struct {
/* struct {
vec3_t org2;
vec3_t lastdir;
} b; //beam
} b;
*/
} u;
// drivers never touch the following fields
@ -74,6 +75,18 @@ typedef struct particle_s
float die;
} particle_t;
#define BS_LASTSEG 0x1 // no draw to next, no delete
#define BS_DEAD 0x2 // segment is dead
#define BS_NODRAW 0x4 // only used for lerp switching
typedef struct beamseg_s
{
particle_t *p;
struct beamseg_s *next; // next in beamseg list
int flags; // flags for beamseg
vec3_t dir;
} beamseg_t;
#define PARTICLE_Z_CLIP 8.0
#define frandom() (rand()*(1.0f/RAND_MAX))