2016-07-12 00:40:13 +00:00
/*
Copyright ( C ) 1996 - 1997 Id Software , Inc .
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation ; either version 2
of the License , or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
See the included ( GNU . txt ) GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
# include "quakedef.h"
# ifdef PSET_CLASSIC
# include "glquake.h"
# include "shader.h"
# include "renderque.h"
# define POLYS
# ifdef FTE_TARGET_WEB
# define rand myrand //emscripten's libc is doing a terrible job of this.
static int rand ( void )
{ //ripped from glibc
static int state = 0xdeadbeef ;
int val = ( ( state * 1103515245 ) + 12345 ) & 0x7fffffff ;
state = val ;
return val ;
}
# endif
typedef enum {
DODGY ,
ROCKET_TRAIL ,
ALT_ROCKET_TRAIL ,
BLOOD_TRAIL ,
GRENADE_TRAIL ,
BIG_BLOOD_TRAIL ,
TRACER1_TRAIL ,
TRACER2_TRAIL ,
VOOR_TRAIL ,
BRIGHTFIELD_POINT ,
BLOBEXPLOSION_POINT ,
LAVASPLASH_POINT ,
EXPLOSION_POINT ,
EXPLOSION2_POINT ,
TELEPORTSPLASH_POINT ,
MUZZLEFLASH_POINT ,
2018-01-22 19:18:04 +00:00
QWGUNSHOT_POINT , //not actually the same as nq, to deal with higher counts better
QWSTDBLOOD_POINT , //same
QWLGBLOOD_POINT , //same
2016-07-12 00:40:13 +00:00
EFFECTTYPE_MAX
} effect_type_t ;
typedef struct cparticle_s
{
avec3_t org ;
float die ;
avec3_t vel ;
float ramp ;
enum
{
pt_static ,
pt_fire ,
pt_explode ,
pt_explode2 ,
pt_blob ,
pt_blob2 ,
pt_grav ,
pt_slowgrav ,
pt_oneframe
} type ;
unsigned int rgb ;
struct cparticle_s * next ;
} cparticle_t ;
# define DEFAULT_NUM_PARTICLES 2048
# define ABSOLUTE_MIN_PARTICLES 512
# define ABSOLUTE_MAX_PARTICLES 8192
static int r_numparticles ;
static cparticle_t * particles , * active_particles , * free_particles ;
extern cvar_t r_part_density , r_part_classic_expgrav , r_part_classic_opaque ;
static unsigned int particleframe ;
static int ramp1 [ 8 ] = { 0x6f , 0x6d , 0x6b , 0x69 , 0x67 , 0x65 , 0x63 , 0x61 } ;
static int ramp2 [ 8 ] = { 0x6f , 0x6e , 0x6d , 0x6c , 0x6b , 0x6a , 0x68 , 0x66 } ;
static int ramp3 [ 8 ] = { 0x6d , 0x6b , 6 , 5 , 4 , 3 } ;
# ifndef POLYS
# define BUFFERVERTS 2048*3
static vecV_t classicverts [ BUFFERVERTS ] ;
static union c
{
byte_vec4_t b ;
unsigned int i ;
} classiccolours [ BUFFERVERTS ] ;
static vec2_t classictexcoords [ BUFFERVERTS ] ;
static index_t classicindexes [ BUFFERVERTS ] ;
static mesh_t classicmesh ;
# endif
static shader_t * classicshader ;
//obtains an index for the name, even if it is unknown (one can be loaded after. will only fail if the effect limit is reached)
//technically this function is not meant to fail often, but thats fine so long as the other functions are meant to safely reject invalid effect numbers.
static int PClassic_FindParticleType ( const char * name )
{
if ( ! stricmp ( " tr_rocket " , name ) )
return ROCKET_TRAIL ;
if ( ! stricmp ( " tr_altrocket " , name ) )
return ALT_ROCKET_TRAIL ;
if ( ! stricmp ( " tr_slightblood " , name ) )
return BLOOD_TRAIL ;
if ( ! stricmp ( " tr_grenade " , name ) )
return GRENADE_TRAIL ;
if ( ! stricmp ( " tr_blood " , name ) )
return BIG_BLOOD_TRAIL ;
if ( ! stricmp ( " tr_wizspike " , name ) )
return TRACER1_TRAIL ;
if ( ! stricmp ( " tr_knightspike " , name ) )
return TRACER2_TRAIL ;
if ( ! stricmp ( " tr_vorespike " , name ) )
return VOOR_TRAIL ;
if ( ! stricmp ( " te_tarexplosion " , name ) )
return BLOBEXPLOSION_POINT ;
if ( ! stricmp ( " te_lavasplash " , name ) )
return LAVASPLASH_POINT ;
if ( ! stricmp ( " te_explosion " , name ) )
return EXPLOSION_POINT ;
if ( ! strnicmp ( " te_explosion2_ " , name , 14 ) )
{
char * e ;
int start = strtoul ( name + 14 , & e , 10 ) ;
int len = strtoul ( ( * e = = ' _ ' ) ? e + 1 : e , & e , 10 ) ;
if ( ! * e & & start > = 0 & & start < = 255 & & len > = 0 & & len < = 255 )
return EXPLOSION2_POINT | ( start < < 8 ) | ( len < < 16 ) ;
}
if ( ! stricmp ( " te_teleport " , name ) )
return TELEPORTSPLASH_POINT ;
if ( ! stricmp ( " te_muzzleflash " , name ) )
return MUZZLEFLASH_POINT ;
if ( ! stricmp ( " ef_brightfield " , name ) )
return BRIGHTFIELD_POINT ;
2018-01-22 19:18:04 +00:00
if ( ! stricmp ( " te_qwgunshot " , name ) )
return QWGUNSHOT_POINT ;
if ( ! stricmp ( " te_qwblood " , name ) )
return QWSTDBLOOD_POINT ;
if ( ! stricmp ( " te_lightningblood " , name ) )
return QWLGBLOOD_POINT ;
2016-07-12 00:40:13 +00:00
return P_INVALID ;
}
static qboolean PClassic_Query ( int type , int body , char * outstr , int outstrlen )
{
char * n = NULL ;
switch ( type & 0xff )
{
case ROCKET_TRAIL :
n = " tr_rocket " ;
break ;
case ALT_ROCKET_TRAIL :
n = " tr_altrocket " ;
break ;
case BLOOD_TRAIL :
n = " tr_slightblood " ;
break ;
case GRENADE_TRAIL :
n = " tr_grenade " ;
break ;
case BIG_BLOOD_TRAIL :
n = " tr_blood " ;
break ;
case TRACER1_TRAIL :
n = " tr_wizspike " ;
break ;
case TRACER2_TRAIL :
n = " tr_knightspike " ;
break ;
case VOOR_TRAIL :
n = " tr_vorespike " ;
break ;
case BLOBEXPLOSION_POINT :
n = " te_tarexplosion " ;
break ;
case LAVASPLASH_POINT :
n = " te_lavasplash " ;
break ;
case EXPLOSION_POINT :
n = " te_explosion " ;
break ;
case EXPLOSION2_POINT :
n = va ( " te_explosion2_%i_%i " , ( type > > 8 ) & 0xff , ( type > > 16 ) & 0xff ) ;
break ;
case TELEPORTSPLASH_POINT :
n = " te_teleport " ;
break ;
case BRIGHTFIELD_POINT :
n = " ef_brightfield " ;
break ;
}
if ( ! n )
return false ;
if ( body = = 0 )
{
Q_strncpyz ( outstr , n , outstrlen ) ;
return true ;
}
return false ;
}
//a convienience function.
static int PClassic_RunParticleEffectTypeString ( vec3_t org , vec3_t dir , float count , char * name )
{
int efnum = P_FindParticleType ( name ) ;
return P_RunParticleEffectState ( org , dir , count , efnum , NULL ) ;
}
//DP extension: add particles within a box that look like rain or snow.
static void PClassic_RunParticleWeather ( vec3_t minb , vec3_t maxb , vec3_t dir , float count , int colour , char * efname )
{
}
//DP extension: add particles within a box.
static void PClassic_RunParticleCube ( int ptype , vec3_t minb , vec3_t maxb , vec3_t dir_min , vec3_t dir_max , float count , int colour , qboolean gravity , float jitter )
{
}
//hexen2 support: add particles flying out from a point with a randomized speed
static void PClassic_RunParticleEffect2 ( vec3_t org , vec3_t dmin , vec3_t dmax , int color , int effect , int count )
{
}
//hexen2 support: add particles within a box.
static void PClassic_RunParticleEffect3 ( vec3_t org , vec3_t box , int color , int effect , int count )
{
}
//hexen2 support: add particles around the spot in a radius. no idea what the 'effect' field is.
static void PClassic_RunParticleEffect4 ( vec3_t org , float radius , int color , int effect , int count )
{
}
//this function is used as a fallback in case a trail effect is unknown.
2019-01-29 07:18:07 +00:00
static void PClassic_ParticleTrailIndex ( vec3_t start , vec3_t end , int type , float timestep , int color , int crnd , trailstate_t * * tsk )
2016-07-12 00:40:13 +00:00
{
}
//the one-time initialisation function, called no mater which renderer is active.
static qboolean PClassic_InitParticles ( void )
{
int i ;
if ( ( i = COM_CheckParm ( " -particles " ) ) & & i + 1 < com_argc )
{
r_numparticles = ( int ) ( Q_atoi ( com_argv [ i + 1 ] ) ) ;
r_numparticles = bound ( ABSOLUTE_MIN_PARTICLES , r_numparticles , ABSOLUTE_MAX_PARTICLES ) ;
}
else
{
r_numparticles = DEFAULT_NUM_PARTICLES ;
}
particles = ( cparticle_t * ) BZ_Malloc ( r_numparticles * sizeof ( cparticle_t ) ) ;
# ifndef POLYS
for ( i = 0 ; i < BUFFERVERTS ; i + = 3 )
{
classictexcoords [ i + 1 ] [ 0 ] = 1 ;
classictexcoords [ i + 2 ] [ 1 ] = 1 ;
classicindexes [ i + 0 ] = i + 0 ;
classicindexes [ i + 1 ] = i + 1 ;
classicindexes [ i + 2 ] = i + 2 ;
}
classicmesh . xyz_array = classicverts ;
classicmesh . st_array = classictexcoords ;
classicmesh . colors4b_array = ( byte_vec4_t * ) classiccolours ;
classicmesh . indexes = classicindexes ;
# endif
classicshader = R_RegisterShader ( " particles_classic " , SUF_NONE ,
" { \n "
" program defaultsprite \n "
" nomipmaps \n "
" surfaceparm nodlight \n "
" { \n "
" if r_part_classic_square \n "
2018-02-11 06:57:54 +00:00
" clampmap classicparticle_square \n "
2016-07-12 00:40:13 +00:00
" else \n "
2018-02-11 06:57:54 +00:00
" clampmap classicparticle \n "
2016-07-12 00:40:13 +00:00
" endif \n "
" rgbgen vertex \n "
" alphagen vertex \n "
" blendfunc blend \n "
" } \n "
" } \n "
) ;
return true ;
}
static void PClassic_ShutdownParticles ( void )
{
BZ_Free ( particles ) ;
particles = NULL ;
}
// a classic trailstate is really just a float stored in a pointer variable...
// assuming float alignment/size is more strict than pointer
static float Classic_GetLeftover ( trailstate_t * * tsk )
{
float * f = ( float * ) tsk ;
if ( ! f )
return 0 ;
return * f ;
}
static void Classic_SetLeftover ( trailstate_t * * tsk , float leftover )
{
float * f = ( float * ) tsk ;
if ( f )
* f = leftover ;
}
//called when an entity is removed from the world, taking its trailstate with it.
static void PClassic_DelinkTrailstate ( trailstate_t * * tsk )
{
* tsk = NULL ;
}
//wipes all the particles ready for the next map.
static void PClassic_ClearParticles ( void )
{
int i ;
free_particles = & particles [ 0 ] ;
active_particles = NULL ;
for ( i = 0 ; i < r_numparticles ; i + + )
particles [ i ] . next = & particles [ i + 1 ] ;
particles [ r_numparticles - 1 ] . next = NULL ;
}
//some particles (brightfield) must last only one frame
static void PClassic_ClearPerFrame ( void )
{
if ( particleframe ! = - 1 & & particleframe ! = cl_framecount )
{
cparticle_t * * link , * kill ;
for ( link = & active_particles ; * link ; )
{
if ( ( * link ) - > type = = pt_oneframe )
{
kill = * link ;
* link = kill - > next ;
kill - > next = free_particles ;
free_particles = kill ;
}
else
link = & ( * link ) - > next ;
}
}
}
//draws all the active particles.
static void PClassic_DrawParticles ( void )
{
cparticle_t * p , * kill ;
int i ;
float time2 , time3 , time1 , dvel , frametime , grav ;
vec3_t up , right ;
float dist , scale , r_partscale = 0 ;
# ifdef POLYS
scenetris_t * scenetri ;
# else
union c usecolours ;
# endif
static float oldtime ;
RSpeedMark ( ) ;
if ( ! active_particles )
{
oldtime = cl . time ;
return ;
}
if ( particleframe ! = - 1 & & particleframe ! = cl_framecount )
{
PClassic_ClearPerFrame ( ) ;
particleframe = - 1 ;
}
if ( r_refdef . useperspective )
r_partscale = 0.004 * tan ( r_refdef . fov_x * ( M_PI / 180 ) * 0.5f ) ;
else
r_partscale = 0 ;
VectorScale ( vup , 1.5 , up ) ;
VectorScale ( vright , 1.5 , right ) ;
frametime = cl . time - oldtime ;
oldtime = cl . time ;
frametime = bound ( 0 , frametime , 1 ) ;
if ( cl . paused | | r_secondaryview | | r_refdef . recurse )
frametime = 0 ;
time3 = frametime * 15 ;
time2 = frametime * 10 ; // 15;
time1 = frametime * 5 ;
grav = frametime * 800 * 0.05 ;
dvel = 4 * frametime ;
# ifdef POLYS
// if (cl_numstris && cl_stris[cl_numstris-1].shader == classicshader && cl_stris[cl_numstris-1].numvert + 8 <= MAX_INDICIES)
// scenetri = &cl_stris[cl_numstris-1];
// else
{
if ( cl_numstris = = cl_maxstris )
{
cl_maxstris + = 8 ;
cl_stris = BZ_Realloc ( cl_stris , sizeof ( * cl_stris ) * cl_maxstris ) ;
}
scenetri = & cl_stris [ cl_numstris + + ] ;
scenetri - > shader = classicshader ;
scenetri - > flags = BEF_NODLIGHT | BEF_NOSHADOWS ;
scenetri - > firstidx = cl_numstrisidx ;
scenetri - > firstvert = cl_numstrisvert ;
scenetri - > numvert = 0 ;
scenetri - > numidx = 0 ;
}
# endif
while ( 1 )
{
kill = active_particles ;
if ( kill & & kill - > die < cl . time )
{
active_particles = kill - > next ;
kill - > next = free_particles ;
free_particles = kill ;
continue ;
}
break ;
}
for ( p = active_particles ; p ; p = p - > next )
{
while ( 1 )
{
kill = p - > next ;
if ( kill & & kill - > die < cl . time )
{
p - > next = kill - > next ;
kill - > next = free_particles ;
free_particles = kill ;
continue ;
}
break ;
}
// hack a scale up to keep particles from disapearing
dist = ( p - > org [ 0 ] - r_origin [ 0 ] ) * vpn [ 0 ] + ( p - > org [ 1 ] - r_origin [ 1 ] ) * vpn [ 1 ] + ( p - > org [ 2 ] - r_origin [ 2 ] ) * vpn [ 2 ] ;
scale = 1 + dist * r_partscale ;
# ifdef POLYS
if ( cl_numstrisvert + 3 > cl_maxstrisvert )
{
cl_maxstrisvert + = 1024 * 3 ;
cl_strisvertv = BZ_Realloc ( cl_strisvertv , sizeof ( * cl_strisvertv ) * cl_maxstrisvert ) ;
cl_strisvertt = BZ_Realloc ( cl_strisvertt , sizeof ( * cl_strisvertt ) * cl_maxstrisvert ) ;
cl_strisvertc = BZ_Realloc ( cl_strisvertc , sizeof ( * cl_strisvertc ) * cl_maxstrisvert ) ;
}
// Vector4Set(cl_strisvertc[cl_numstrisvert+0],1,1,1,1);
// Vector4Set(cl_strisvertc[cl_numstrisvert+1],1,1,1,1);
// Vector4Set(cl_strisvertc[cl_numstrisvert+2],1,1,1,1);
2018-01-03 04:40:06 +00:00
Vector4Set ( cl_strisvertc [ cl_numstrisvert + 0 ] , ( ( p - > rgb & 0xff ) > > 0 ) / 255.0 , ( ( p - > rgb & 0xff00 ) > > 8 ) / 255.0 , ( ( p - > rgb & 0xff0000 ) > > 16 ) / 255.0 , ( ( p - > type = = pt_fire & & ! r_part_classic_opaque . ival ) ? ( ( 6 - p - > ramp ) * 0.166666 ) : 1.0 ) ) ;
2016-07-12 00:40:13 +00:00
Vector4Copy ( cl_strisvertc [ cl_numstrisvert + 0 ] , cl_strisvertc [ cl_numstrisvert + 1 ] ) ;
Vector4Copy ( cl_strisvertc [ cl_numstrisvert + 0 ] , cl_strisvertc [ cl_numstrisvert + 2 ] ) ;
Vector2Set ( cl_strisvertt [ cl_numstrisvert + 0 ] , 0 , 0 ) ;
Vector2Set ( cl_strisvertt [ cl_numstrisvert + 1 ] , 1 , 0 ) ;
Vector2Set ( cl_strisvertt [ cl_numstrisvert + 2 ] , 0 , 1 ) ;
VectorCopy ( p - > org , cl_strisvertv [ cl_numstrisvert + 0 ] ) ;
VectorMA ( p - > org , scale , up , cl_strisvertv [ cl_numstrisvert + 1 ] ) ;
VectorMA ( p - > org , scale , right , cl_strisvertv [ cl_numstrisvert + 2 ] ) ;
if ( cl_numstrisidx + 3 > cl_maxstrisidx )
{
cl_maxstrisidx + = 1024 * 3 ;
cl_strisidx = BZ_Realloc ( cl_strisidx , sizeof ( * cl_strisidx ) * cl_maxstrisidx ) ;
}
cl_strisidx [ cl_numstrisidx + + ] = ( cl_numstrisvert - scenetri - > firstvert ) + 0 ;
cl_strisidx [ cl_numstrisidx + + ] = ( cl_numstrisvert - scenetri - > firstvert ) + 1 ;
cl_strisidx [ cl_numstrisidx + + ] = ( cl_numstrisvert - scenetri - > firstvert ) + 2 ;
cl_numstrisvert + = 3 ;
scenetri - > numvert + = 3 ;
scenetri - > numidx + = 3 ;
# else
if ( classicmesh . numvertexes > = BUFFERVERTS - 3 )
{
classicmesh . numindexes = classicmesh . numvertexes ;
BE_DrawMesh_Single ( classicshader , & classicmesh , NULL , & classicshader - > defaulttextures , 0 ) ;
classicmesh . numvertexes = 0 ;
}
usecolours . i = p - > rgb ;
if ( p - > type = = pt_fire )
usecolours . b [ 3 ] = 255 * ( 6 - p - > ramp ) / 6 ;
else
usecolours . b [ 3 ] = 255 ;
classiccolours [ classicmesh . numvertexes ] . i = usecolours . i ;
VectorCopy ( p - > org , classicverts [ classicmesh . numvertexes ] ) ;
classicmesh . numvertexes + + ;
classiccolours [ classicmesh . numvertexes ] . i = usecolours . i ;
VectorMA ( p - > org , scale , up , classicverts [ classicmesh . numvertexes ] ) ;
classicmesh . numvertexes + + ;
classiccolours [ classicmesh . numvertexes ] . i = usecolours . i ;
VectorMA ( p - > org , scale , right , classicverts [ classicmesh . numvertexes ] ) ;
classicmesh . numvertexes + + ;
# endif
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_oneframe :
case pt_static :
break ;
case pt_fire :
p - > ramp + = time1 ;
if ( p - > ramp > = 6 )
p - > die = - 1 ;
else
2018-03-04 14:41:16 +00:00
p - > rgb = d_quaketo24srgbtable [ ramp3 [ ( int ) p - > ramp ] ] ;
2016-07-12 00:40:13 +00:00
p - > vel [ 2 ] + = grav ;
break ;
case pt_explode :
p - > ramp + = time2 ;
if ( p - > ramp > = 8 )
p - > die = - 1 ;
else
2018-03-04 14:41:16 +00:00
p - > rgb = d_quaketo24srgbtable [ ramp1 [ ( int ) p - > ramp ] ] ;
2016-07-12 00:40:13 +00:00
for ( i = 0 ; i < 3 ; i + + )
p - > vel [ i ] + = p - > vel [ i ] * dvel ;
p - > vel [ 2 ] - = grav * r_part_classic_expgrav . value ;
break ;
case pt_explode2 :
p - > ramp + = time3 ;
if ( p - > ramp > = 8 )
p - > die = - 1 ;
else
2018-03-04 14:41:16 +00:00
p - > rgb = d_quaketo24srgbtable [ ramp2 [ ( int ) p - > ramp ] ] ;
2016-07-12 00:40:13 +00:00
for ( i = 0 ; i < 3 ; i + + )
p - > vel [ i ] - = p - > vel [ i ] * frametime ;
p - > vel [ 2 ] - = grav * r_part_classic_expgrav . value ;
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_slowgrav :
case pt_grav :
p - > vel [ 2 ] - = grav ;
break ;
}
}
# ifndef POLYS
if ( classicmesh . numvertexes )
{
classicmesh . numindexes = classicmesh . numvertexes ;
BE_DrawMesh_Single ( classicshader , & classicmesh , NULL , & classicshader - > defaulttextures , 0 ) ;
classicmesh . numvertexes = 0 ;
}
# endif
RSpeedEnd ( RSPEED_PARTICLESDRAW ) ;
}
static void Classic_ParticleExplosion ( vec3_t org )
{
int i , j ;
cparticle_t * p ;
int count ;
count = 1024 * r_part_density . value ;
for ( i = 0 ; i < count ; i + + )
{
if ( ! free_particles )
return ;
p = free_particles ;
free_particles = p - > next ;
p - > next = active_particles ;
active_particles = p ;
p - > die = cl . time + 5 ;
2018-03-04 14:41:16 +00:00
p - > rgb = d_8to24srgbtable [ ramp1 [ 0 ] ] ;
2016-07-12 00:40:13 +00:00
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 ;
}
}
}
}
static void Classic_ParticleExplosion2 ( vec3_t org , int colorStart , int colorLength )
{
int i , j ;
cparticle_t * p ;
int colorMod = 0 ;
for ( i = 0 ; i < 512 ; i + + )
{
if ( ! free_particles )
return ;
p = free_particles ;
free_particles = p - > next ;
p - > next = active_particles ;
active_particles = p ;
p - > die = cl . time + 0.3 ;
2018-03-04 14:41:16 +00:00
p - > rgb = d_8to24srgbtable [ ( colorStart + ( colorMod % colorLength ) ) & 255 ] ;
2016-07-12 00:40:13 +00:00
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 ;
}
}
}
static void Classic_BlobExplosion ( vec3_t org )
{
int i , j ;
cparticle_t * p ;
int count ;
count = 1024 * r_part_density . value ;
for ( i = 0 ; i < count ; i + + )
{
if ( ! free_particles )
return ;
p = free_particles ;
free_particles = p - > next ;
p - > next = active_particles ;
active_particles = p ;
p - > die = cl . time + 1 + ( rand ( ) & 8 ) * 0.05 ;
if ( i & 1 )
{
p - > type = pt_blob ;
2018-03-04 14:41:16 +00:00
p - > rgb = d_8to24srgbtable [ 66 + rand ( ) % 6 ] ;
2016-07-12 00:40:13 +00:00
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 ;
2018-03-04 14:41:16 +00:00
p - > rgb = d_8to24srgbtable [ 150 + rand ( ) % 6 ] ;
2016-07-12 00:40:13 +00:00
for ( j = 0 ; j < 3 ; j + + )
{
p - > org [ j ] = org [ j ] + ( ( rand ( ) % 32 ) - 16 ) ;
p - > vel [ j ] = ( rand ( ) % 512 ) - 256 ;
}
}
}
}
2018-01-22 19:18:04 +00:00
static void Classic_RunParticleEffect ( vec3_t org , vec3_t dir , int color , int count , qboolean qwstyle )
2016-07-12 00:40:13 +00:00
{
int i , j , scale ;
cparticle_t * p ;
if ( ! dir )
dir = vec3_origin ;
2018-01-22 19:18:04 +00:00
if ( qwstyle )
scale = ( count > 130 ) ? 3 : ( count > 20 ) ? 2 : 1 ; //QW
else
scale = 1 ; //NQ
2016-07-12 00:40:13 +00:00
2018-01-05 01:45:49 +00:00
count = ceil ( count * r_part_density . value ) ; //round-to-0 was resulting in blood being far too hard to see, especially when blood is often spawned with multiple points all rounded down
2016-07-12 00:40:13 +00:00
for ( i = 0 ; i < count ; i + + )
{
if ( ! free_particles )
return ;
p = free_particles ;
free_particles = p - > next ;
p - > next = active_particles ;
active_particles = p ;
p - > die = cl . time + 0.1 * ( rand ( ) % 5 ) ;
2018-03-04 14:41:16 +00:00
p - > rgb = d_8to24srgbtable [ ( color & ~ 7 ) + ( rand ( ) & 7 ) ] ;
2018-01-22 19:18:04 +00:00
if ( qwstyle )
p - > type = pt_grav ; //QW
else
p - > type = pt_slowgrav ; //NQ
2016-07-12 00:40:13 +00:00
for ( j = 0 ; j < 3 ; j + + )
{
p - > org [ j ] = org [ j ] + scale * ( ( rand ( ) & 15 ) - 8 ) ;
p - > vel [ j ] = dir [ j ] * 15 ;
}
}
}
static void Classic_LavaSplash ( vec3_t org )
{
int i , j , k ;
cparticle_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 + + )
{
if ( ! free_particles )
return ;
p = free_particles ;
free_particles = p - > next ;
p - > next = active_particles ;
active_particles = p ;
p - > die = cl . time + 2 + ( rand ( ) & 31 ) * 0.02 ;
2018-03-04 14:41:16 +00:00
p - > rgb = d_8to24srgbtable [ 224 + ( rand ( ) & 7 ) ] ;
2016-07-12 00:40:13 +00:00
p - > type = pt_grav ;
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 ) ;
VectorNormalizeFast ( dir ) ;
vel = 50 + ( rand ( ) & 63 ) ;
VectorScale ( dir , vel , p - > vel ) ;
}
}
}
}
static void Classic_TeleportSplash ( vec3_t org )
{
int i , j , k ;
cparticle_t * p ;
float vel ;
vec3_t dir ;
int st = 4 / r_part_density . value ;
if ( st = = 0 )
st = 1 ;
for ( i = - 16 ; i < 16 ; i + = st )
{
for ( j = - 16 ; j < 16 ; j + = st )
{
for ( k = - 24 ; k < 32 ; k + = st )
{
if ( ! free_particles )
return ;
p = free_particles ;
free_particles = p - > next ;
p - > next = active_particles ;
active_particles = p ;
p - > die = cl . time + 0.2 + ( rand ( ) & 7 ) * 0.02 ;
2018-03-04 14:41:16 +00:00
p - > rgb = d_8to24srgbtable [ 7 + ( rand ( ) & 7 ) ] ;
2016-07-12 00:40:13 +00:00
p - > type = pt_grav ;
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 ) ;
VectorNormalizeFast ( dir ) ;
vel = 50 + ( rand ( ) & 63 ) ;
VectorScale ( dir , vel , p - > vel ) ;
}
}
}
}
# define NUMVERTEXNORMALS 162
//vec3_t avelocity = {23, 7, 3};
//float partstep = 0.01;
//float timescale = 0.01;
static vec3_t avelocities [ NUMVERTEXNORMALS ] ;
static void Classic_BrightField ( vec3_t org )
{
extern float r_avertexnormals [ NUMVERTEXNORMALS ] [ 3 ] ;
float beamlength = 16 ;
int i ;
cparticle_t * p ;
float angle ;
float sp , sy , cp , cy ;
vec3_t forward ;
float dist ;
PClassic_ClearPerFrame ( ) ;
particleframe = cl_framecount ;
dist = 64 ;
if ( ! avelocities [ 0 ] [ 0 ] )
{
for ( i = 0 ; i < NUMVERTEXNORMALS ; i + + )
{
avelocities [ i ] [ 0 ] = ( rand ( ) & 255 ) * 0.01 ;
avelocities [ i ] [ 1 ] = ( rand ( ) & 255 ) * 0.01 ;
avelocities [ i ] [ 2 ] = ( rand ( ) & 255 ) * 0.01 ;
}
}
for ( i = 0 ; i < NUMVERTEXNORMALS ; i + + )
{
if ( ! free_particles )
return ;
p = free_particles ;
free_particles = p - > next ;
p - > next = active_particles ;
active_particles = p ;
angle = cl . time * avelocities [ i ] [ 0 ] ;
sy = sin ( angle ) ;
cy = cos ( angle ) ;
angle = cl . time * avelocities [ i ] [ 1 ] ;
sp = sin ( angle ) ;
cp = cos ( angle ) ;
//fixme: is roll important?
forward [ 0 ] = cp * cy ;
forward [ 1 ] = cp * sy ;
forward [ 2 ] = - sp ;
p - > die = cl . time ; // + 0.01;
2018-03-04 14:41:16 +00:00
p - > rgb = d_8to24srgbtable [ 0x6f ] ;
2016-07-12 00:40:13 +00:00
p - > type = pt_oneframe ;
p - > org [ 0 ] = org [ 0 ] + r_avertexnormals [ i ] [ 0 ] * dist + forward [ 0 ] * beamlength ;
p - > org [ 1 ] = org [ 1 ] + r_avertexnormals [ i ] [ 1 ] * dist + forward [ 1 ] * beamlength ;
p - > org [ 2 ] = org [ 2 ] + r_avertexnormals [ i ] [ 2 ] * dist + forward [ 2 ] * beamlength ;
}
}
//svc_tempentity support: this is the function that handles 'special' point effects.
//use the trail state so fast/slow frames keep the correct particle counts on certain every-frame effects
static int PClassic_RunParticleEffectState ( vec3_t org , vec3_t dir , float count , int typenum , trailstate_t * * tsk )
{
switch ( typenum & 0xff )
{
case BRIGHTFIELD_POINT :
Classic_BrightField ( org ) ;
break ;
case BLOBEXPLOSION_POINT :
Classic_BlobExplosion ( org ) ;
break ;
case LAVASPLASH_POINT :
Classic_LavaSplash ( org ) ;
break ;
case EXPLOSION_POINT :
Classic_ParticleExplosion ( org ) ;
break ;
case EXPLOSION2_POINT :
Classic_ParticleExplosion2 ( org , ( typenum > > 8 ) & 0xff , ( typenum > > 16 ) & 0xff ) ;
break ;
case TELEPORTSPLASH_POINT :
Classic_TeleportSplash ( org ) ;
break ;
case MUZZLEFLASH_POINT :
{
dlight_t * dl = CL_AllocDlight ( 0 ) ;
if ( dir )
VectorCopy ( dir , dl - > axis [ 0 ] ) ;
else
2018-07-22 11:49:37 +00:00
VectorSet ( dl - > axis [ 0 ] , 0 , 0 , 1 ) ;
2016-07-12 00:40:13 +00:00
VectorVectors ( dl - > axis [ 0 ] , dl - > axis [ 1 ] , dl - > axis [ 2 ] ) ;
VectorInverse ( dl - > axis [ 1 ] ) ;
if ( dir )
VectorMA ( org , 15 , dl - > axis [ 0 ] , dl - > origin ) ;
else
VectorCopy ( org , dl - > origin ) ;
dl - > radius = 200 + ( rand ( ) & 31 ) ;
dl - > minlight = 32 ;
dl - > die = cl . time + 0.1 ;
dl - > color [ 0 ] = 1.5 ;
dl - > color [ 1 ] = 1.3 ;
dl - > color [ 2 ] = 1.0 ;
dl - > channelfade [ 0 ] = 1.5 ;
dl - > channelfade [ 1 ] = 0.75 ;
dl - > channelfade [ 2 ] = 0.375 ;
dl - > decay = 1000 ;
# ifdef RTLIGHTS
dl - > lightcolourscales [ 2 ] = 4 ;
# endif
}
break ;
2018-01-22 19:18:04 +00:00
case QWGUNSHOT_POINT :
Classic_RunParticleEffect ( org , dir , 0 , count * 20 , true ) ;
break ;
case QWSTDBLOOD_POINT :
Classic_RunParticleEffect ( org , dir , 73 , count * 20 , true ) ;
break ;
case QWLGBLOOD_POINT :
Classic_RunParticleEffect ( org , dir , 225 , count * 50 , true ) ;
break ;
2016-07-12 00:40:13 +00:00
default :
return 1 ;
}
return 0 ;
}
static float Classic_ParticleTrail ( vec3_t start , vec3_t end , float leftover , effect_type_t type )
{
2018-01-28 04:12:59 +00:00
vec3_t point , delta , dir , step ;
2016-07-12 00:40:13 +00:00
float len , rlen , scale ;
int i , j , num_particles ;
cparticle_t * p ;
static int tracercount ;
if ( type > = BRIGHTFIELD_POINT )
{
PClassic_RunParticleEffectState ( end , vec3_origin , 1 , type , NULL ) ;
return 0 ;
}
VectorCopy ( start , point ) ;
VectorSubtract ( end , start , delta ) ;
if ( ! ( len = VectorLength ( delta ) ) )
goto done ;
VectorScale ( delta , 1 / len , dir ) ; //unit vector in direction of trail
VectorMA ( point , - leftover , dir , point ) ;
len + = leftover ;
rlen = len ;
switch ( type )
{
case ALT_ROCKET_TRAIL :
scale = 1.5 ; break ;
case BLOOD_TRAIL :
scale = 6 ; break ;
default :
scale = 3 ; break ;
case TRACER1_TRAIL :
case TRACER2_TRAIL :
scale = ( r_part_density . value < 0.5 ) ? 6 * r_part_density . value : 3 ;
break ;
}
scale / = r_part_density . value ;
2018-01-28 04:12:59 +00:00
VectorScale ( dir , scale , step ) ;
2016-07-12 00:40:13 +00:00
len / = scale ;
leftover = rlen - ( ( int ) ( len ) * scale ) ;
num_particles = ( int ) len ;
for ( i = 0 ; i < num_particles & & free_particles ; i + + )
{
p = free_particles ;
free_particles = p - > next ;
p - > next = active_particles ;
active_particles = p ;
VectorClear ( p - > vel ) ;
p - > die = cl . time + 2 ;
switch ( type )
{
case GRENADE_TRAIL :
p - > ramp = ( rand ( ) & 3 ) + 2 ;
2018-03-04 14:41:16 +00:00
p - > rgb = d_8to24srgbtable [ ramp3 [ ( int ) p - > ramp ] ] ;
2016-07-12 00:40:13 +00:00
p - > type = pt_fire ;
for ( j = 0 ; j < 3 ; j + + )
p - > org [ j ] = point [ j ] + ( ( rand ( ) % 6 ) - 3 ) ;
break ;
case BLOOD_TRAIL :
p - > type = pt_slowgrav ;
2018-03-04 14:41:16 +00:00
p - > rgb = d_8to24srgbtable [ 67 + ( rand ( ) & 3 ) ] ;
2016-07-12 00:40:13 +00:00
for ( j = 0 ; j < 3 ; j + + )
p - > org [ j ] = point [ j ] + ( ( rand ( ) % 6 ) - 3 ) ;
break ;
case BIG_BLOOD_TRAIL :
p - > type = pt_slowgrav ;
2018-03-04 14:41:16 +00:00
p - > rgb = d_8to24srgbtable [ 67 + ( rand ( ) & 3 ) ] ;
2016-07-12 00:40:13 +00:00
for ( j = 0 ; j < 3 ; j + + )
p - > org [ j ] = point [ j ] + ( ( rand ( ) % 6 ) - 3 ) ;
break ;
case TRACER1_TRAIL :
case TRACER2_TRAIL :
p - > die = cl . time + 0.5 ;
p - > type = pt_static ;
if ( type = = TRACER1_TRAIL )
2018-03-04 14:41:16 +00:00
p - > rgb = d_8to24srgbtable [ 52 + ( ( tracercount & 4 ) < < 1 ) ] ;
2016-07-12 00:40:13 +00:00
else
2018-03-04 14:41:16 +00:00
p - > rgb = d_8to24srgbtable [ 230 + ( ( tracercount & 4 ) < < 1 ) ] ;
2016-07-12 00:40:13 +00:00
tracercount + + ;
VectorCopy ( point , p - > org ) ;
if ( tracercount & 1 )
2018-01-28 04:12:59 +00:00
{ //the addition of /scale here counters dir being rescaled
p - > vel [ 0 ] = 30 * dir [ 1 ] ;
p - > vel [ 1 ] = 30 * - dir [ 0 ] ;
2016-07-12 00:40:13 +00:00
}
else
{
2018-01-28 04:12:59 +00:00
p - > vel [ 0 ] = 30 * - dir [ 1 ] ;
p - > vel [ 1 ] = 30 * dir [ 0 ] ;
2016-07-12 00:40:13 +00:00
}
break ;
case VOOR_TRAIL :
2018-03-04 14:41:16 +00:00
p - > rgb = d_8to24srgbtable [ 9 * 16 + 8 + ( rand ( ) & 3 ) ] ;
2016-07-12 00:40:13 +00:00
p - > type = pt_static ;
p - > die = cl . time + 0.3 ;
for ( j = 0 ; j < 3 ; j + + )
p - > org [ j ] = point [ j ] + ( ( rand ( ) & 15 ) - 8 ) ;
break ;
case ALT_ROCKET_TRAIL :
p - > ramp = ( rand ( ) & 3 ) ;
2018-03-04 14:41:16 +00:00
p - > rgb = d_8to24srgbtable [ ramp3 [ ( int ) p - > ramp ] ] ;
2016-07-12 00:40:13 +00:00
p - > type = pt_fire ;
for ( j = 0 ; j < 3 ; j + + )
p - > org [ j ] = point [ j ] + ( ( rand ( ) % 6 ) - 3 ) ;
break ;
case ROCKET_TRAIL :
default :
p - > ramp = ( rand ( ) & 3 ) ;
2018-03-04 14:41:16 +00:00
p - > rgb = d_8to24srgbtable [ ramp3 [ ( int ) p - > ramp ] ] ;
2016-07-12 00:40:13 +00:00
p - > type = pt_fire ;
for ( j = 0 ; j < 3 ; j + + )
p - > org [ j ] = point [ j ] + ( ( rand ( ) % 6 ) - 3 ) ;
break ;
}
2018-01-28 04:12:59 +00:00
VectorAdd ( point , step , point ) ;
2016-07-12 00:40:13 +00:00
}
done :
return leftover ;
}
int PClassic_PointFile ( int c , vec3_t point )
{
cparticle_t * p ;
if ( ! free_particles )
return 0 ;
p = free_particles ;
free_particles = p - > next ;
p - > next = active_particles ;
active_particles = p ;
VectorClear ( p - > vel ) ;
p - > die = 99999 ;
2018-03-04 14:41:16 +00:00
p - > rgb = d_8to24srgbtable [ ( - c ) & 0xff ] ;
2016-07-12 00:40:13 +00:00
p - > type = pt_static ;
VectorCopy ( point , p - > org ) ;
return 1 ;
}
//builds a trail from here to there. The trail state can be used to remember how far you got last frame.
2019-01-29 07:18:07 +00:00
static int PClassic_ParticleTrail ( vec3_t startpos , vec3_t end , int type , float timestep , int dlkey , vec3_t dlaxis [ 3 ] , trailstate_t * * tsk )
2016-07-12 00:40:13 +00:00
{
float leftover ;
if ( type = = P_INVALID )
return 1 ;
leftover = Classic_ParticleTrail ( startpos , end , Classic_GetLeftover ( tsk ) , type ) ;
Classic_SetLeftover ( tsk , leftover ) ;
return 0 ;
}
//svc_particle support: add X particles with the given colour, velocity, and aproximate origin.
static void PClassic_RunParticleEffect ( vec3_t org , vec3_t dir , int color , int count )
{
2018-01-22 19:18:04 +00:00
Classic_RunParticleEffect ( org , dir , color , count , false ) ;
2016-07-12 00:40:13 +00:00
}
static void PClassic_RunParticleEffectPalette ( const char * nameprefix , vec3_t org , vec3_t dir , int color , int count )
{
2018-01-22 19:18:04 +00:00
Classic_RunParticleEffect ( org , dir , color , count , false ) ;
2016-07-12 00:40:13 +00:00
}
particleengine_t pe_classic =
{
" Classic " ,
NULL ,
PClassic_FindParticleType ,
PClassic_Query ,
PClassic_RunParticleEffectTypeString ,
PClassic_ParticleTrail ,
PClassic_RunParticleEffectState ,
PClassic_RunParticleWeather ,
PClassic_RunParticleCube ,
PClassic_RunParticleEffect ,
PClassic_RunParticleEffect2 ,
PClassic_RunParticleEffect3 ,
PClassic_RunParticleEffect4 ,
PClassic_RunParticleEffectPalette ,
PClassic_ParticleTrailIndex ,
PClassic_InitParticles ,
PClassic_ShutdownParticles ,
PClassic_DelinkTrailstate ,
PClassic_ClearParticles ,
PClassic_DrawParticles
} ;
# endif