2009-07-17 22:28:16 +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 .
*/
2004-08-23 00:15:46 +00:00
# include "quakedef.h"
2008-11-09 22:29:28 +00:00
# define NUMVERTEXNORMALS 162
float r_avertexnormals [ NUMVERTEXNORMALS ] [ 3 ] = {
# include "anorms.h"
} ;
2005-10-10 01:16:58 +00:00
2004-08-23 00:15:46 +00:00
2015-03-03 00:14:43 +00:00
# include "shader.h"
# include "com_mesh.h"
//FIXME: we're likely going to want to thread the building routine at some point.
//the alias mesh stuff will need some rework as it uses statics inside.
# define DESCSPERSHADER 8
typedef struct
{
int x , y , z ; //rebuilt if changed
int key ;
model_t * loadingmodel ; //needs rebuilding, but wait till this is loaded.
struct
{
shader_t * shader ;
mesh_t mesh ;
mesh_t * pmesh ;
vbo_t vbo ;
} soups [ 64 ] ;
size_t numsoups ;
} cluttersector_t ;
static cluttersector_t cluttersector [ 3 * 3 * 3 ] ;
cvar_t r_clutter_density = CVARD ( " r_clutter_density " , " 1 " , " Scaler for clutter counts. 0 disables clutter completely. \n Clutter requires shaders with 'fte_clutter MODEL SPACING SCALEMIN SCALEMAX ZOFS ANGLEMIN ANGLEMAX' terms " ) ;
cvar_t r_clutter_distance = CVARD ( " r_clutter_distance " , " 1024 " , " Distance at which clutter will become invisible. " ) ; //should be used by various shaders to fade it out by here
void R_Clutter_Init ( void )
{
Cvar_Register ( & r_clutter_density , " Ground Clutter " ) ;
Cvar_Register ( & r_clutter_distance , " Ground Clutter " ) ;
}
typedef struct
{
model_t * loadingmodel ;
struct clutter_build_ctx_soup_s
{
shader_t * shader ;
vecV_t * coord ;
vec2_t * texcoord ;
vec4_t * colour ;
vec3_t * normal ;
vec3_t * sdir ;
vec3_t * tdir ;
index_t * idx ;
size_t numverts ;
size_t numidx ;
size_t maxverts ;
size_t maxidx ;
} soups [ 64 ] ;
unsigned int numsoups ;
float area [ DESCSPERSHADER ] ; //here so it can overflow, so large values with small surfaces actually does something. not evenly perhaps, but not much else we can do
unsigned int x , y , z , w ;
} clutter_build_ctx_t ;
//to make things repeatable so that people can depend upon placement.
unsigned int R_Clutter_Random ( clutter_build_ctx_t * ctx )
{ //ripped from wikipedia (originally called xorshift128)
unsigned int t = ctx - > x ^ ( ctx - > x < < 11 ) ;
ctx - > x = ctx - > y ; ctx - > y = ctx - > z ; ctx - > z = ctx - > w ;
return ctx - > w = ctx - > w ^ ( ctx - > w > > 19 ) ^ t ^ ( t > > 8 ) ;
}
float R_Clutter_FRandom ( clutter_build_ctx_t * ctx )
{
unsigned int r = R_Clutter_Random ( ctx ) ;
return ( r & 0xffffff ) / ( float ) 0xffffff ;
}
static void R_Clutter_Insert_Soup ( clutter_build_ctx_t * ctx , shader_t * shader , vecV_t * fte_restrict coord , vec2_t * fte_restrict texcoord , vec3_t * fte_restrict normal , vec3_t * fte_restrict sdir , vec3_t * fte_restrict tdir , vec4_t * fte_restrict colours , size_t numverts , index_t * fte_restrict index , size_t numidx , float scale , vec3_t origin , vec3_t axis [ ] )
{
vec3_t diffuse , ambient , ldir ;
float dot ;
struct clutter_build_ctx_soup_s * soup = NULL ;
size_t i ;
shader_t * os = shader ;
shader = R_RegisterShader ( va ( " clutter#replace=%s " , os - > name ) , SUF_NONE ,
" { \n "
" program defaultsprite#MASK=0.666 \n "
// "surfaceparm nodlight\n"
" surfaceparm noshadows \n "
// "cull disable\n"
" { \n "
" map $diffuse \n "
" rgbgen vertex \n "
" alphagen vertex \n "
// "alphafunc ge128\n"
" } \n "
" } \n "
) ;
2015-05-03 19:57:46 +00:00
* shader - > defaulttextures = * os - > defaulttextures ;
2015-03-03 00:14:43 +00:00
for ( i = 0 , soup = ctx - > soups ; i < ctx - > numsoups ; i + + , soup + + )
{
if ( soup - > shader = = shader )
if ( soup - > numverts + numverts < = MAX_INDICIES )
break ;
}
if ( i = = ctx - > numsoups )
{
if ( i = = sizeof ( ctx - > soups ) / sizeof ( ctx - > soups [ 0 ] ) )
return ; //too many different shaders or something
soup - > shader = shader ;
ctx - > numsoups + + ;
}
//inject the indicies
if ( soup - > numidx + numidx > soup - > maxidx )
{
soup - > maxidx = ( soup - > numidx + numidx ) * 2 ;
soup - > idx = BZ_Realloc ( soup - > idx , sizeof ( * soup - > idx ) * soup - > maxidx ) ;
}
for ( i = 0 ; i < numidx ; i + + )
soup - > idx [ soup - > numidx + + ] = soup - > numverts + * index + + ;
cl . worldmodel - > funcs . LightPointValues ( cl . worldmodel , origin , diffuse , ambient , ldir ) ;
VectorScale ( ambient , 1 / 255.0 , ambient ) ;
VectorScale ( diffuse , 1 / 255.0 , diffuse ) ;
//inject the verts
if ( soup - > numverts + numverts > soup - > maxverts )
{
soup - > maxverts = ( soup - > numverts + numverts ) * 2 ;
soup - > coord = BZ_Realloc ( soup - > coord , sizeof ( * soup - > coord ) * soup - > maxverts ) ;
soup - > texcoord = BZ_Realloc ( soup - > texcoord , sizeof ( * soup - > texcoord ) * soup - > maxverts ) ;
soup - > colour = BZ_Realloc ( soup - > colour , sizeof ( * soup - > colour ) * soup - > maxverts ) ;
soup - > normal = BZ_Realloc ( soup - > normal , sizeof ( * soup - > normal ) * soup - > maxverts ) ;
soup - > sdir = BZ_Realloc ( soup - > sdir , sizeof ( * soup - > sdir ) * soup - > maxverts ) ;
soup - > tdir = BZ_Realloc ( soup - > tdir , sizeof ( * soup - > tdir ) * soup - > maxverts ) ;
}
for ( i = 0 ; i < numverts ; i + + )
{
VectorMA ( origin , scale * coord [ i ] [ 0 ] , axis [ 0 ] , soup - > coord [ soup - > numverts ] ) ;
VectorMA ( soup - > coord [ soup - > numverts ] , scale * coord [ i ] [ 1 ] , axis [ 1 ] , soup - > coord [ soup - > numverts ] ) ;
VectorMA ( soup - > coord [ soup - > numverts ] , scale * coord [ i ] [ 2 ] , axis [ 2 ] , soup - > coord [ soup - > numverts ] ) ;
Vector2Copy ( texcoord [ i ] , soup - > texcoord [ soup - > numverts ] ) ;
VectorMA ( vec3_origin , normal [ i ] [ 0 ] , axis [ 0 ] , soup - > normal [ soup - > numverts ] ) ;
VectorMA ( soup - > normal [ soup - > numverts ] , normal [ i ] [ 1 ] , axis [ 1 ] , soup - > normal [ soup - > numverts ] ) ;
VectorMA ( soup - > normal [ soup - > numverts ] , normal [ i ] [ 2 ] , axis [ 2 ] , soup - > normal [ soup - > numverts ] ) ;
VectorMA ( vec3_origin , sdir [ i ] [ 0 ] , axis [ 0 ] , soup - > sdir [ soup - > numverts ] ) ;
VectorMA ( soup - > sdir [ soup - > numverts ] , sdir [ i ] [ 1 ] , axis [ 1 ] , soup - > sdir [ soup - > numverts ] ) ;
VectorMA ( soup - > sdir [ soup - > numverts ] , sdir [ i ] [ 2 ] , axis [ 2 ] , soup - > sdir [ soup - > numverts ] ) ;
VectorMA ( vec3_origin , tdir [ i ] [ 0 ] , axis [ 0 ] , soup - > tdir [ soup - > numverts ] ) ;
VectorMA ( soup - > tdir [ soup - > numverts ] , tdir [ i ] [ 1 ] , axis [ 1 ] , soup - > tdir [ soup - > numverts ] ) ;
VectorMA ( soup - > tdir [ soup - > numverts ] , tdir [ i ] [ 2 ] , axis [ 2 ] , soup - > tdir [ soup - > numverts ] ) ;
// VectorCopy(ambient, soup->colour[soup->numverts]);
dot = DotProduct ( ldir , soup - > normal [ soup - > numverts ] ) ;
if ( dot < 0 )
dot = 0 ;
VectorMA ( ambient , dot , diffuse , soup - > colour [ soup - > numverts ] ) ;
if ( colours ) //most model formats don't have vertex colours
soup - > colour [ soup - > numverts ] [ 3 ] = colours [ i ] [ 3 ] ;
else
soup - > colour [ soup - > numverts ] [ 3 ] = 1 ;
soup - > numverts + + ;
}
}
static void R_Clutter_Insert_Mesh ( clutter_build_ctx_t * ctx , model_t * mod , float scale , vec3_t origin , vec3_t axis [ 3 ] )
{
mesh_t mesh ;
galiasinfo_t * inf ;
unsigned int surfnum = 0 ;
entity_t re ;
unsigned int randanim = R_Clutter_Random ( ctx ) ;
unsigned int randskin = R_Clutter_Random ( ctx ) ;
if ( ! mod )
return ;
//fill in the parts of the entity_t that Alias_GAliasBuildMesh needs.
memset ( & re , 0 , sizeof ( re ) ) ;
// memset(&re.framestate, 0, sizeof(re.framestate));
re . framestate . g [ FS_REG ] . lerpweight [ 0 ] = 1 ;
re . model = mod ;
inf = ( galiasinfo_t * ) Mod_Extradata ( mod ) ;
while ( inf )
{
galiasskin_t * skins = inf - > ofsskins ;
re . framestate . g [ FS_REG ] . frame [ 0 ] = randanim % inf - > numanimations ;
if ( skins - > numframes )
{
unsigned int frame = randskin % skins - > numframes ;
Alias_GAliasBuildMesh ( & mesh , NULL , inf , surfnum , & re , false ) ;
surfnum + + ;
//fixme: if shares verts, rewind the verts and don't add more somehow, while being careful with shaders
R_Clutter_Insert_Soup ( ctx , skins - > frame [ frame ] . shader , mesh . xyz_array , mesh . st_array , mesh . normals_array , mesh . snormals_array , mesh . tnormals_array , mesh . colors4f_array [ 0 ] , mesh . numvertexes , mesh . indexes , mesh . numindexes , scale , origin , axis ) ;
}
inf = inf - > nextsurf ;
}
Alias_FlushCache ( ) ; //it got built using an entity on the stack, make sure other stuff doesn't get hurt.
}
static void R_Clutter_Insert ( void * vctx , vec3_t * fte_restrict points , size_t numtris , shader_t * surface )
{
struct shader_clutter_s * clut ;
unsigned int obj ;
clutter_build_ctx_t * ctx = vctx ;
model_t * mod [ DESCSPERSHADER ] ;
if ( ! surface | | ! surface - > clutter )
return ; //nothing to do.
//avoid returning on error, so the randomization is dependable when content is still loading.
for ( clut = surface - > clutter , obj = 0 ; clut & & obj < = DESCSPERSHADER ; clut = clut - > next , obj + + )
{
mod [ obj ] = Mod_ForName ( clut - > modelname , MLV_WARN ) ;
if ( mod [ obj ] - > loadstate = = MLS_LOADING )
{
if ( ! ctx - > loadingmodel )
ctx - > loadingmodel = mod [ obj ] ;
mod [ obj ] = NULL ;
}
else if ( mod [ obj ] - > type ! = mod_alias )
mod [ obj ] = NULL ;
}
while ( numtris - - > 0 )
{
vec3_t xd ;
vec3_t yd ;
vec3_t zd ;
vec3_t norm ;
vec3_t axis [ 3 ] ;
vec3_t org , dir ;
float dot ;
float triarea ;
// vec3_t discard;
// unsigned int subimage;
vec_t xm , ym , zm , s ;
VectorSubtract ( points [ 1 ] , points [ 0 ] , xd ) ;
VectorSubtract ( points [ 2 ] , points [ 0 ] , yd ) ;
VectorSubtract ( points [ 2 ] , points [ 1 ] , zd ) ;
CrossProduct ( yd , xd , norm ) ;
VectorNormalize ( norm ) ;
if ( norm [ 2 ] > = 0.7 )
{
//determine area of triangle
xm = Length ( xd ) ;
ym = Length ( yd ) ;
zm = Length ( zd ) ;
s = ( xm + ym + zm ) / 2 ;
triarea = sqrt ( s * ( s - xm ) * ( s - ym ) * ( s - zm ) ) ;
for ( clut = surface - > clutter , obj = 0 ; clut & & obj < = DESCSPERSHADER ; clut = clut - > next , obj + + )
{
float spacing = clut - > spacing / r_clutter_density . value ;
if ( spacing < 1 )
spacing = 1 ;
ctx - > area [ obj ] + = triarea ;
while ( ctx - > area [ obj ] > = spacing )
{
float scale = clut - > scalemin + R_Clutter_FRandom ( ctx ) * ( clut - > scalemax - clut - > scalemin ) ;
ctx - > area [ obj ] - = spacing ;
//pick a random spot
xm = R_Clutter_FRandom ( ctx ) * R_Clutter_FRandom ( ctx ) ;
ym = R_Clutter_FRandom ( ctx ) * ( 1 - xm ) ;
VectorMA ( points [ 0 ] , xm , xd , org ) ;
VectorMA ( org , ym , yd , org ) ;
//randomize the direction
dot = clut - > anglemin + R_Clutter_FRandom ( ctx ) * ( clut - > anglemax - clut - > anglemin ) ;
dir [ 0 ] = cos ( dot ) ;
dir [ 1 ] = sin ( dot ) ;
dir [ 2 ] = 0 ;
//figure out various directions
dot = - DotProduct ( dir , norm ) ;
VectorMA ( dir , dot , norm , dir ) ;
VectorNormalize ( dir ) ;
VectorCopy ( norm , axis [ 2 ] ) ;
CrossProduct ( axis [ 2 ] , dir , axis [ 1 ] ) ;
CrossProduct ( axis [ 1 ] , axis [ 2 ] , axis [ 0 ] ) ;
VectorMA ( org , clut - > zofs * scale , axis [ 2 ] , org ) ;
R_Clutter_Insert_Mesh ( ctx , mod [ obj ] , scale , org , axis ) ;
/*
VectorMA ( org , r_clutter_size . value / 2 , dir , vertcoord [ numverts ] ) ;
VectorMA ( org , - ( r_clutter_size . value / 2 ) , dir , vertcoord [ numverts + 1 ] ) ;
VectorMA ( vertcoord [ numverts ] , r_clutter_height . value , norm , vertcoord [ numverts + 2 ] ) ;
VectorMA ( vertcoord [ numverts + 1 ] , r_clutter_height . value , norm , vertcoord [ numverts + 3 ] ) ;
subimage = R_Clutter_Random ( ctx ) ;
Vector2Set ( texcoord [ numverts ] , subimage % r_clutter_atlaswidth . ival , ( subimage / r_clutter_atlaswidth . ival ) % r_clutter_atlasheight . ival ) ;
texcoord [ numverts ] [ 0 ] * = 1 / r_clutter_atlaswidth . value ;
texcoord [ numverts ] [ 1 ] * = 1 / r_clutter_atlasheight . value ;
Vector2Set ( texcoord [ numverts + 1 ] , texcoord [ numverts ] [ 0 ] + ( 1 / r_clutter_atlaswidth . value ) , texcoord [ numverts ] [ 1 ] ) ;
Vector2Set ( texcoord [ numverts + 2 ] , texcoord [ numverts ] [ 0 ] , texcoord [ numverts ] [ 1 ] ) ;
Vector2Set ( texcoord [ numverts + 3 ] , texcoord [ numverts ] [ 0 ] + ( 1 / r_clutter_atlaswidth . value ) , texcoord [ numverts ] [ 1 ] ) ;
texcoord [ numverts + 0 ] [ 1 ] + = ( 1 / r_clutter_atlasheight . value ) ;
texcoord [ numverts + 1 ] [ 1 ] + = ( 1 / r_clutter_atlasheight . value ) ;
Vector4Set ( colours [ numverts + 0 ] , 1 , 1 , 1 , 1 ) ;
VectorMA ( org , 1 / 8.0 , norm , org ) ; //push away from the surface to avoid precision issues with lighting on slopes
cl . worldmodel - > funcs . LightPointValues ( cl . worldmodel , org , colours [ numverts + 0 ] , discard , discard ) ;
VectorScale ( colours [ numverts + 0 ] , 1 / 512.0 , colours [ numverts + 0 ] ) ;
Vector4Copy ( colours [ numverts + 0 ] , colours [ numverts + 1 ] ) ;
Vector4Copy ( colours [ numverts + 0 ] , colours [ numverts + 2 ] ) ;
Vector4Copy ( colours [ numverts + 1 ] , colours [ numverts + 3 ] ) ;
indexes [ numidx + 0 ] = numverts + 0 ;
indexes [ numidx + 1 ] = numverts + 2 ;
indexes [ numidx + 2 ] = numverts + 1 ;
indexes [ numidx + 3 ] = numverts + 2 ;
indexes [ numidx + 4 ] = numverts + 3 ;
indexes [ numidx + 5 ] = numverts + 1 ;
numverts + = 4 ;
numidx + = 6 ;
*/
}
}
}
points + = 3 ;
}
}
void R_Clutter_Emit ( batch_t * * batches )
{
const float cluttersize = r_clutter_distance . value ;
int vx , vy , vz ;
int x , y , z , key , i ;
cluttersector_t * sect ;
batch_t * b ;
qboolean rebuildlimit = false ;
if ( ! cl . worldmodel | | cl . worldmodel - > loadstate ! = MLS_LOADED | | r_clutter_density . value < = 0 )
return ;
if ( qrenderer ! = QR_OPENGL ) //vbo only!
return ;
//rebuild if any of the cvars changes.
key = r_clutter_density . modified + r_clutter_distance . modified ;
vx = floor ( ( r_refdef . vieworg [ 0 ] / cluttersize ) ) ;
vy = floor ( ( r_refdef . vieworg [ 1 ] / cluttersize ) ) ;
vz = floor ( ( r_refdef . vieworg [ 2 ] / cluttersize ) ) ;
for ( z = vz - 1 ; z < = vz + 1 ; z + + )
for ( y = vy - 1 ; y < = vy + 1 ; y + + )
for ( x = vx - 1 ; x < = vx + 1 ; x + + )
{
int ix = x % 3 ;
int iy = y % 3 ;
int iz = z % 3 ;
if ( ix < 0 )
ix + = 3 ;
if ( iy < 0 )
iy + = 3 ;
if ( iz < 0 )
iz + = 3 ;
sect = & cluttersector [ ix + ( iy * 3 ) + ( iz * 3 * 3 ) ] ;
if ( sect - > loadingmodel & & sect - > loadingmodel - > loadstate ! = MLS_LOADING )
{
sect - > loadingmodel = NULL ;
sect - > key - = 1 ; //rebuild even if failed, this covers multiple models.
}
if ( sect - > x ! = x | | sect - > y ! = y | | sect - > z ! = z | | sect - > key ! = key )
{
vbobctx_t vctx ;
clutter_build_ctx_t cctx ;
vec3_t org = { x * cluttersize + ( cluttersize / 2 ) , y * cluttersize + ( cluttersize / 2 ) , z * cluttersize + ( cluttersize / 2 ) } ;
vec3_t down = { 0 , 0 , - 1 } ;
vec3_t forward = { 1 , 0 , 0 } ;
vec3_t right = { 0 , 1 , 0 } ;
if ( r_refdef . recurse ) //FIXME
continue ;
if ( rebuildlimit )
continue ;
rebuildlimit = true ;
sect - > x = x ;
sect - > y = y ;
sect - > z = z ;
sect - > key = key ;
//make sure any old state is gone
for ( i = 0 ; i < sect - > numsoups ; i + + )
{
BE_VBO_Destroy ( & sect - > soups [ i ] . vbo . coord ) ;
BE_VBO_Destroy ( & sect - > soups [ i ] . vbo . indicies ) ;
}
sect - > numsoups = 0 ;
memset ( & cctx , 0 , sizeof ( cctx ) ) ;
cctx . x = x ;
cctx . y = y ;
cctx . z = z ;
cctx . w = ( sect - cluttersector ) + 1 ;
Mod_ClipDecal ( cl . worldmodel , org , down , forward , right , cluttersize , R_Clutter_Insert , & cctx ) ;
sect - > loadingmodel = cctx . loadingmodel ;
for ( i = 0 ; i < cctx . numsoups ; i + + )
{
if ( cctx . soups [ i ] . numverts )
{
sect - > soups [ sect - > numsoups ] . shader = cctx . soups [ i ] . shader ;
sect - > soups [ sect - > numsoups ] . pmesh = & sect - > soups [ sect - > numsoups ] . mesh ;
BE_VBO_Begin ( & vctx , ( sizeof ( cctx . soups [ i ] . coord [ 0 ] ) + sizeof ( cctx . soups [ i ] . texcoord [ 0 ] ) + sizeof ( cctx . soups [ i ] . colour [ 0 ] ) + 3 * sizeof ( vec3_t ) ) * cctx . soups [ i ] . numverts ) ;
BE_VBO_Data ( & vctx , cctx . soups [ i ] . coord , sizeof ( cctx . soups [ i ] . coord [ 0 ] ) * cctx . soups [ i ] . numverts , & sect - > soups [ sect - > numsoups ] . vbo . coord ) ;
BE_VBO_Data ( & vctx , cctx . soups [ i ] . texcoord , sizeof ( cctx . soups [ i ] . texcoord [ 0 ] ) * cctx . soups [ i ] . numverts , & sect - > soups [ sect - > numsoups ] . vbo . texcoord ) ;
BE_VBO_Data ( & vctx , cctx . soups [ i ] . colour , sizeof ( cctx . soups [ i ] . colour [ 0 ] ) * cctx . soups [ i ] . numverts , & sect - > soups [ sect - > numsoups ] . vbo . colours [ 0 ] ) ;
BE_VBO_Data ( & vctx , cctx . soups [ i ] . normal , sizeof ( cctx . soups [ i ] . normal [ 0 ] ) * cctx . soups [ i ] . numverts , & sect - > soups [ sect - > numsoups ] . vbo . normals ) ;
BE_VBO_Data ( & vctx , cctx . soups [ i ] . sdir , sizeof ( cctx . soups [ i ] . sdir [ 0 ] ) * cctx . soups [ i ] . numverts , & sect - > soups [ sect - > numsoups ] . vbo . svector ) ;
BE_VBO_Data ( & vctx , cctx . soups [ i ] . tdir , sizeof ( cctx . soups [ i ] . tdir [ 0 ] ) * cctx . soups [ i ] . numverts , & sect - > soups [ sect - > numsoups ] . vbo . tvector ) ;
BE_VBO_Finish ( & vctx , cctx . soups [ i ] . idx , sizeof ( cctx . soups [ i ] . idx [ 0 ] ) * cctx . soups [ i ] . numidx , & sect - > soups [ sect - > numsoups ] . vbo . indicies ) ;
sect - > soups [ sect - > numsoups ] . mesh . numindexes = sect - > soups [ sect - > numsoups ] . vbo . indexcount = cctx . soups [ i ] . numidx ;
sect - > soups [ sect - > numsoups ] . mesh . numvertexes = sect - > soups [ sect - > numsoups ] . vbo . vertcount = cctx . soups [ i ] . numverts ;
sect - > numsoups + + ;
}
BZ_Free ( cctx . soups [ i ] . coord ) ;
BZ_Free ( cctx . soups [ i ] . texcoord ) ;
BZ_Free ( cctx . soups [ i ] . colour ) ;
BZ_Free ( cctx . soups [ i ] . normal ) ;
BZ_Free ( cctx . soups [ i ] . sdir ) ;
BZ_Free ( cctx . soups [ i ] . tdir ) ;
BZ_Free ( cctx . soups [ i ] . idx ) ;
}
}
//emit a batch if we have grassy surfaces in this block
for ( i = 0 ; i < sect - > numsoups ; i + + )
{
b = BE_GetTempBatch ( ) ;
if ( ! b )
return ;
memset ( b , 0 , sizeof ( * b ) ) ;
b - > ent = & r_worldentity ;
b - > meshes = 1 ;
b - > mesh = & sect - > soups [ i ] . pmesh ;
b - > vbo = & sect - > soups [ i ] . vbo ;
b - > shader = sect - > soups [ i ] . shader ;
b - > next = batches [ b - > shader - > sort ] ;
batches [ b - > shader - > sort ] = b ;
}
}
}
void R_Clutter_Purge ( void )
{
size_t i , j ;
cluttersector_t * sect ;
if ( ! qrenderer )
return ;
for ( i = 0 ; i < sizeof ( cluttersector ) / sizeof ( cluttersector [ 0 ] ) ; i + + )
{
sect = & cluttersector [ i ] ;
for ( j = 0 ; j < sect - > numsoups ; j + + )
{
BE_VBO_Destroy ( & sect - > soups [ j ] . vbo . coord ) ;
BE_VBO_Destroy ( & sect - > soups [ j ] . vbo . indicies ) ;
}
memset ( sect , 0 , sizeof ( * sect ) ) ;
}
}
2015-04-21 04:12:00 +00:00
static void QDECL R_Rockettrail_Callback ( struct cvar_s * var , char * oldvalue )
2008-11-09 22:29:28 +00:00
{
int i ;
model_t * mod ;
2014-08-25 07:35:41 +00:00
extern model_t * mod_known ;
2008-11-09 22:29:28 +00:00
extern int mod_numknown ;
2004-08-23 00:15:46 +00:00
2008-11-09 22:29:28 +00:00
if ( cls . state = = ca_disconnected )
return ; // don't bother parsing while disconnected
2004-08-23 00:15:46 +00:00
2008-11-09 22:29:28 +00:00
for ( i = 0 , mod = mod_known ; i < mod_numknown ; i + + , mod + + )
{
2014-10-05 20:04:11 +00:00
if ( mod - > loadstate = = MLS_LOADED )
2011-10-27 15:46:36 +00:00
if ( mod - > flags & MF_ROCKET )
2014-10-05 20:04:11 +00:00
P_LoadedModel ( mod ) ;
2008-11-09 22:29:28 +00:00
}
}
2004-08-23 00:15:46 +00:00
2015-04-21 04:12:00 +00:00
static void QDECL R_Grenadetrail_Callback ( struct cvar_s * var , char * oldvalue )
2008-11-09 22:29:28 +00:00
{
int i ;
model_t * mod ;
2014-08-25 07:35:41 +00:00
extern model_t * mod_known ;
2008-11-09 22:29:28 +00:00
extern int mod_numknown ;
2004-08-23 00:15:46 +00:00
2008-11-09 22:29:28 +00:00
if ( cls . state = = ca_disconnected )
return ; // don't bother parsing while disconnected
2004-08-23 00:15:46 +00:00
2008-11-09 22:29:28 +00:00
for ( i = 0 , mod = mod_known ; i < mod_numknown ; i + + , mod + + )
{
2014-10-05 20:04:11 +00:00
if ( mod - > loadstate = = MLS_LOADED )
2011-10-27 15:46:36 +00:00
if ( mod - > flags & MF_GRENADE )
2014-10-05 20:04:11 +00:00
P_LoadedModel ( mod ) ;
2008-11-09 22:29:28 +00:00
}
}
2005-03-10 03:55:18 +00:00
2013-03-12 23:09:25 +00:00
extern particleengine_t pe_null ;
# ifdef PSET_CLASSIC
extern particleengine_t pe_classic ;
# endif
2008-11-09 22:29:28 +00:00
particleengine_t pe_darkplaces ;
2010-07-11 02:22:39 +00:00
particleengine_t pe_qmb ;
2013-03-12 23:09:25 +00:00
# ifdef PSET_SCRIPT
extern particleengine_t pe_script ;
# endif
2004-08-23 00:15:46 +00:00
2008-11-09 22:29:28 +00:00
particleengine_t * particlesystem [ ] =
{
2013-03-12 23:09:25 +00:00
# ifdef PSET_SCRIPT
2008-11-09 22:29:28 +00:00
& pe_script ,
2013-03-12 23:09:25 +00:00
# endif
2008-11-09 22:29:28 +00:00
& pe_darkplaces ,
2010-07-11 02:22:39 +00:00
& pe_qmb ,
2013-03-12 23:09:25 +00:00
# ifdef PSET_CLASSIC
2008-11-09 22:29:28 +00:00
& pe_classic ,
2013-03-12 23:09:25 +00:00
# endif
2008-11-09 22:29:28 +00:00
& pe_null ,
2009-10-06 00:22:28 +00:00
NULL ,
2008-11-09 22:29:28 +00:00
} ;
2004-08-23 00:15:46 +00:00
2015-04-21 04:12:00 +00:00
static void QDECL R_ParticleSystem_Callback ( struct cvar_s * var , char * oldvalue )
2008-11-09 22:29:28 +00:00
{
int i ;
if ( pe )
2010-07-13 09:49:38 +00:00
{
2011-06-18 12:25:36 +00:00
CL_ClearTEntParticleState ( ) ;
CL_ClearLerpEntsParticleState ( ) ;
2013-10-08 14:28:11 +00:00
# ifdef Q2CLIENT
CLQ2_ClearParticleState ( ) ;
# endif
2011-06-18 12:25:36 +00:00
2008-11-09 22:29:28 +00:00
pe - > ShutdownParticles ( ) ;
2010-07-13 09:49:38 +00:00
}
2004-08-23 00:15:46 +00:00
2010-05-01 22:47:47 +00:00
if ( ! qrenderer )
2010-07-13 09:49:38 +00:00
{
2010-05-01 22:47:47 +00:00
pe = & pe_null ;
2010-07-13 09:49:38 +00:00
}
2010-05-01 22:47:47 +00:00
else
2008-11-09 22:29:28 +00:00
{
2010-05-01 22:47:47 +00:00
pe = NULL ;
for ( i = 0 ; particlesystem [ i ] ; i + + )
2008-11-09 22:29:28 +00:00
{
2010-05-01 22:47:47 +00:00
if ( ( particlesystem [ i ] - > name1 & & ! stricmp ( var - > string , particlesystem [ i ] - > name1 ) )
| | ( particlesystem [ i ] - > name2 & & ! stricmp ( var - > string , particlesystem [ i ] - > name2 ) ) )
{
2008-11-09 22:29:28 +00:00
pe = particlesystem [ i ] ;
2010-05-01 22:47:47 +00:00
break ;
}
if ( ! pe )
if ( particlesystem [ i ] - > name1 )
pe = particlesystem [ i ] ;
}
2008-11-09 22:29:28 +00:00
}
if ( ! pe )
Sys_Error ( " No particle system available. Please recompile. " ) ;
2005-05-30 04:34:47 +00:00
2010-07-11 02:22:39 +00:00
if ( ! pe - > InitParticles ( ) )
{
Con_Printf ( " Particlesystem %s failed to init \n " , pe - > name1 ) ;
pe = & pe_null ;
pe - > InitParticles ( ) ;
}
2008-11-09 22:29:28 +00:00
pe - > ClearParticles ( ) ;
CL_RegisterParticles ( ) ;
}
2004-08-23 00:15:46 +00:00
2010-07-11 02:22:39 +00:00
cvar_t r_rockettrail = CVARFC ( " r_rockettrail " , " 1 " , CVAR_SEMICHEAT , R_Rockettrail_Callback ) ;
cvar_t r_grenadetrail = CVARFC ( " r_grenadetrail " , " 1 " , CVAR_SEMICHEAT , R_Grenadetrail_Callback ) ;
2012-04-24 12:22:50 +00:00
cvar_t r_particlesystem = CVARFC ( " r_particlesystem " , IFMINIMAL ( " classic " , " script " ) , CVAR_SEMICHEAT | CVAR_ARCHIVE , R_ParticleSystem_Callback ) ;
cvar_t r_particledesc = CVARAF ( " r_particledesc " , " classic " , " r_particlesdesc " , CVAR_SEMICHEAT | CVAR_ARCHIVE ) ;
2004-08-23 00:15:46 +00:00
extern cvar_t r_bouncysparks ;
extern cvar_t r_part_rain ;
extern cvar_t r_bloodstains ;
2005-10-09 01:36:42 +00:00
extern cvar_t gl_part_flame ;
2011-12-26 15:19:13 +00:00
cvar_t r_part_rain_quantity = CVARF ( " r_part_rain_quantity " , " 1 " , CVAR_ARCHIVE ) ;
2004-08-23 00:15:46 +00:00
2011-12-26 15:19:13 +00:00
cvar_t r_particle_tracelimit = CVARFD ( " r_particle_tracelimit " , " 200 " , CVAR_ARCHIVE , " Number of traces to allow per frame for particle physics. " ) ;
2010-07-11 02:22:39 +00:00
cvar_t r_part_sparks = CVAR ( " r_part_sparks " , " 1 " ) ;
cvar_t r_part_sparks_trifan = CVAR ( " r_part_sparks_trifan " , " 1 " ) ;
cvar_t r_part_sparks_textured = CVAR ( " r_part_sparks_textured " , " 1 " ) ;
cvar_t r_part_beams = CVAR ( " r_part_beams " , " 1 " ) ;
2011-12-26 15:19:13 +00:00
cvar_t r_part_contentswitch = CVARFD ( " r_part_contentswitch " , " 1 " , CVAR_ARCHIVE , " Enable particle effects to change based on content (ex. water). " ) ;
2012-04-24 12:22:50 +00:00
cvar_t r_part_density = CVARF ( " r_part_density " , " 1 " , CVAR_ARCHIVE ) ;
2014-09-02 02:44:43 +00:00
cvar_t r_part_classic_expgrav = CVARFD ( " r_part_classic_expgrav " , " 10 " , CVAR_ARCHIVE , " Scaler for how fast classic explosion particles should accelerate due to gravity. 1 for like vanilla, 10 for like zquake. " ) ;
2004-09-07 18:21:08 +00:00
2004-08-23 00:15:46 +00:00
2008-11-09 22:29:28 +00:00
particleengine_t * pe ;
2005-09-08 22:52:46 +00:00
2008-11-09 22:29:28 +00:00
void P_InitParticleSystem ( void )
{
char * particlecvargroupname = " Particle effects " ;
2004-08-23 00:15:46 +00:00
2008-11-09 22:29:28 +00:00
Cvar_Register ( & r_particlesystem , " Particles " ) ;
2004-08-23 00:15:46 +00:00
2008-11-09 22:29:28 +00:00
//particles
2010-12-05 02:46:07 +00:00
Cvar_Register ( & r_particledesc , particlecvargroupname ) ;
2008-11-09 22:29:28 +00:00
Cvar_Register ( & r_bouncysparks , particlecvargroupname ) ;
Cvar_Register ( & r_part_rain , particlecvargroupname ) ;
2006-09-17 00:59:22 +00:00
2008-11-09 22:29:28 +00:00
Cvar_Register ( & r_part_rain_quantity , particlecvargroupname ) ;
2004-08-23 00:15:46 +00:00
2008-11-09 22:29:28 +00:00
Cvar_Register ( & r_particle_tracelimit , particlecvargroupname ) ;
Cvar_Register ( & r_part_sparks , particlecvargroupname ) ;
Cvar_Register ( & r_part_sparks_trifan , particlecvargroupname ) ;
Cvar_Register ( & r_part_sparks_textured , particlecvargroupname ) ;
Cvar_Register ( & r_part_beams , particlecvargroupname ) ;
Cvar_Register ( & r_part_contentswitch , particlecvargroupname ) ;
2012-04-24 07:59:11 +00:00
Cvar_Register ( & r_part_density , particlecvargroupname ) ;
2014-09-02 02:44:43 +00:00
Cvar_Register ( & r_part_classic_expgrav , particlecvargroupname ) ;
2008-11-09 22:29:28 +00:00
Cvar_Register ( & gl_part_flame , particlecvargroupname ) ;
2011-06-18 05:41:10 +00:00
Cvar_Register ( & r_rockettrail , particlecvargroupname ) ;
Cvar_Register ( & r_grenadetrail , particlecvargroupname ) ;
2015-03-03 00:14:43 +00:00
R_Clutter_Init ( ) ;
2004-08-23 00:15:46 +00:00
}
2011-01-30 01:32:30 +00:00
void P_Shutdown ( void )
{
if ( pe )
{
2014-04-06 15:16:39 +00:00
CL_ClearTEntParticleState ( ) ;
2013-03-12 23:21:14 +00:00
CL_ClearLerpEntsParticleState ( ) ;
2014-04-06 15:16:39 +00:00
# ifdef Q2CLIENT
CLQ2_ClearParticleState ( ) ;
# endif
2011-01-30 01:32:30 +00:00
pe - > ShutdownParticles ( ) ;
}
pe = NULL ;
2015-03-03 00:14:43 +00:00
R_Clutter_Purge ( ) ;
2011-01-30 01:32:30 +00:00
}
2014-12-25 02:32:22 +00:00
//0 says hit nothing.
//1 says hit world
//>1 says hit some entity
unsigned int TraceLineN ( vec3_t start , vec3_t end , vec3_t impact , vec3_t normal )
2004-08-23 00:15:46 +00:00
{
2008-11-09 22:29:28 +00:00
trace_t trace ;
float len , bestlen ;
2004-08-23 00:15:46 +00:00
int i ;
2008-11-09 22:29:28 +00:00
vec3_t delta , ts , te ;
physent_t * pe ;
2014-12-25 02:32:22 +00:00
int result = 0 ;
2010-08-28 17:14:38 +00:00
vec3_t axis [ 3 ] ;
2008-11-09 22:29:28 +00:00
memset ( & trace , 0 , sizeof ( trace ) ) ;
VectorSubtract ( end , start , delta ) ;
bestlen = Length ( delta ) ;
VectorCopy ( end , impact ) ;
2014-12-25 02:32:22 +00:00
for ( i = 0 ; i < pmove . numphysent ; i + + )
2004-08-23 00:15:46 +00:00
{
2008-11-09 22:29:28 +00:00
pe = & pmove . physents [ i ] ;
2009-03-03 01:52:30 +00:00
if ( pe - > nonsolid )
continue ;
2014-10-05 20:04:11 +00:00
if ( pe - > model & & pe - > model - > loadstate = = MLS_LOADED )
2008-11-09 22:29:28 +00:00
{
VectorSubtract ( start , pe - > origin , ts ) ;
VectorSubtract ( end , pe - > origin , te ) ;
2010-08-28 17:14:38 +00:00
if ( pe - > angles [ 0 ] | | pe - > angles [ 1 ] | | pe - > angles [ 2 ] )
{
AngleVectors ( pe - > angles , axis [ 0 ] , axis [ 1 ] , axis [ 2 ] ) ;
VectorNegate ( axis [ 1 ] , axis [ 1 ] ) ;
2014-09-17 03:04:08 +00:00
pe - > model - > funcs . NativeTrace ( pe - > model , 0 , 0 , axis , ts , te , vec3_origin , vec3_origin , false , MASK_WORLDSOLID , & trace ) ;
2010-08-28 17:14:38 +00:00
}
else
2014-09-17 03:04:08 +00:00
pe - > model - > funcs . NativeTrace ( pe - > model , 0 , 0 , NULL , ts , te , vec3_origin , vec3_origin , false , MASK_WORLDSOLID , & trace ) ;
2008-11-09 22:29:28 +00:00
if ( trace . fraction < 1 )
{
VectorSubtract ( trace . endpos , ts , delta ) ;
len = Length ( delta ) ;
if ( len < bestlen )
{
bestlen = len ;
2010-07-11 02:22:39 +00:00
if ( normal )
VectorCopy ( trace . plane . normal , normal ) ;
2008-11-09 22:29:28 +00:00
VectorAdd ( pe - > origin , trace . endpos , impact ) ;
}
2004-08-23 00:15:46 +00:00
2014-12-25 02:32:22 +00:00
result = pe - > info + 1 ;
2008-11-09 22:29:28 +00:00
}
if ( trace . startsolid )
{
VectorNormalize ( delta ) ;
2010-07-11 02:22:39 +00:00
if ( normal )
{
normal [ 0 ] = - delta [ 0 ] ;
normal [ 1 ] = - delta [ 1 ] ;
normal [ 2 ] = - delta [ 2 ] ;
}
2012-01-17 07:57:46 +00:00
VectorCopy ( end , impact ) ;
return false ;
2008-11-09 22:29:28 +00:00
}
2004-08-23 00:15:46 +00:00
2008-11-09 22:29:28 +00:00
}
}
2014-12-25 02:32:22 +00:00
return result ;
2005-03-10 03:55:18 +00:00
}
2008-11-09 22:29:28 +00:00
//handy utility...
void P_EmitEffect ( vec3_t pos , int type , trailstate_t * * tsk )
2005-09-08 22:52:46 +00:00
{
2008-11-09 22:29:28 +00:00
if ( cl . paused )
return ;
2009-11-17 00:15:44 +00:00
pe - > RunParticleEffectState ( pos , NULL , ( ( host_frametime > 0.1 ) ? 0.1 : host_frametime ) , type , tsk ) ;
2005-09-08 22:52:46 +00:00
}
2004-08-23 00:15:46 +00:00
2008-11-09 22:29:28 +00:00
// P_SelectableTrail: given default/opposite effects, model pointer, and a user selection cvar
// changes model to the appropriate trail effect and default trail index
2014-10-05 20:04:11 +00:00
static void P_SelectableTrail ( int * trailid , int * trailpalidx , cvar_t * selection , int mdleffect , int mdlcidx , int oppeffect , int oppcidx )
2005-05-17 02:36:54 +00:00
{
2008-11-09 22:29:28 +00:00
int select = ( int ) ( selection - > value ) ;
switch ( select )
2005-05-17 02:36:54 +00:00
{
2008-11-09 22:29:28 +00:00
case 0 : // check for string, otherwise no trail
if ( selection - > string [ 0 ] = = ' 0 ' )
2007-05-25 22:16:29 +00:00
{
2014-10-05 20:04:11 +00:00
* trailid = P_INVALID ;
* trailpalidx = - 1 ;
2008-11-09 22:29:28 +00:00
break ;
2007-05-25 22:16:29 +00:00
}
else
2005-05-17 02:36:54 +00:00
{
2008-11-09 22:29:28 +00:00
int effect = P_FindParticleType ( selection - > string ) ;
2005-05-17 02:36:54 +00:00
2008-11-09 22:29:28 +00:00
if ( effect > = 0 )
2007-05-25 22:16:29 +00:00
{
2014-10-05 20:04:11 +00:00
* trailid = effect ;
* trailpalidx = mdlcidx ;
2008-11-09 22:29:28 +00:00
break ;
2007-05-25 22:16:29 +00:00
}
2005-05-17 02:36:54 +00:00
}
2008-11-09 22:29:28 +00:00
// fall through to default (so semicheat will work properly)
case 1 : // default model effect
2007-05-25 22:16:29 +00:00
default :
2014-10-05 20:04:11 +00:00
* trailid = mdleffect ;
* trailpalidx = mdlcidx ;
2008-11-09 22:29:28 +00:00
break ;
case 2 : // opposite effect
2014-10-05 20:04:11 +00:00
* trailid = oppeffect ;
* trailpalidx = oppcidx ;
2008-11-09 22:29:28 +00:00
break ;
case 3 : // alt rocket effect
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " TR_ALTROCKET " ) ;
* trailpalidx = 107 ;
2008-11-09 22:29:28 +00:00
break ;
case 4 : // gib
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " TR_BLOOD " ) ;
* trailpalidx = 70 ;
2008-11-09 22:29:28 +00:00
break ;
case 5 : // zombie gib
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " TR_SLIGHTBLOOD " ) ;
* trailpalidx = 70 ;
2008-11-09 22:29:28 +00:00
break ;
case 6 : // Scrag tracer
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " TR_WIZSPIKE " ) ;
* trailpalidx = 60 ;
2008-11-09 22:29:28 +00:00
break ;
case 7 : // Knight tracer
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " TR_KNIGHTSPIKE " ) ;
* trailpalidx = 238 ;
2008-11-09 22:29:28 +00:00
break ;
case 8 : // Vore tracer
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " TR_VORESPIKE " ) ;
* trailpalidx = 154 ;
2008-11-09 22:29:28 +00:00
break ;
case 9 : // rail trail
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " TE_RAILTRAIL " ) ;
* trailpalidx = 15 ;
2007-05-25 22:16:29 +00:00
break ;
2005-05-17 02:36:54 +00:00
}
}
2004-08-23 00:15:46 +00:00
2008-11-09 22:29:28 +00:00
//figure out which particle trail to use for the given model, filling in its values as required.
2015-03-03 07:54:10 +00:00
void P_DefaultTrail ( unsigned int entityeffects , unsigned int modelflags , int * trailid , int * trailpalidx )
2008-11-09 22:29:28 +00:00
{
// TODO: EF_BRIGHTFIELD should probably be handled in here somewhere
// TODO: make trail default color into RGB values instead of indexes
2014-04-29 02:29:04 +00:00
if ( ! pe )
return ;
2004-08-23 00:15:46 +00:00
2015-03-03 07:54:10 +00:00
if ( entityeffects & EF_BRIGHTFIELD )
{
* trailid = P_FindParticleType ( " EF_BRIGHTFIELD " ) ;
* trailpalidx = 70 ;
}
else if ( entityeffects & DPEF_FLAME )
{
* trailid = P_FindParticleType ( " EF_FLAME " ) ;
* trailpalidx = 70 ;
}
else if ( entityeffects & DPEF_STARDUST )
{
* trailid = P_FindParticleType ( " EF_STARDUST " ) ;
* trailpalidx = 70 ;
}
else if ( modelflags & MF_ROCKET )
2014-10-05 20:04:11 +00:00
P_SelectableTrail ( trailid , trailpalidx , & r_rockettrail , P_FindParticleType ( " TR_ROCKET " ) , 109 , P_FindParticleType ( " TR_GRENADE " ) , 6 ) ;
else if ( modelflags & MF_GRENADE )
P_SelectableTrail ( trailid , trailpalidx , & r_grenadetrail , P_FindParticleType ( " TR_GRENADE " ) , 6 , P_FindParticleType ( " TR_ROCKET " ) , 109 ) ;
else if ( modelflags & MF_GIB )
{
* trailid = P_FindParticleType ( " TR_BLOOD " ) ;
* trailpalidx = 70 ;
}
else if ( modelflags & MF_TRACER )
2004-08-23 00:15:46 +00:00
{
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " TR_WIZSPIKE " ) ;
* trailpalidx = 60 ;
2004-08-23 00:15:46 +00:00
}
2014-10-05 20:04:11 +00:00
else if ( modelflags & MF_ZOMGIB )
2004-08-23 00:15:46 +00:00
{
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " TR_SLIGHTBLOOD " ) ;
* trailpalidx = 70 ;
2004-08-23 00:15:46 +00:00
}
2014-10-05 20:04:11 +00:00
else if ( modelflags & MF_TRACER2 )
2005-01-04 19:19:51 +00:00
{
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " TR_KNIGHTSPIKE " ) ;
* trailpalidx = 238 ;
2005-01-04 19:19:51 +00:00
}
2014-10-05 20:04:11 +00:00
else if ( modelflags & MF_TRACER3 )
2005-01-04 19:19:51 +00:00
{
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " TR_VORESPIKE " ) ;
* trailpalidx = 154 ;
2005-01-04 19:19:51 +00:00
}
2014-10-05 20:04:11 +00:00
# ifdef HEXEN2
else if ( modelflags & MFH2_BLOODSHOT ) //these are the hexen2 ones.
2006-08-20 00:47:13 +00:00
{
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " tr_bloodshot " ) ;
* trailpalidx = 136 ;
2006-08-20 00:47:13 +00:00
}
2014-10-05 20:04:11 +00:00
else if ( modelflags & MFH2_FIREBALL )
2004-08-23 00:15:46 +00:00
{
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " tr_fireball " ) ;
* trailpalidx = 424 ;
2004-08-23 00:15:46 +00:00
}
2014-10-05 20:04:11 +00:00
else if ( modelflags & MFH2_ACIDBALL )
2005-06-22 17:10:13 +00:00
{
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " tr_acidball " ) ;
* trailpalidx = 440 ;
2005-06-22 17:10:13 +00:00
}
2014-10-05 20:04:11 +00:00
else if ( modelflags & MFH2_ICE )
2004-08-23 00:15:46 +00:00
{
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " tr_ice " ) ;
* trailpalidx = 408 ;
2004-08-23 00:15:46 +00:00
}
2014-10-05 20:04:11 +00:00
else if ( modelflags & MFH2_SPIT )
2004-08-23 00:15:46 +00:00
{
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " tr_spit " ) ;
* trailpalidx = 260 ;
2004-08-23 00:15:46 +00:00
}
2014-10-05 20:04:11 +00:00
else if ( modelflags & MFH2_SPELL )
2004-08-23 00:15:46 +00:00
{
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " tr_spell " ) ;
* trailpalidx = 260 ;
2004-08-23 00:15:46 +00:00
}
2014-10-05 20:04:11 +00:00
else if ( modelflags & MFH2_VORP_MISSILE )
2004-08-23 00:15:46 +00:00
{
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " tr_vorpmissile " ) ;
* trailpalidx = 302 ;
2004-08-23 00:15:46 +00:00
}
2014-10-05 20:04:11 +00:00
else if ( modelflags & MFH2_SET_STAFF )
2004-08-23 00:15:46 +00:00
{
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " tr_setstaff " ) ;
* trailpalidx = 424 ;
2004-08-23 00:15:46 +00:00
}
2014-10-05 20:04:11 +00:00
else if ( modelflags & MFH2_MAGICMISSILE )
2005-11-26 23:11:19 +00:00
{
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " tr_magicmissile " ) ;
* trailpalidx = 149 ;
2005-11-26 23:11:19 +00:00
}
2014-10-05 20:04:11 +00:00
else if ( modelflags & MFH2_BONESHARD )
2005-11-26 23:11:19 +00:00
{
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " tr_boneshard " ) ;
* trailpalidx = 384 ;
2005-11-26 23:11:19 +00:00
}
2014-10-05 20:04:11 +00:00
else if ( modelflags & MFH2_SCARAB )
2005-11-26 23:11:19 +00:00
{
2014-10-05 20:04:11 +00:00
* trailid = P_FindParticleType ( " tr_scarab " ) ;
* trailpalidx = 254 ;
2005-11-26 23:11:19 +00:00
}
2014-10-05 20:04:11 +00:00
else if ( modelflags & MFH2_ROCKET )
2005-11-26 23:11:19 +00:00
{
2014-10-05 20:04:11 +00:00
//spiders
* trailid = P_FindParticleType ( " TR_GREENBLOOD " ) ;
* trailpalidx = 70 ; //fixme
2005-11-26 23:11:19 +00:00
}
2014-10-05 20:04:11 +00:00
# endif
2004-08-23 00:15:46 +00:00
else
2011-06-18 12:25:36 +00:00
{
2014-10-05 20:04:11 +00:00
* trailid = P_INVALID ;
* trailpalidx = - 1 ;
2011-06-18 12:25:36 +00:00
}
2004-08-23 00:15:46 +00:00
}