Files
illusion-arena-engine/code/client/fx_main.c
leilei- 1947afbe2e - Widescreen fixes, should expand on all viewsize values now and leave less refdefs alone
- Accumulation buffer motion blur, which should work on shader model 2 hardware with less memory footprint. Pixel shader moved to r_motionblur 3 (will do texture motionblur on 2)
- Tried to make r_tvMode 2 half vertical resolution, and failed.
- Q3MME's FX scripted particle system files, just dropped in there, because they can't compile at all atm. Procrastinplanning on integrating this to replace my cgame effects
2014-06-07 05:07:56 -04:00

1569 lines
47 KiB
C

/*
===========================================================================
Copyright (C) 2006 Sjoerd van der Berg ( harekiet @ gmail.com )
This file is part of Quake III Arena source code.
Quake III Arena source code 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.
Quake III Arena source code 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 Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// fx_main.c Run FX Scripts and provide several commands for them
#include "client.h"
#include "fx_local.h"
fxMain_t fx;
cvar_t *fx_Megs;
cvar_t *fx_Vibrate;
cvar_t *fx_Override;
cvar_t *fx_Debug;
static ID_INLINE void fxMaskInput( const byte *mask, const void *readV, void *writeV ) {
const long *read = (long *)readV;
long *write = (long*)writeV;
read += mask[0];
mask++;
while (1) {
int count;
/* Copy blocks */
if (!mask[0])
break;
count = mask[0];
do {
*write++ = *read++;
count--;
} while (count > 0);
/* Skip blocks */
if (!mask[1])
break;
read += mask[1];
mask += 2;
}
}
static ID_INLINE void fxMaskOutput( const byte *mask, const void *readV, void *writeV ) {
const long *read = (long *)readV;
long *write = (long*)writeV;
write += mask[0];
mask++;
while (1) {
int count;
/* Copy blocks */
if (!mask[0])
break;
count = mask[0];
do {
*write++ = *read++;
count--;
} while (count > 0);
/* Skip blocks */
if (!mask[1])
break;
write += mask[1];
mask += 2;
}
}
//Some forward defines
static void *fxGetActive( unsigned int key1, unsigned int key2, int size );
static const void *fxRun( fxRun_t *run, const void *data );
static ID_INLINE int fxRand( void ) {
fx.seed = (69069 * fx.seed + 1);
return fx.seed;
}
static ID_INLINE float fxRandom( void ) {
return (fxRand() & 0xffff) * (1.0f / 0x10000);
}
/* Very simple allocation function */
static void *fxAlloc( int size ) {
int *search = fx.allocSearch;
int *searchStart = search;
size += sizeof( int );
//TODO make size a multiple of sizeof (void *)
while (1) {
//Used block
if ( search[0] == size ) {
search[0] = -size;
fx.allocSearch = (int*)(((char *)search) + size);
return search + 1;
} else if ( search[0] > size ) {
fx.allocSearch = (int*)(((char *)search) + size);
fx.allocSearch[0] = search[0] - size;
search[0] = -size;
return search + 1;
} else if ( search[0] < 0 ) {
search = (int*)(((char *)search) - search[0]);
//Last block
} else if (!search[0] ) {
search = fx.allocStart;
//Free block but too small, find a free one after it and join
} else {
int *searchNext = (int*)(((char *)search) + search[0]);
/* Is the next one free, if so add it */
if ( searchNext[0] > 0 ) {
search[0] += searchNext[0];
/* Added the start one and still not enough room? */
if ( searchStart == searchNext && search[0] < size )
break;
continue;
} else {
search = searchNext;
}
}
/* Check if it went a full loop, out of space then */
if ( search == searchStart) {
break;
}
}
if ( fx_Debug->integer ) {
Com_Printf("FX_Debug:Ran out of allocation space, needed %d\n", size );
} else {
Com_Error( ERR_DROP, "FX Ran out of allocation space for %d bytes, increase fx_Megs\n", size );
}
return 0;
}
static void fxVibrate( const vec3_t position, float strength ) {
if ( fx.vibrate.used < FX_VIBRATE_QUEUE ) {
int i = fx.vibrate.used++;
VectorCopy( position, fx.vibrate.queue[i].origin );
fx.vibrate.queue[i].strength = strength;
}
}
static void fxDoVibrate( const vec3_t origin, const vec3_t position, float strength ) {
vec3_t dist;
float magnitude, oldmagnitude;
VectorSubtract( origin, position, dist );
magnitude = 1.0f - VectorLength( dist ) / 1000.0;
if( magnitude <= 0 )
return;
magnitude = strength * magnitude * magnitude;
if( fx.vibrate.time ) {
// Adjust for current vibration
int time = fx.time - fx.vibrate.time;
if( time < 20 )
time = 20;
oldmagnitude = fx.vibrate.magnitude / (time / 20.0f);
}
else {
oldmagnitude = 0;
}
fx.vibrate.magnitude = magnitude + oldmagnitude;
if( fx.vibrate.magnitude < 0 )
fx.vibrate.magnitude= 0;
fx.vibrate.offset = fxRandom() * 360; // Always on the 'up' half
fx.vibrate.time = fx.time;
}
void FX_VibrateView( float scale, vec3_t origin, vec3_t angles ) {
int i, time;
float magnitude;
scale *= fx_Vibrate->value;
if (scale <= 0) {
fx.vibrate.used = 0;
return;
}
for ( i = 0; i < fx.vibrate.used; i++ ) {
fxDoVibrate( origin, fx.vibrate.queue[i].origin, fx.vibrate.queue[i].strength * scale );
}
fx.vibrate.used = 0;
if( !fx.vibrate.time )
return;
time = fx.time - fx.vibrate.time;
if( time < 20 )
time = 20;
magnitude = ((fx.vibrate.magnitude > 100) ? 100 : fx.vibrate.magnitude) / (time / 20.0f);
if( magnitude <= 3 )
{
// There's not enough vibration to care about
fx.vibrate.time = 0;
return;
}
origin[2] += magnitude * 0.3 * sin( 1.4 * (fx.vibrate.offset + SQRTFAST(time)) );
angles[ROLL] += magnitude * (0.1 * sin( 0.1 * (fx.vibrate.offset + SQRTFAST(time)) ) - 0.05);
}
static int fxSize( void *block ) {
return ((int*)block)[-1];
}
static void fxFree( void *block ) {
int *alloc = ((int*)block) - 1;
alloc[0] = -alloc[0];
}
static const void *fxRunDecal( const fxRun_t *run, const fxRunDecal_t *decal ) {
int i;
vec4_t color;
for (i = 0;i<4;i++)
color[i] = run->color[i] * ( 1.0f / 255);
re.DecalProject( run->shader, run->origin, run->dir, color, run->rotate, run->size, decal->flags, decal->life );
return decal + 1;
}
static const void* fxRunBeam( const fxRun_t *run, const fxRunRender_t* render ) {
refBeam_t beam;
VectorCopy( run->origin, beam.start );
VectorAdd( beam.start, run->dir, beam.end );
beam.width = run->size;
beam.rotateAngle = run->rotate;
beam.shaderTime = run->shaderTime;
beam.shader = run->shader;
beam.renderfx = render->renderfx;
Byte4Copy( run->color, beam.color );
re.AddBeamToScene( &beam );
return render + 1;
}
static const void* fxRunSpark( const fxRun_t *run, const fxRunRender_t* render ) {
refBeam_t spark;
VectorNormalize2( run->velocity, spark.end );
VectorMA( run->origin, -0.5*run->size, spark.end, spark.start );
VectorMA( run->origin, 0.5*run->size, spark.end, spark.end );
spark.width = run->width;
spark.rotateAngle = 0;
spark.shaderTime = run->shaderTime;
spark.shader = run->shader;
spark.renderfx = render->renderfx;
Byte4Copy( run->color, spark.color );
re.AddBeamToScene( &spark );
return render + 1;
}
static const void* fxRunRings( const fxRun_t *run, const fxRunRender_t* render ) {
refRings_t rings;
VectorCopy( run->origin, rings.start );
VectorAdd( rings.start, run->dir, rings.end );
rings.radius = run->size;
rings.stepSize = run->width;
rings.shaderTime = run->shaderTime;
rings.shader = run->shader;
rings.renderfx = render->renderfx;
Byte4Copy( run->color, rings.color );
re.AddRingsToScene( &rings );
return render + 1;
}
static const void* fxRunSprite( const fxRun_t* run, const fxRunRender_t* render ) {
refSprite_t sprite;
VectorCopy( run->origin, sprite.origin );
sprite.shader = run->shader;
sprite.radius = run->size;
sprite.rotation = run->rotate;
sprite.shaderTime = run->shaderTime;
Byte4Copy( run->color, sprite.color );
sprite.renderfx = render->renderfx;
re.AddSpriteToScene( &sprite );
return render + 1;
}
static const void* fxRunQuad( const fxRun_t *run, const fxRunRender_t* render ) {
polyVert_t verts[4];
vec3_t left, up;
PerpendicularVector( up, run->dir );
RotatePointAroundVector( left, run->dir, up, run->angles[ROLL] );
CrossProduct( run->dir, left, up );
VectorScale( up, run->size, up );
VectorScale( left, -run->size, left );
verts[0].xyz[0] = run->origin[0] + left[0] + up[0];
verts[0].xyz[1] = run->origin[1] + left[1] + up[1];
verts[0].xyz[2] = run->origin[2] + left[2] + up[2];
verts[1].xyz[0] = run->origin[0] - left[0] + up[0];
verts[1].xyz[1] = run->origin[1] - left[1] + up[1];
verts[1].xyz[2] = run->origin[2] - left[2] + up[2];
verts[2].xyz[0] = run->origin[0] - left[0] - up[0];
verts[2].xyz[1] = run->origin[1] - left[1] - up[1];
verts[2].xyz[2] = run->origin[2] - left[2] - up[2];
verts[3].xyz[0] = run->origin[0] + left[0] - up[0];
verts[3].xyz[1] = run->origin[1] + left[1] - up[1];
verts[3].xyz[2] = run->origin[2] + left[2] - up[2];
Byte4Copy( run->color, verts[0].modulate );
Byte4Copy( run->color, verts[1].modulate );
Byte4Copy( run->color, verts[2].modulate );
Byte4Copy( run->color, verts[3].modulate );
verts[0].st[0] = 0; verts[0].st[1] = 0;
verts[1].st[0] = 1; verts[1].st[1] = 0;
verts[2].st[0] = 1; verts[2].st[1] = 1;
verts[3].st[0] = 0; verts[3].st[1] = 1;
re.AddPolyToScene( run->shader, 4, verts, 1 );
return render + 1;
}
static const void* fxRunAnglesModel( const fxRun_t *run, const fxRunRender_t* render ) {
refModel_t model;
memset( &model, 0, sizeof( model ));
VectorCopy( run->origin, model.origin );
VectorCopy( run->origin, model.oldorigin );
model.hModel = run->model;
model.customShader = run->shader;
model.shaderTime = run->shaderTime;
model.renderfx = render->renderfx;
Byte4Copy( run->color, model.shaderRGBA );
// Setup the axis from the angles
AnglesToAxis( run->angles, model.axis );
if ( run->size > 0 && run->size != 1 ) {
VectorScale( model.axis[0], run->size, model.axis[0] );
VectorScale( model.axis[1], run->size, model.axis[1] );
VectorScale( model.axis[2], run->size, model.axis[2] );
model.nonNormalizedAxes = qtrue;
}
re.AddModelToScene( &model );
return render + 1;
}
static const void* fxRunDirModel( const fxRun_t *run, const fxRunRender_t* render ) {
refModel_t model;
memset( &model, 0, sizeof( model ));
VectorCopy( run->origin, model.origin );
VectorCopy( run->origin, model.oldorigin );
model.hModel = run->model;
model.customShader = run->shader;
model.shaderTime = run->shaderTime;
model.renderfx = render->renderfx;
Byte4Copy( run->color, model.shaderRGBA );
// Setup the axis from the angles
VectorCopy( run->dir, model.axis[0] );
RotateAroundDirection( model.axis, run->rotate );
if ( run->size != 1 ) {
VectorScale( model.axis[0], run->size, model.axis[0] );
VectorScale( model.axis[1], run->size, model.axis[1] );
VectorScale( model.axis[2], run->size, model.axis[2] );
model.nonNormalizedAxes = qtrue;
}
re.AddModelToScene( &model );
return render + 1;
}
static const void* fxRunAxisModel( const fxRun_t *run, const fxRunRender_t* render ) {
refModel_t model;
memset( &model, 0, sizeof( model ));
VectorCopy( run->origin, model.origin );
VectorCopy( run->origin, model.oldorigin );
model.hModel = run->model;
model.customShader = run->shader;
model.shaderTime = run->shaderTime;
model.renderfx = render->renderfx;
Byte4Copy( run->color, model.shaderRGBA );
// Setup the axis from the angles
if ( run->size != 1 ) {
VectorScale( run->axis[0], run->size, model.axis[0] );
VectorScale( run->axis[1], run->size, model.axis[1] );
VectorScale( run->axis[2], run->size, model.axis[2] );
model.nonNormalizedAxes = qtrue;
} else {
VectorCopy( run->axis[0], model.axis[0] );
VectorCopy( run->axis[1], model.axis[1] );
VectorCopy( run->axis[2], model.axis[2] );
model.nonNormalizedAxes = qfalse;
}
re.AddModelToScene( &model );
return render + 1;
}
static void fxRunLight( fxRun_t *run ) {
float r = run->color[0] * ( 1 / 255.0f );
float g = run->color[1] * ( 1 / 255.0f );
float b = run->color[2] * ( 1 / 255.0f );
re.AddLightToScene( run->origin, run->size, r, g, b);
}
static void fxRunTrace( fxRun_t *run ) {
trace_t tr;
vec3_t end;
VectorMA( run->origin, 8000, run->dir, end );
CM_BoxTrace( &tr, run->origin, end, 0, 0, 0, CONTENTS_SOLID, qfalse );
if ( tr.fraction < 1 ) {
VectorCopy( tr.endpos, run->origin );
VectorCopy( tr.plane.normal, run->dir );
}
}
static const void *fxRunShader( fxRun_t *run, const fxRunShader_t *shader ) {
run->shader = shader->shader;
return shader + 1;
}
static const void *fxRunShaderList( fxRun_t *run, const fxRunShaderList_t *shaderList ) {
float i;
qhandle_t *list = (qhandle_t*)fxRunMath( run, shaderList + 1, &i );
run->shader = list[ (unsigned int)(i * shaderList->count) % shaderList->count ];
return list + shaderList->count;
}
static const void *fxRunModel( fxRun_t *run, const fxRunModel_t *model ) {
run->model = model->model;
return model + 1;
}
static const void *fxRunModelList( fxRun_t *run, const fxRunModelList_t *modelList ) {
float i;
qhandle_t *list = (qhandle_t*)fxRunMath( run, modelList + 1, &i );
run->model = list[ (unsigned int)(i * modelList->count) % modelList->count ];
return list + modelList->count;
}
static const void *fxRunSoundList( const fxRun_t *run, const fxRunSoundList_t *sound ) {
unsigned int index = fxRand() % sound->count;
S_StartSound( run->origin, 0, 0, sound->handle[index] );
return &sound->handle[ sound->count ];
}
static const void *fxRunSound( const fxRun_t *run, const fxRunSound_t *sound ) {
S_StartSound( run->origin, 0, 0, sound->handle );
return sound + 1;
}
static const void *fxRunLoopSound( const fxRun_t *run, const fxRunLoopSound_t *loopSound ) {
S_LoopingSound( (void *)run->key, run->origin, run->velocity, loopSound->handle, 127 );
return loopSound + 1;
}
static const void *fxRunColor( fxRun_t *run, const fxRunColor_t *color ) {
run->color[0] = color->value[0];
run->color[1] = color->value[1];
run->color[2] = color->value[2];
return color + 1;
}
static const void *fxRunColorList( fxRun_t *run, const fxRunColorList_t *colorList ) {
float i;
color4ub_t *list = (color4ub_t*)fxRunMath( run, colorList + 1, &i );
byte *newColor;
newColor = list[(unsigned int)(i * colorList->count) % colorList->count ];
run->color[0] = newColor[0];
run->color[1] = newColor[1];
run->color[2] = newColor[2];
return list + colorList->count;
}
static const void *fxRunColorBlend( fxRun_t *run, const fxRunColorBlend_t *colorBlend ) {
float i;
unsigned int index;
int blend;
color4ub_t *list = (color4ub_t*)fxRunMath( run, colorBlend + 1, &i );
const byte *start, *end;
i *= colorBlend->count-1;
index = (int)( i );
start = list[ index % colorBlend->count ];
end = list[ (index + 1) % colorBlend->count ];
blend = Q_ftol(i * 256.0f) & 255;
run->color[0] = start[0] + ((( end[0] - start[0] ) * blend) >> 8);
run->color[1] = start[1] + ((( end[1] - start[1] ) * blend) >> 8);
run->color[2] = start[2] + ((( end[2] - start[2] ) * blend) >> 8);
return list + colorBlend->count;
}
static const void *fxRunColorHue( fxRun_t *run, const void *data ) {
float i;
const void *next = fxRunMath( run, data, &i );
float deg, angle, v;
deg = (i * 360);
angle = DEG2RAD( fmodf( deg, 120));
v = ((cos(angle) / cos((M_PI / 3) - angle)) + 1) / 3;
if ( deg <= 120) {
run->color[0] = (v*255);
run->color[1] = ((1-v)*255);
run->color[2] = 0;
} else if ( deg <= 240) {
run->color[0] = 0;
run->color[1] = (v*255);
run->color[2] = ((1-v)*255);
} else {
run->color[0] = ((1-v)*255);
run->color[1] = 0;
run->color[2] = (v*255);
}
return next;
}
static const void *fxRunColorMath( fxRun_t *run, const fxRunColorMath_t *colorMath ) {
float i;
const void *next = fxRunMath( run, colorMath + 1, &i );
byte *color = run->color + colorMath->index;
if ( i < 0)
color[0] = 0;
else if ( i > 1.0f)
color[0] = 255;
else
color[0] = Q_ftol( 255 * i );
return next;
}
static const void *fxRunColorScale( fxRun_t *run, const void *data ) {
float i;
const void *next = fxRunMath( run, data, &i );
int s = Q_ftol(255 * i);
run->color[0] = (s * run->color[0]) >> 8;
run->color[1] = (s * run->color[1]) >> 8;
run->color[2] = (s * run->color[2]) >> 8;
return next;
}
static const void *fxRunColorFade( fxRun_t *run, const fxRunColorFade_t *colorFade ) {
if ( run->lerp > colorFade->delay ) {
int s = 255 - (run->lerp - colorFade->delay) * 255 * (1.0f / (1.0f - colorFade->delay));
run->color[0] = (s * run->color[0]) >> 8;
run->color[1] = (s * run->color[1]) >> 8;
run->color[2] = (s * run->color[2]) >> 8;
}
return colorFade + 1;
}
static const void *fxRunAlphaFade( fxRun_t *run, const fxRunAlphaFade_t *alphaFade ) {
if ( run->lerp > alphaFade->delay ) {
unsigned int s = 255 + Q_ftol( (run->lerp - alphaFade->delay) * alphaFade->scale);
run->color[3] = (s * run->color[3]) >> 8;
}
return alphaFade + 1;
}
static const void *fxRunVibrate( fxRun_t *run, const fxRunVibrate_t *vibrate) {
fxVibrate( run->origin, vibrate->strength );
return vibrate + 1;
}
static const void *fxRunScale( fxRun_t *run, const fxRunScale_t *scale) {
const float *src = fxFloatSrc( run, scale->src );
float *dst = fxFloatDst( run, scale->dst );
float v;
const void *next;
next = fxRunMath( run, scale+1, &v );
VectorScale( src, v, dst );
return next;
}
static const void *fxRunValue( fxRun_t *run, const fxRunValue_t *value) {
float *dst = fxFloatDst( run, value->dst );
return fxRunMath( run, value + 1, dst );
}
static const void *fxRunClear( fxRun_t *run, const fxRunClear_t *clear) {
float *dst = fxFloatDst( run, clear->dst );
VectorClear( dst );
return clear + 1;
}
static const void *fxRunAddScale( fxRun_t *run, const fxRunAddScale_t *addScale) {
const float *src = fxFloatSrc( run, addScale->src );
const float *scale = fxFloatSrc( run, addScale->scale );
float *dst = fxFloatDst( run, addScale->dst );
float v;
const void *next;
next = fxRunMath( run, addScale+1, &v );
VectorMA( src, v, scale, dst );
return next;
}
static const void *fxRunSubScale( fxRun_t *run, const fxRunSubScale_t *subScale) {
const float *src = fxFloatSrc( run, subScale->src );
const float *scale = fxFloatSrc( run, subScale->scale );
float *dst = fxFloatDst( run, subScale->dst );
float v;
const void *next;
next = fxRunMath( run, subScale+1, &v );
VectorMA( src, -v, scale, dst );
return next;
}
static const void *fxRunAdd( fxRun_t *run, const fxRunAdd_t *add) {
const float *src1 = fxFloatSrc( run, add->src1 );
const float *src2 = fxFloatSrc( run, add->src2 );
float *dst = fxFloatDst( run, add->dst );
VectorAdd( src1, src2, dst );
return add + 1;
}
static const void *fxRunSub( fxRun_t *run, const fxRunSub_t *sub) {
const float *src1 = fxFloatSrc( run, sub->src1 );
const float *src2 = fxFloatSrc( run, sub->src2 );
float *dst = fxFloatDst( run, sub->dst );
VectorSubtract( src1, src2, dst );
return sub + 1;
}
static const void *fxRunRotateAround( fxRun_t *run, const fxRunRotateAround_t *rotateAround) {
const float *src = fxFloatSrc( run, rotateAround->src );
const float *dir = fxFloatSrc( run, rotateAround->dir );
float *dst = fxFloatDst( run, rotateAround->dst );
float v;
const void *next;
next = fxRunMath( run, rotateAround+1, &v );
RotatePointAroundVector( dst, dir, src, v );
return next;
}
static const void *fxRunCopy( fxRun_t *run, const fxRunCopy_t *copy) {
const float *src = fxFloatSrc( run, copy->src );
float *dst = fxFloatDst( run, copy->dst );
VectorCopy( src, dst );
return copy + 1;
}
static const void *fxRunInverse( fxRun_t *run, const fxRunInverse_t *inverse ) {
const float *src = fxFloatSrc( run, inverse->src );
float *dst = fxFloatDst( run, inverse->dst );
dst[0] = -src[0];
dst[1] = -src[1];
dst[2] = -src[2];
return inverse+1;
}
static const void *fxRunNormalize( fxRun_t *run, const fxRunNormalize_t *normalize ) {
const float *src = fxFloatSrc( run, normalize->src );
float *dst = fxFloatDst( run, normalize->dst );
VectorNormalize2( src, dst );
return normalize+1;
}
static const void *fxRunPerpendicular( fxRun_t *run, const fxRunPerpendicular_t *perpendicular ) {
const float *src = fxFloatSrc( run, perpendicular->src );
float *dst = fxFloatDst( run, perpendicular->dst );
PerpendicularVector( dst, src );
return perpendicular+1;
}
static const void *fxRunWobble( fxRun_t *run, const fxRunWobble_t *wobble ) {
const float *src = fxFloatSrc( run, wobble->src );
float *dst = fxFloatDst( run, wobble->dst );
float v;
const void *next;
float angle, s;
vec3_t axis[3];
next = fxRunMath( run, wobble+1, &v );
/* Check for the unlikely upward/downward vector */
VectorCopy( src, axis[0] );
RotateAroundDirection( axis, fxRandom() * 360 );
angle = DEG2RAD( v );
s = cos( angle );
VectorScale( axis[0], s, dst );
s = sin( angle );
VectorMA( dst, s, axis[2], dst );
return next;
}
static const void *fxRunMakeAngles( fxRun_t *run, const fxRunMakeAngles_t *makeAngles ) {
const float *src = fxFloatSrc( run, makeAngles->src );
float *dst = fxFloatDst( run, makeAngles->dst );
vectoangles( src, dst );
return makeAngles + 1;
}
static const void *fxRunRandom( fxRun_t *run, const fxRunRandom_t *random ) {
float *dst = fxFloatDst( run, random->dst );
float yaw = fxRandom() * 2 * M_PI;
float pitch = fxRandom() * 2 * M_PI;
float cp = cos ( pitch );
float sp = sin ( pitch );
float cy = cos ( yaw );
float sy = sin ( yaw );
dst[0] = cp*cy;
dst[1] = cp*sy;
dst[2] = -sp;
return random + 1;
}
static const void *fxRunEmitter( fxRun_t *run, const fxRunEmitter_t *emitter ) {
fxEntity_t *entity;
entity = fxAlloc( emitter->allocSize );
if ( entity ) {
float life;
entity->next = fx.entityNew;
fx.entityNew = entity;
entity->startTime = fx.time;
fxRunMath( run, emitter + 1, &life );
entity->lifeScale = 1.0f / (life * 1000);
entity->traceTime = 0;
entity->moveTime = 0;
entity->flags = 0;
VectorCopy( run->origin, entity->origin );
VectorCopy( run->velocity, entity->velocity );
entity->emitter = emitter;
fxMaskInput( emitter->mask, run->velocity + 3, entity + 1 );
}
return ((byte *)emitter) + emitter->size;
}
const void *fxRunRepeat( fxRun_t *run, const fxRunRepeat_t *repeat ) {
int i, count;
const void *next;
float v, loopScale;
next = fxRunMath( run, repeat + 1, &v );
count = Q_ftol(v);
loopScale = 1.0 / count;
for (i = 0; i < count; i++ ) {
run->loop = i * loopScale;
fxRun( run, next );
}
return ((char *)repeat) + repeat->size;
}
const void *fxRunIf( fxRun_t *run, const fxRunIf_t *runIf ) {
int i;
const void *next;
float v;
next = runIf + 1;
for ( i = 0; i<runIf->testCount;i++) {
int *blockSize;
next = fxRunMath( run, next, &v );
blockSize = (int *)next;
next = blockSize + 1;
if ( v ) {
fxRun( run, next );
return ((char *)runIf) + runIf->size;
} else {
/* Skip the block for this if case */
next = ((char *)(next)) + blockSize[0];
}
}
if ( runIf->elseStep) {
return next;
} else {
return ((char *)runIf) + runIf->size;
}
}
static const void *fxRunSkip( fxRun_t *run, const fxRunSkip_t *skip ) {
return ((byte *)skip) + skip->size;
}
static const void *fxRunDistance( fxRun_t *run, const fxRunDistance_t *distance ) {
fxActiveDistance_t *active;
vec3_t delta, realOrigin;
float lengthScale;
if ( run->entity && run->entity->flags & FX_ENT_STATIONARY )
return ((byte *)distance) + distance->size;
active = fxGetActive( run->key, (unsigned int)distance, sizeof( fxActiveDistance_t) );
if ( !active )
return ((byte *)distance) + distance->size;
if ( !active->beenUsed ) {
active->beenUsed = qtrue;
VectorCopy( run->origin, active->lastOrigin );
return ((byte *)distance) + distance->size;
}
VectorSubtract( run->origin, active->lastOrigin, delta );
lengthScale = VectorNormalize( delta );
if ( lengthScale + active->distance < 0 ) {
return ((byte *)distance) + distance->size;
}
active->distance += lengthScale;
lengthScale = -active->distance;
VectorCopy( run->origin, realOrigin );
while ( active->distance >= 0 ) {
float step;
const void *mathEnd = fxRunMath( run, distance + 1, &step );
VectorMA( active->lastOrigin, lengthScale, delta, run->origin );
lengthScale += step;
active->distance -= step;
fxRun( run, mathEnd );
}
VectorCopy( realOrigin, active->lastOrigin );
VectorCopy( realOrigin, run->origin );
return ((byte *)distance) + distance->size;
}
static const void *fxRunInterval( fxRun_t *run, const fxRunInterval_t *interval ) {
float i;
fxActiveInterval_t *active;
const void *mathNext;
active = fxGetActive( run->key, (unsigned int)interval, sizeof( fxActiveInterval_t) );
if ( !active->beenUsed ) {
mathNext = fxRunMath( run, interval + 1, &i );
if ( i <= 0)
goto returnIt;
active->beenUsed = qtrue;
fxRun( run, mathNext);
active->nextTime = fx.time + i * 1000;
} else while( active->nextTime <= fx.time ) {
mathNext = fxRunMath( run, interval + 1, &i );
if ( i <= 0)
break;
fxRun( run, mathNext);
active->nextTime += i * 1000;
}
returnIt:
return ((byte *)interval) + interval->size;
}
static const void *fxRunOnce( fxRun_t *run, const fxRunOnce_t *once ) {
fxActiveOnce_t *active;
active = fxGetActive( run->key, (unsigned int)once, sizeof( fxActiveOnce_t) );
if ( !active->beenUsed ) {
active->beenUsed = qtrue;
fxRun( run, once + 1);
}
return ((byte *)once) + once->size;
}
static const void *fxRunPushParent( fxRun_t *run, const fxRunPushParent_t *push ) {
int i;
float *input = ( float *)(((char *)(run->parent)) + push->offset);
if ( run->stackUsed + push->count > FX_STACK_SIZE ) {
Com_Printf( "Overflowing run stack\n" );
return push + 1;
}
for (i = push->count; i > 0; i--) {
run->stackData[run->stackUsed++] = *input++;
}
return push + 1;
}
static const void *fxRunPush( fxRun_t *run, const fxRunPush_t *push ) {
int i;
float *input = ( float *)(((char *)run) + push->offset);
if ( run->stackUsed + push->count > FX_STACK_SIZE ) {
Com_Printf( "Overflowing run stack\n" );
return push + 1;
}
for (i = push->count; i > 0; i--) {
run->stackData[run->stackUsed++] = *input++;
}
return push + 1;
}
static const void *fxRunPop( fxRun_t *run, const fxRunPop_t *pop ) {
int i;
float *output = ( float *)(((char *)run) + pop->offset);
if ( run->stackUsed - pop->count < 0) {
Com_Printf( "underflowing run stack\n" );
return pop + 1;
}
for (i = pop->count; i > 0; i--) {
output[i-1] = run->stackData[--run->stackUsed];
}
return pop + 1;
}
static const void *fxRun( fxRun_t *run, const void *data );
static const void *fxRunScript( fxRun_t *run, const fxRunScript_t *script ) {
fxRun( run, script->data );
return script + 1;
}
static const void *fxRun( fxRun_t *run, const void *data ) {
while ( 1 ) {
const int *cmdList = (int *)data;
int cmd = cmdList[0];
data = cmdList + 1;
switch ( cmd ) {
case fxCmdHalt:
return data;
default:
Com_Error( ERR_DROP, "FX:Unknown command %d\n", cmd );
break;
case fxCmdSkip:
data = fxRunSkip( run, (fxRunSkip_t*)data);
break;
case fxCmdKill:
if ( run->entity )
run->entity->flags |= FX_ENT_DEAD;
return data;
case fxCmdEmitter:
data = fxRunEmitter( run, (fxRunEmitter_t*)data);
break;
case fxCmdRepeat:
data = fxRunRepeat( run, (fxRunRepeat_t*)data);
break;
case fxCmdDistance:
data = fxRunDistance( run, (fxRunDistance_t*)data);
break;
case fxCmdInterval:
data = fxRunInterval( run, (fxRunInterval_t*)data);
break;
case fxCmdIf:
data = fxRunIf( run, (fxRunIf_t*)data);
break;
case fxCmdOnce:
data = fxRunOnce( run, (fxRunOnce_t*)data);
break;
case fxCmdLight:
fxRunLight( run );
break;
case fxCmdSprite:
data = fxRunSprite( run, (fxRunRender_t*)data );
break;
case fxCmdQuad:
data = fxRunQuad( run, (fxRunRender_t*)data );
break;
case fxCmdSpark:
data = fxRunSpark( run, (fxRunRender_t*)data );
break;
case fxCmdBeam:
data = fxRunBeam( run, (fxRunRender_t*)data );
break;
case fxCmdRings:
data = fxRunRings( run, (fxRunRender_t*)data );
break;
case fxCmdAxisModel:
data = fxRunAxisModel( run, (fxRunRender_t*)data );
break;
case fxCmdAnglesModel:
data = fxRunAnglesModel( run, (fxRunRender_t*)data );
break;
case fxCmdDirModel:
data = fxRunDirModel( run, (fxRunRender_t*)data );
break;
case fxCmdDecal:
data = fxRunDecal( run, (fxRunDecal_t*)data );
break;
case fxCmdTrace:
fxRunTrace( run );
break;
case fxCmdScript:
data = fxRunScript( run, (fxRunScript_t*)data );
break;
case fxCmdColor:
data = fxRunColor( run, (fxRunColor_t*)data);
break;
case fxCmdColorList:
data = fxRunColorList( run, (fxRunColorList_t*)data);
break;
case fxCmdColorBlend:
data = fxRunColorBlend( run, (fxRunColorBlend_t*)data);
break;
case fxCmdColorMath:
data = fxRunColorMath( run, (fxRunColorMath_t*)data);
break;
case fxCmdColorScale:
data = fxRunColorScale( run, data);
break;
case fxCmdColorHue:
data = fxRunColorHue( run, data);
break;
case fxCmdShader:
data = fxRunShader( run, (fxRunShader_t*)data);
break;
case fxCmdShaderList:
data = fxRunShaderList( run, (fxRunShaderList_t*)data);
break;
case fxCmdModel:
data = fxRunModel( run, (fxRunModel_t*)data);
break;
case fxCmdModelList:
data = fxRunModelList( run, (fxRunModelList_t*)data);
break;
case fxCmdSound:
data = fxRunSound( run, (fxRunSound_t*)data);
break;
case fxCmdSoundList:
data = fxRunSoundList( run, (fxRunSoundList_t*)data);
break;
case fxCmdLoopSound:
data = fxRunLoopSound( run, (fxRunLoopSound_t*)data);
break;
case fxCmdVibrate:
data = fxRunVibrate( run, (fxRunVibrate_t*)data);
break;
case fxCmdAlphaFade:
data = fxRunAlphaFade( run, (fxRunAlphaFade_t*)data);
break;
case fxCmdColorFade:
data = fxRunColorFade( run, (fxRunColorFade_t*)data);
break;
case fxCmdPush:
data = fxRunPush( run, (fxRunPush_t*)data);
break;
case fxCmdPop:
data = fxRunPop( run, (fxRunPop_t*)data);
break;
case fxCmdPushParent:
data = fxRunPushParent( run, (fxRunPushParent_t*)data);
break;
case fxCmdAdd:
data = fxRunAdd( run, (fxRunAdd_t*)data);
break;
case fxCmdSub:
data = fxRunSub( run, (fxRunSub_t*)data);
break;
case fxCmdAddScale:
data = fxRunAddScale( run, (fxRunAddScale_t*)data);
break;
case fxCmdSubScale:
data = fxRunSubScale( run, (fxRunSubScale_t*)data);
break;
case fxCmdRotateAround:
data = fxRunRotateAround( run, (fxRunRotateAround_t*)data);
break;
case fxCmdScale:
data = fxRunScale( run, (fxRunScale_t*)data);
break;
case fxCmdCopy:
data = fxRunCopy( run, (fxRunCopy_t*)data);
break;
case fxCmdRandom:
data = fxRunRandom( run, (fxRunRandom_t*)data);
break;
case fxCmdWobble:
data = fxRunWobble( run, (fxRunWobble_t*)data);
break;
case fxCmdNormalize:
data = fxRunNormalize( run, (fxRunNormalize_t*)data);
break;
case fxCmdPerpendicular:
data = fxRunPerpendicular( run, (fxRunPerpendicular_t*)data);
break;
case fxCmdInverse:
data = fxRunInverse( run, (fxRunInverse_t*)data);
break;
case fxCmdClear:
data = fxRunClear( run, (fxRunClear_t*)data);
break;
case fxCmdValue:
data = fxRunValue( run, (fxRunValue_t*)data );
break;
}
}
return data;
}
static fxEntity_t *fxRunEntities( fxEntity_t **nextEntity ) {
fxEntity_t *lastEntity;
fxRun_t run;
lastEntity = 0;
for (; nextEntity[0]; nextEntity = &nextEntity[0]->next ) {
float deltaTime;
float moveTime;
int movePasses;
const fxRunEmitter_t *emitter;
killSkip:
run.entity = nextEntity[0];
fx.last.entityCount++;
fx.last.entitySize += fxSize( run.entity );
deltaTime = (fx.time - run.entity->startTime) + fx.timeFraction;
run.lerp = deltaTime * run.entity->lifeScale;
if ( run.lerp >= 1.0f ) {
run.entity->flags |= FX_ENT_DEAD;
} else if ( deltaTime < 0 ) {
lastEntity = run.entity;
continue;
}
run.shaderTime = run.entity->startTime;
run.life = deltaTime * 0.001;
run.key = (unsigned int)run.entity;
emitter = run.entity->emitter;
run.stackUsed = 0;
if ( !(emitter->flags & FX_EMIT_MOVE ) ) {
VectorCopy( run.entity->origin, run.origin );
VectorCopy( run.entity->velocity, run.velocity );
goto runEntity;
} else if ( !(emitter->flags & FX_EMIT_TRACE ) ) {
VectorMA( run.entity->origin, run.life, run.entity->velocity, run.origin );
VectorCopy( run.entity->velocity, run.velocity );
run.velocity[2] -= 2 * run.life * emitter->gravity;
run.origin[2] -= run.life * run.life * emitter->gravity;
goto runEntity;
} else if ( run.entity->flags & FX_ENT_STATIONARY ) {
VectorCopy( run.entity->origin, run.origin );
if ( emitter->flags & FX_EMIT_SINK && run.lerp > emitter->sinkDelay ) {
run.origin[2] -= emitter->sinkDepth * ( run.lerp - emitter->sinkDelay );
}
VectorClear( run.velocity );
goto runEntity;
}
moveTime = run.life - run.entity->moveTime;
//Still within the valid trace range just move it directly
if( !moveTime || (moveTime < run.entity->traceTime )) {
traceOkay:
VectorMA( run.entity->origin, moveTime, run.entity->velocity, run.origin );
run.origin[2] -= moveTime * moveTime * emitter->gravity;
VectorCopy( run.entity->velocity, run.velocity );
run.velocity[2] -= 2 * moveTime * emitter->gravity;
goto runEntity;
}
//movepasses will go down fast incase stuff fails
for( movePasses = 2; movePasses >= 0; movePasses-- ) {
trace_t tr;
vec3_t oldOrigin, oldVelocity, newOrigin;
vec3_t traceMin, traceMax;
float addTime;
//Tracetime holds the time since the last update to the origin/velocity
//Calculate the previous trace end position
VectorMA( run.entity->origin, run.entity->traceTime, run.entity->velocity, oldOrigin );
oldOrigin[2] -= run.entity->traceTime * run.entity->traceTime * emitter->gravity;
VectorCopy( run.entity->velocity, oldVelocity );
oldVelocity[2] -= 2 * run.entity->traceTime * emitter->gravity;
//Predict how far we can go from this position if there's any time left in the particle's life anyway
addTime = 0.001 * movePasses * ( 30 + (rand() & 15 ) );
//If the entity is still moving upwards check when upward speed will hit 0 if that takes longer that 2 milliseconds
if ( oldVelocity[2] > 0 ) {
float left = oldVelocity[2] / ( 2 * emitter->gravity );
if ( left < addTime ) {
addTime = left;
}
traceMin[2] = 0;
traceMax[2] = addTime * addTime * emitter->gravity;
} else {
traceMin[2] = -addTime * addTime * emitter->gravity;
traceMax[2] = 0;
}
traceMin[0] = traceMin[1] = -0.1f;
traceMin[2] -= 0.1f;
traceMax[0] = traceMax[1] = 0.1f;
traceMax[2] += 0.1f;
//Check the end position of the trace without the last piece of gravity
VectorMA( oldOrigin, addTime, oldVelocity, newOrigin );
CM_BoxTrace( &tr, oldOrigin, newOrigin, traceMin, traceMax, 0, CONTENTS_SOLID, qfalse );
if ( tr.allsolid || tr.fraction == 0 )
break;
if ( tr.fraction < 1 ) {
addTime *= tr.fraction * 0.99;
}
run.entity->traceTime += addTime;
//Have we moved into safe territory for now
if ( run.entity->traceTime >= moveTime ) {
goto traceOkay;
}
//Did we only add a very small piece go into regular trace mode
// if ( addTime > 0.001f )
// break;
}
//Can do a few passes if the particle bounces between a few surfaces
for( movePasses = 2; movePasses >= 0; movePasses-- ) {
vec3_t oldOrigin;
trace_t tr;
VectorMA( run.entity->origin, run.entity->traceTime, run.entity->velocity, oldOrigin );
oldOrigin[2] -= run.entity->traceTime * run.entity->traceTime * emitter->gravity;
VectorCopy( run.entity->origin, run.origin );
VectorMA( run.entity->origin, moveTime, run.entity->velocity, run.origin );
run.origin[2] -= moveTime * moveTime * emitter->gravity;
VectorCopy( run.entity->velocity, run.velocity );
run.velocity[2] -= 2 * moveTime * emitter->gravity;
//Determine how far we can still go before we hit something
CM_BoxTrace( &tr, oldOrigin, run.origin, vec3_origin, vec3_origin, 0, CONTENTS_SOLID, qtrue );
if ( tr.allsolid || (tr.surfaceFlags & SURF_NOIMPACT) ) {
/* Special case just don't do anything */
goto killEntity;
//Full trace succeeded
} else if ( tr.fraction == 1 ) {
goto runEntity;
} else {
if ( emitter->flags & FX_EMIT_IMPACT ) {
run.entity->flags |= FX_ENT_DEAD;
goto runEntity;
}
if ( emitter->impactRun && (VectorLengthSquared( run.velocity ) > emitter->impactSpeed ) ) {
/* Run the optional code for the impact handler */
VectorCopy( tr.endpos, run.origin );
VectorCopy( tr.plane.normal, run.dir );
fxRun( &run, ((char *)emitter) + emitter->impactRun );
}
if ( emitter->bounceFactor > 0 ) {
float bounceTime;
float reflect;
float fractionTime = ( moveTime - run.entity->traceTime ) * tr.fraction;
bounceTime = run.entity->traceTime + fractionTime;
if ( tr.fraction ) {
//Add back the velocity till the impact time
run.velocity[2] += 2 * fractionTime * emitter->gravity;
reflect = -2 * DotProduct( tr.plane.normal, run.velocity );
VectorMA( run.velocity, reflect, tr.plane.normal, run.velocity );
VectorScale( run.velocity, emitter->bounceFactor, run.velocity );
VectorCopy( run.velocity, run.entity->velocity );
}
VectorCopy( tr.endpos, run.entity->origin );
//Stop moving if it's on a vertical slope and speed is low */
if ( ( tr.plane.normal[2] > 0 && VectorLengthSquared( run.velocity ) < 40 ) ) {
run.entity->flags |= FX_ENT_STATIONARY;
VectorClear( run.entity->velocity );
goto runEntity;
}
//Move slightly back in time to prevent negative offsets
run.entity->moveTime += bounceTime;
moveTime = run.life - run.entity->moveTime;
run.entity->traceTime = 0;
continue;
}
}
}
runEntity:
fxMaskOutput( emitter->mask, run.entity + 1, run.velocity + 3 );
if ( run.entity->flags & FX_ENT_DEAD ) {
if ( emitter->deathRun )
fxRun( &run, ((char *)emitter) + emitter->deathRun );
goto killEntity;
} else {
fxRun( &run, ((char *)emitter) + emitter->emitRun );
}
lastEntity = run.entity;
continue;
killEntity:
nextEntity[0] = run.entity->next;
fxFree( run.entity );
if ( !nextEntity[0] )
break;
goto killSkip;
}
return lastEntity;
}
static void *fxGetActive( unsigned int key1, unsigned int key2, int size ) {
fxActive_t *active;
unsigned int index;
index = ((key1 ^ key2) >> FX_ACTIVE_HASH_SHIFT) ^ key1 ^ key2;
index &= FX_ACTIVE_HASH_MASK;
active = fx.activeHash[ index ];
while (1) {
if (!active) {
active = fxAlloc( sizeof( fxActive_t ) + size );
if (!active)
return 0;
active->next = fx.activeHash[ index ];
fx.activeHash[ index ] = active;
active->key1 = key1;
active->key2 = key2;
memset( active + 1, 0, size );
fx.last.activeCount++;
fx.last.activeSize += fxSize( active );
break;
} else if (active->key1 == key1 && active->key2 == key2) {
fx.last.activeCount++;
fx.last.activeSize += fxSize( active );
break;
}
active = active->next;
}
active->beenUsed = qtrue;
return active + 1;
}
static void fxDebugDrawString( int x, int y, const char *line ) {
int i;
for (i = 0; i<40;i++) {
if (!line[i])
break;
SCR_DrawSmallChar( x, y, line[i] );
x += SMALLCHAR_WIDTH;
}
}
static void fxDebugDraw( void ) {
char buf[512];
int x = cls.glconfig.vidWidth * 3 / 4;
int y = cls.glconfig.vidHeight - SMALLCHAR_HEIGHT * 10 * 1.5;
re.SetColor( colorWhite );
Com_sprintf( buf, sizeof( buf ), "Entities %d, mem %dkb", fx.last.entityCount, fx.last.entitySize / -1024 );
fxDebugDrawString( x, y, buf );
y += SMALLCHAR_HEIGHT * 1.5;
Com_sprintf( buf, sizeof( buf ), "Active %d, mem %dkb", fx.last.activeCount, fx.last.activeSize / -1024 );
fxDebugDrawString( x, y, buf );
y += SMALLCHAR_HEIGHT * 1.5;
re.SetColor( NULL );
}
void FX_Debug( void ) {
if ( fx_Debug->integer )
fxDebugDraw();
}
void FX_Reset( void ) {
int *lastAlloc;
memset( fx.activeHash, 0, sizeof( fx.activeHash ));
fx.entityActive = 0;
fx.entityNew = 0;
fx.vibrate.time = 0;
fx.vibrate.used = 0;
/* Create the terminator block */
fx.allocStart[0] = fx.allocSize - sizeof( int );
fx.allocSearch = fx.allocStart;
lastAlloc = (int *)(((char *)fx.allocStart) + fx.allocSize - sizeof( int ));
lastAlloc[0] = 0;
}
void FX_Begin( int time, float timeFraction ) {
fx.deltaTime = fx.time - fx.oldTime;
fx.oldTime = fx.time;
fx.time = time;
fx.timeFraction = timeFraction;
if ( fx.deltaTime < 0 || fx.deltaTime > 50) {
fx.oldTime = fx.time;
fx.deltaTime = 0;
}
fx.seed = time;
/* Clear some stats */
fx.last.activeCount = 0;
fx.last.entityCount = 0;
fx.last.entitySize = 0;
fx.last.activeSize = 0;
/* Run the currently active entities */
fxRunEntities( &fx.entityActive );
}
void FX_End( void ) {
int i;
int loops = 5;
/* Run all the new entities and loop a few times for recursive entities */
while ( loops>0 && fx.entityNew ) {
fxEntity_t *newEntities, *lastEntity;
newEntities = fx.entityNew;
fx.entityNew = 0;
lastEntity = fxRunEntities( &newEntities );
if ( lastEntity ) {
lastEntity->next = fx.entityActive;
fx.entityActive = newEntities;
}
loops --;
}
/* Go through the active list and set all to false */
for( i = 0; i < FX_ACTIVE_HASH_SIZE; i++) {
fxActive_t **activeList = &fx.activeHash[i];
while ( activeList[0] ) {
fxActive_t *active = activeList[0];
if ( active->beenUsed ) {
active->beenUsed = qfalse;
if (!active->next)
break;
activeList = &active->next;
continue;
}
activeList[0] = active->next;
fxFree( active );
}
}
}
void FX_Run( fxHandle_t handle, const fxParent_t *parent, unsigned int key ) {
fxRun_t run;
const fxScript_t *script;
if (handle <= 0 || handle > fx.scriptCount || !parent )
return;
script = fx.scripts[handle];
//Check for illegal readmasks for undefined values
if ( fx_Debug->integer ) {
int debug = fx_Debug->integer;
char warning[256];
warning[0] = 0;
if ( (script->readMask & FX_MASK_ORIGIN) && !(parent->flags & FXP_ORIGIN ) ) {
Q_strcat( warning, sizeof( warning ), "origin, " );
}
if ( (script->readMask & FX_MASK_VELOCITY) && !(parent->flags & FXP_VELOCITY ) ) {
Q_strcat( warning, sizeof( warning ), "velocity, " );
}
if ( (script->readMask & FX_MASK_DIR) && !(parent->flags & FXP_DIR ) ) {
Q_strcat( warning, sizeof( warning ), "dir, " );
}
if ( (script->readMask & FX_MASK_ANGLES) && !(parent->flags & FXP_ANGLES ) ) {
Q_strcat( warning, sizeof( warning ), "angles, " );
}
if ( debug > 1 && (script->readMask & FX_MASK_SIZE) && !(parent->flags & FXP_SIZE ) ) {
Q_strcat( warning, sizeof( warning ), "size, " );
}
if ( (script->readMask & FX_MASK_WIDTH) && !(parent->flags & FXP_WIDTH ) ) {
Q_strcat( warning, sizeof( warning ), "width, " );
}
if ( debug > 1 && (script->readMask & FX_MASK_SHADER) && !(parent->flags & FXP_SHADER ) ) {
Q_strcat( warning, sizeof( warning ), "shader, " );
}
if ( (script->readMask & FX_MASK_MODEL) && !(parent->flags & FXP_MODEL ) ) {
Q_strcat( warning, sizeof( warning ), "model, " );
}
if ( (script->readMask & FX_MASK_AXIS) && !(parent->flags & FXP_AXIS ) ) {
Q_strcat( warning, sizeof( warning ), "axis, " );
}
if ( warning[0] ) {
Com_Printf("FX_Debug:script '%s' requires %s which aren't supplied by caller\n", script->entry->name, warning );
}
}
VectorCopy( parent->origin, run.origin );
if ( parent->flags & FXP_VELOCITY)
VectorCopy( parent->velocity, run.velocity );
else
VectorClear( run.velocity );
if ( parent->flags & FXP_DIR )
VectorCopy( parent->dir, run.dir );
if ( parent->flags & FXP_ANGLES ) {
VectorCopy( parent->angles, run.angles );
run.rotate = run.angles[ROLL];
} else {
VectorClear( run.angles );
run.rotate = 0;
}
if ( parent->flags & FXP_AXIS )
AxisCopy( parent->axis, run.axis );
*(int *)run.color = ( parent->flags & FXP_COLOR ) ? *(int *)parent->color : 0xffffffff;
run.shader = ( parent->flags & FXP_SHADER ) ? parent->shader : 0;
run.model = ( parent->flags & FXP_MODEL ) ? parent->model : 0;
run.size = ( parent->flags & FXP_SIZE ) ? parent->size : 1.0f;
run.width = ( parent->flags & FXP_WIDTH ) ? parent->width : 1.0f;
run.parent = parent;
run.shaderTime = 0;
run.key = key;
run.stackUsed = 0;
run.entity = 0;
fxRun( &run, script->data );
}
static void fxList( void ) {
const fxScript_t *script;
int i;
if ( fx.scriptCount <= 1) {
Com_Printf("No fx scripts loaded\n");
return;
}
Com_Printf ( "-----------------------\n" );
for ( i = 1; i<fx.scriptCount;i++ ) {
script = fx.scripts[i];
Com_Printf( "%s", script->entry->name );
if ( script->remap ) {
Com_Printf( " remap to %s", script->remap->entry->name );
}
Com_Printf( "\n");
}
Com_Printf( "%d Total scripts\n", fx.scriptCount - 1);
Com_Printf ( "-----------------------\n" );
}
static void fxStats( void ) {
int *alloc = fx.allocStart;
int allocSize, allocBlocks;
int freeSize, freeBlocks;
allocSize = allocBlocks = 0;
freeSize = freeBlocks = 0;
while ( 1 ) {
int size = alloc[0];
if ( size < 0 ) {
size = -size;
allocSize += size;
allocBlocks++;
} else if ( size > 0 ) {
freeSize += size;
freeBlocks++;
} else
break;
alloc = (int*)(((char *)alloc) + size);
}
Com_Printf( "FX Stats:\n" );
Com_Printf( "Free %d in %d blocks\n", freeSize, freeBlocks );
Com_Printf( "Allocated %d in %d blocks\n", allocSize, allocBlocks );
}
void FX_Init( void ) {
Com_Memset( &fx, 0, sizeof( fx ));
fx_Megs = Cvar_Get( "fx_Megs", "2", CVAR_ARCHIVE | CVAR_LATCH );
fx_Vibrate = Cvar_Get( "fx_Vibrate", "1", CVAR_ARCHIVE );
fx_Override = Cvar_Get( "fx_Override", "", CVAR_ARCHIVE );
fx_Debug = Cvar_Get( "fx_Debug", "0", CVAR_CHEAT );
fx.allocSize = fx_Megs->integer;
if ( fx.allocSize < 1)
fx.allocSize = 1;
else if (fx.allocSize > 8) {
fx.allocSize = 8;
}
fx.allocSize *= 1024*1024;
fx.allocStart = Hunk_Alloc( fx.allocSize, h_low );
FX_Reset( );
Cmd_AddCommand( "fxStats", fxStats );
Cmd_AddCommand( "fxList", fxList );
Cmd_AddCommand( "fxReset", FX_Reset );
fxInitParser();
}
void FX_Shutdown( void ) {
Cmd_RemoveCommand( "fxStats" );
Cmd_RemoveCommand( "fxReset" );
Cmd_RemoveCommand( "fxRemap" );
Cmd_RemoveCommand( "fxMath" );
Cmd_RemoveCommand( "fxReload" );
if ( fx.alloc.entries )
fxFreeMemory();
}