Fixed particles for Prey.

This commit is contained in:
Justin Marshall
2026-05-07 14:00:07 -07:00
parent 469be38dbe
commit 13e58aba52
2 changed files with 463 additions and 162 deletions
+393 -117
View File
@@ -171,246 +171,459 @@ void idDeclParticle::ParseParms( idLexer &src, float *parms, int maxParms ) {
} }
} }
/*
================
idDeclParticle::IsParticleStageToken
================
*/
static bool IsParticleStageToken(const idToken& token) {
if (!token.Icmp("}")) {
return true;
}
if (!token.Icmp("{")) {
return true;
}
if (!token.Icmp("material")) {
return true;
}
if (!token.Icmp("count")) {
return true;
}
if (!token.Icmp("time")) {
return true;
}
if (!token.Icmp("cycles")) {
return true;
}
if (!token.Icmp("timeOffset")) {
return true;
}
if (!token.Icmp("deadTime")) {
return true;
}
if (!token.Icmp("randomDistribution")) {
return true;
}
if (!token.Icmp("bunching")) {
return true;
}
if (!token.Icmp("distribution")) {
return true;
}
if (!token.Icmp("direction")) {
return true;
}
if (!token.Icmp("orientation")) {
return true;
}
if (!token.Icmp("customPath")) {
return true;
}
if (!token.Icmp("speed")) {
return true;
}
if (!token.Icmp("rotation")) {
return true;
}
if (!token.Icmp("angle")) {
return true;
}
if (!token.Icmp("entityColor")) {
return true;
}
if (!token.Icmp("size")) {
return true;
}
if (!token.Icmp("aspect")) {
return true;
}
if (!token.Icmp("fadeIn")) {
return true;
}
if (!token.Icmp("fadeOut")) {
return true;
}
if (!token.Icmp("fadeIndex")) {
return true;
}
if (!token.Icmp("color")) {
return true;
}
if (!token.Icmp("fadeColor")) {
return true;
}
if (!token.Icmp("offset")) {
return true;
}
if (!token.Icmp("animationFrames")) {
return true;
}
if (!token.Icmp("animationRate")) {
return true;
}
if (!token.Icmp("boundsExpansion")) {
return true;
}
if (!token.Icmp("gravity")) {
return true;
}
#ifdef PREY
if (!token.Icmp("lowSkippable")) {
return true;
}
#endif
return false;
}
/*
================
idDeclParticle::ReadParticleParmFloat
================
*/
static bool ReadParticleParmFloat(idLexer& src, float& value) {
idToken token;
if (!src.ReadToken(&token)) {
src.Error("missing particle parm value");
return false;
}
if (!token.Cmp("-")) {
idToken token2;
if (!src.ReadToken(&token2)) {
src.Error("no number after minus sign");
return false;
}
token = "-" + token2;
}
token.StripQuotes();
value = atof(token.c_str());
return true;
}
/* /*
================ ================
idDeclParticle::ParseParametric idDeclParticle::ParseParametric
================ ================
*/ */
void idDeclParticle::ParseParametric( idLexer &src, idParticleParm *parm ) { void idDeclParticle::ParseParametric(idLexer& src, idParticleParm* parm) {
idToken token; idToken token;
parm->table = NULL; parm->table = NULL;
parm->from = parm->to = 0.0f; parm->from = 0.0f;
parm->to = 0.0f;
if ( !src.ReadToken( &token ) ) { #ifdef PREY
src.Error( "not enough parameters" ); parm->randomp = 0.0f;
#endif
//
// First token can be:
// 10
// -10
// table sometable
//
if (!src.ReadToken(&token)) {
src.Error("missing particle parm");
return; return;
} }
if ( token.IsNumeric() ) { if (!token.Cmp("-")) {
// can have a to + 2nd parm idToken token2;
parm->from = parm->to = atof( token );
if ( src.ReadToken( &token ) ) { if (!src.ReadToken(&token2)) {
if ( !token.Icmp( "to" ) ) { src.Error("no number after minus sign");
if ( !src.ReadToken( &token ) ) { return;
src.Error( "missing second parameter" );
return;
}
parm->to = atof( token );
} else {
src.UnreadToken( &token );
}
} }
} else {
// table token = "-" + token2;
parm->table = static_cast<const idDeclTable *>( declManager->FindType( DECL_TABLE, token, false ) );
} }
} token.StripQuotes();
if (!token.Icmp("table")) {
if (!src.ReadToken(&token)) {
src.Error("missing table name");
return;
}
token.StripQuotes();
parm->table = static_cast<const idDeclTable*>(declManager->FindType(DECL_TABLE, token.c_str(), false));
parm->from = 0.0f;
parm->to = 1.0f;
}
else {
parm->from = atof(token.c_str());
parm->to = parm->from;
}
//
// Prey allows:
// size 1.000 to 2.000 with 0.500
// and also broken/exported formatting:
// size 1.000
// with 0.500
//
while (src.ReadToken(&token)) {
if (IsParticleStageToken(token)) {
src.UnreadToken(&token);
break;
}
if (!token.Icmp("to")) {
if (!ReadParticleParmFloat(src, parm->to)) {
return;
}
continue;
}
#ifdef PREY
if (!token.Icmp("with")) {
if (!ReadParticleParmFloat(src, parm->randomp)) {
return;
}
continue;
}
#endif
//
// If some other token appears, do not eat the rest of the stage.
// Put it back so the normal stage parser can report it correctly.
//
src.UnreadToken(&token);
break;
}
}
/* /*
================ ================
idDeclParticle::ParseParticleStage idDeclParticle::ParseParticleStage
================ ================
*/ */
idParticleStage *idDeclParticle::ParseParticleStage( idLexer &src ) { idParticleStage* idDeclParticle::ParseParticleStage(idLexer& src) {
idToken token; idToken token;
idParticleStage *stage = new idParticleStage; idParticleStage* stage = new idParticleStage;
stage->Default(); stage->Default();
while (1) { while (1) {
if ( src.HadError() ) { if (src.HadError()) {
break; break;
} }
if ( !src.ReadToken( &token ) ) { if (!src.ReadToken(&token)) {
break; break;
} }
if ( !token.Icmp( "}" ) ) { if (!token.Icmp("}")) {
break; break;
} }
if ( !token.Icmp( "material" ) ) { if (!token.Icmp("material")) {
src.ReadToken( &token ); src.ReadToken(&token);
stage->material = declManager->FindMaterial( token.c_str() ); stage->material = declManager->FindMaterial(token.c_str());
continue; continue;
} }
if ( !token.Icmp( "count" ) ) { if (!token.Icmp("count")) {
stage->totalParticles = src.ParseInt(); stage->totalParticles = src.ParseInt();
continue; continue;
} }
if ( !token.Icmp( "time" ) ) { if (!token.Icmp("time")) {
stage->particleLife = src.ParseFloat(); stage->particleLife = src.ParseFloat();
continue; continue;
} }
if ( !token.Icmp( "cycles" ) ) { if (!token.Icmp("cycles")) {
stage->cycles = src.ParseFloat(); stage->cycles = src.ParseFloat();
continue; continue;
} }
if ( !token.Icmp( "timeOffset" ) ) { if (!token.Icmp("timeOffset")) {
stage->timeOffset = src.ParseFloat(); stage->timeOffset = src.ParseFloat();
continue; continue;
} }
if ( !token.Icmp( "deadTime" ) ) { if (!token.Icmp("deadTime")) {
stage->deadTime = src.ParseFloat(); stage->deadTime = src.ParseFloat();
continue; continue;
} }
if ( !token.Icmp( "randomDistribution" ) ) { if (!token.Icmp("randomDistribution")) {
stage->randomDistribution = src.ParseBool(); stage->randomDistribution = src.ParseBool();
continue; continue;
} }
if ( !token.Icmp( "bunching" ) ) { if (!token.Icmp("bunching")) {
stage->spawnBunching = src.ParseFloat(); stage->spawnBunching = src.ParseFloat();
continue; continue;
} }
if ( !token.Icmp( "distribution" ) ) { if (!token.Icmp("distribution")) {
src.ReadToken( &token ); src.ReadToken(&token);
if ( !token.Icmp( "rect" ) ) { if (!token.Icmp("rect")) {
stage->distributionType = PDIST_RECT; stage->distributionType = PDIST_RECT;
} else if ( !token.Icmp( "cylinder" ) ) { }
else if (!token.Icmp("cylinder")) {
stage->distributionType = PDIST_CYLINDER; stage->distributionType = PDIST_CYLINDER;
} else if ( !token.Icmp( "sphere" ) ) { }
else if (!token.Icmp("sphere")) {
stage->distributionType = PDIST_SPHERE; stage->distributionType = PDIST_SPHERE;
} else {
src.Error( "bad distribution type: %s\n", token.c_str() );
} }
ParseParms( src, stage->distributionParms, sizeof( stage->distributionParms ) / sizeof( stage->distributionParms[0] ) ); else {
src.Error("bad distribution type: %s\n", token.c_str());
}
ParseParms(src, stage->distributionParms, sizeof(stage->distributionParms) / sizeof(stage->distributionParms[0]));
continue; continue;
} }
if ( !token.Icmp( "direction" ) ) { if (!token.Icmp("direction")) {
src.ReadToken( &token ); src.ReadToken(&token);
if ( !token.Icmp( "cone" ) ) { if (!token.Icmp("cone")) {
stage->directionType = PDIR_CONE; stage->directionType = PDIR_CONE;
} else if ( !token.Icmp( "outward" ) ) { }
else if (!token.Icmp("outward")) {
stage->directionType = PDIR_OUTWARD; stage->directionType = PDIR_OUTWARD;
} else {
src.Error( "bad direction type: %s\n", token.c_str() );
} }
ParseParms( src, stage->directionParms, sizeof( stage->directionParms ) / sizeof( stage->directionParms[0] ) ); else {
src.Error("bad direction type: %s\n", token.c_str());
}
ParseParms(src, stage->directionParms, sizeof(stage->directionParms) / sizeof(stage->directionParms[0]));
continue; continue;
} }
if ( !token.Icmp( "orientation" ) ) { if (!token.Icmp("orientation")) {
src.ReadToken( &token ); src.ReadToken(&token);
if ( !token.Icmp( "view" ) ) { if (!token.Icmp("view")) {
stage->orientation = POR_VIEW; stage->orientation = POR_VIEW;
} else if ( !token.Icmp( "aimed" ) ) { }
else if (!token.Icmp("aimed")) {
stage->orientation = POR_AIMED; stage->orientation = POR_AIMED;
} else if ( !token.Icmp( "x" ) ) { }
else if (!token.Icmp("x")) {
stage->orientation = POR_X; stage->orientation = POR_X;
} else if ( !token.Icmp( "y" ) ) { }
else if (!token.Icmp("y")) {
stage->orientation = POR_Y; stage->orientation = POR_Y;
} else if ( !token.Icmp( "z" ) ) { }
else if (!token.Icmp("z")) {
stage->orientation = POR_Z; stage->orientation = POR_Z;
} else {
src.Error( "bad orientation type: %s\n", token.c_str() );
} }
ParseParms( src, stage->orientationParms, sizeof( stage->orientationParms ) / sizeof( stage->orientationParms[0] ) ); else {
src.Error("bad orientation type: %s\n", token.c_str());
}
ParseParms(src, stage->orientationParms, sizeof(stage->orientationParms) / sizeof(stage->orientationParms[0]));
continue; continue;
} }
if ( !token.Icmp( "customPath" ) ) { if (!token.Icmp("customPath")) {
src.ReadToken( &token ); src.ReadToken(&token);
if ( !token.Icmp( "standard" ) ) { if (!token.Icmp("standard")) {
stage->customPathType = PPATH_STANDARD; stage->customPathType = PPATH_STANDARD;
} else if ( !token.Icmp( "helix" ) ) {
stage->customPathType = PPATH_HELIX;
} else if ( !token.Icmp( "flies" ) ) {
stage->customPathType = PPATH_FLIES;
} else if ( !token.Icmp( "spherical" ) ) {
stage->customPathType = PPATH_ORBIT;
} else {
src.Error( "bad path type: %s\n", token.c_str() );
} }
ParseParms( src, stage->customPathParms, sizeof( stage->customPathParms ) / sizeof( stage->customPathParms[0] ) ); else if (!token.Icmp("helix")) {
stage->customPathType = PPATH_HELIX;
}
else if (!token.Icmp("flies")) {
stage->customPathType = PPATH_FLIES;
}
else if (!token.Icmp("spherical")) {
stage->customPathType = PPATH_ORBIT;
}
else {
src.Error("bad path type: %s\n", token.c_str());
}
ParseParms(src, stage->customPathParms, sizeof(stage->customPathParms) / sizeof(stage->customPathParms[0]));
continue; continue;
} }
if ( !token.Icmp( "speed" ) ) { if (!token.Icmp("speed")) {
ParseParametric( src, &stage->speed ); ParseParametric(src, &stage->speed);
continue; continue;
} }
if ( !token.Icmp( "rotation" ) ) { if (!token.Icmp("rotation")) {
ParseParametric( src, &stage->rotationSpeed ); ParseParametric(src, &stage->rotationSpeed);
continue; continue;
} }
if ( !token.Icmp( "angle" ) ) { if (!token.Icmp("angle")) {
stage->initialAngle = src.ParseFloat(); stage->initialAngle = src.ParseFloat();
continue; continue;
} }
if ( !token.Icmp( "entityColor" ) ) { if (!token.Icmp("entityColor")) {
stage->entityColor = src.ParseBool(); stage->entityColor = src.ParseBool();
continue; continue;
} }
if ( !token.Icmp( "size" ) ) { if (!token.Icmp("size")) {
ParseParametric( src, &stage->size ); ParseParametric(src, &stage->size);
continue; continue;
} }
if ( !token.Icmp( "aspect" ) ) { if (!token.Icmp("aspect")) {
ParseParametric( src, &stage->aspect ); ParseParametric(src, &stage->aspect);
continue; continue;
} }
if ( !token.Icmp( "fadeIn" ) ) { if (!token.Icmp("fadeIn")) {
stage->fadeInFraction = src.ParseFloat(); stage->fadeInFraction = src.ParseFloat();
continue; continue;
} }
if ( !token.Icmp( "fadeOut" ) ) { if (!token.Icmp("fadeOut")) {
stage->fadeOutFraction = src.ParseFloat(); stage->fadeOutFraction = src.ParseFloat();
continue; continue;
} }
if ( !token.Icmp( "fadeIndex" ) ) { if (!token.Icmp("fadeIndex")) {
stage->fadeIndexFraction = src.ParseFloat(); stage->fadeIndexFraction = src.ParseFloat();
continue; continue;
} }
if ( !token.Icmp( "color" ) ) { if (!token.Icmp("color")) {
stage->color[0] = src.ParseFloat(); stage->color[0] = src.ParseFloat();
stage->color[1] = src.ParseFloat(); stage->color[1] = src.ParseFloat();
stage->color[2] = src.ParseFloat(); stage->color[2] = src.ParseFloat();
stage->color[3] = src.ParseFloat(); stage->color[3] = src.ParseFloat();
continue; continue;
} }
if ( !token.Icmp( "fadeColor" ) ) { if (!token.Icmp("fadeColor")) {
stage->fadeColor[0] = src.ParseFloat(); stage->fadeColor[0] = src.ParseFloat();
stage->fadeColor[1] = src.ParseFloat(); stage->fadeColor[1] = src.ParseFloat();
stage->fadeColor[2] = src.ParseFloat(); stage->fadeColor[2] = src.ParseFloat();
stage->fadeColor[3] = src.ParseFloat(); stage->fadeColor[3] = src.ParseFloat();
continue; continue;
} }
if ( !token.Icmp("offset" ) ) { if (!token.Icmp("offset")) {
stage->offset[0] = src.ParseFloat(); stage->offset[0] = src.ParseFloat();
stage->offset[1] = src.ParseFloat(); stage->offset[1] = src.ParseFloat();
stage->offset[2] = src.ParseFloat(); stage->offset[2] = src.ParseFloat();
continue; continue;
} }
if ( !token.Icmp( "animationFrames" ) ) { if (!token.Icmp("animationFrames")) {
stage->animationFrames = src.ParseInt(); stage->animationFrames = src.ParseInt();
continue; continue;
} }
if ( !token.Icmp( "animationRate" ) ) { if (!token.Icmp("animationRate")) {
stage->animationRate = src.ParseFloat(); stage->animationRate = src.ParseFloat();
continue; continue;
} }
if ( !token.Icmp( "boundsExpansion" ) ) { if (!token.Icmp("boundsExpansion")) {
stage->boundsExpansion = src.ParseFloat(); stage->boundsExpansion = src.ParseFloat();
continue; continue;
} }
if ( !token.Icmp( "gravity" ) ) { if (!token.Icmp("gravity")) {
src.ReadToken( &token ); src.ReadToken(&token);
if ( !token.Icmp( "world" ) ) { if (!token.Icmp("world")) {
stage->worldGravity = true; stage->worldGravity = true;
} else { }
src.UnreadToken( &token ); else {
src.UnreadToken(&token);
} }
stage->gravity = src.ParseFloat(); stage->gravity = src.ParseFloat();
continue; continue;
} }
src.Error( "unknown token %s\n", token.c_str() ); #ifdef PREY
if (!token.Icmp("lowSkippable")) {
stage->lowSkippable = src.ParseBool();
continue;
}
#endif
src.Error("unknown token %s\n", token.c_str());
} }
// derive values // derive values
stage->cycleMsec = ( stage->particleLife + stage->deadTime ) * 1000; stage->cycleMsec = (stage->particleLife + stage->deadTime) * 1000;
return stage; return stage;
} }
@@ -420,62 +633,122 @@ idParticleStage *idDeclParticle::ParseParticleStage( idLexer &src ) {
idDeclParticle::Parse idDeclParticle::Parse
================ ================
*/ */
bool idDeclParticle::Parse( const char *text, const int textLength ) { bool idDeclParticle::Parse(const char* text, const int textLength) {
idLexer src; idLexer src;
idToken token; idToken token;
src.LoadMemory( text, textLength, GetFileName(), GetLineNum() ); src.LoadMemory(text, textLength, GetFileName(), GetLineNum());
src.SetFlags( DECL_LEXER_FLAGS ); src.SetFlags(DECL_LEXER_FLAGS);
src.SkipUntilString( "{" ); src.SkipUntilString("{");
depthHack = 0.0f; depthHack = 0.0f;
#ifdef PREY
bool boundsSpecified = false;
bounds.Clear();
#endif
while (1) { while (1) {
if ( !src.ReadToken( &token ) ) { if (!src.ReadToken(&token)) {
break; break;
} }
if ( !token.Icmp( "}" ) ) { if (!token.Icmp("}")) {
break; break;
} }
if ( !token.Icmp( "{" ) ) { if (!token.Icmp("{")) {
idParticleStage *stage = ParseParticleStage( src ); idParticleStage* stage = ParseParticleStage(src);
if ( !stage ) { if (!stage) {
src.Warning( "Particle stage parse failed" ); src.Warning("Particle stage parse failed");
MakeDefault(); MakeDefault();
return false; return false;
} }
stages.Append( stage ); stages.Append(stage);
continue; continue;
} }
if ( !token.Icmp( "depthHack" ) ) { if (!token.Icmp("depthHack")) {
depthHack = src.ParseFloat(); depthHack = src.ParseFloat();
continue; continue;
} }
src.Warning( "bad token %s", token.c_str() ); #ifdef PREY
if (!token.Icmp("bounds")) {
bounds[0][0] = src.ParseFloat();
bounds[0][1] = src.ParseFloat();
bounds[0][2] = src.ParseFloat();
bounds[1][0] = src.ParseFloat();
bounds[1][1] = src.ParseFloat();
bounds[1][2] = src.ParseFloat();
boundsSpecified = true;
continue;
}
#endif
src.Warning("bad token %s", token.c_str());
MakeDefault(); MakeDefault();
return false; return false;
} }
#ifdef PREY
//
// Prey declarations may carry explicit bounds.
// If no bounds were present, or the cvar asks for recalculation,
// recalculate them and rebuild the source text with a bounds line.
//
if (!boundsSpecified || cvarSystem->GetCVarBool("r_recalcParticleBounds")) {
CalcBounds();
RebuildTextSource();
}
#else
// //
// calculate the bounds // calculate the bounds
// //
bounds.Clear(); bounds.Clear();
for( int i = 0; i < stages.Num(); i++ ) { for (int i = 0; i < stages.Num(); i++) {
GetStageBounds( stages[i] ); GetStageBounds(stages[i]);
bounds.AddBounds( stages[i]->bounds ); bounds.AddBounds(stages[i]->bounds);
} }
if ( bounds.GetVolume() <= 0.1f ) { if (bounds.GetVolume() <= 0.1f) {
bounds = idBounds( vec3_origin ).Expand( 8.0f ); bounds = idBounds(vec3_origin).Expand(8.0f);
} }
#endif
return true; return true;
} }
#ifdef PREY
/*
================
idDeclParticle::CalcBounds
HUMANHEAD bjk
================
*/
void idDeclParticle::CalcBounds(void) {
bounds.Clear();
for (int i = 0; i < stages.Num(); i++) {
#ifdef PARTICLE_BOUNDS
idDrawVert particleVerts[MAX_PARTICLES * 4]; // see note below
GetStageBounds(stages[i], particleVerts);
#else
GetStageBounds(stages[i]);
#endif
bounds.AddBounds(stages[i]->bounds);
}
if (bounds.GetVolume() <= 0.1f) {
bounds = idBounds(vec3_origin).Expand(8.0f);
}
}
#endif
/* /*
================ ================
idDeclParticle::FreeData idDeclParticle::FreeData
@@ -744,6 +1017,9 @@ void idParticleStage::Default() {
spawnBunching = 1.0f; spawnBunching = 1.0f;
particleLife = 1.5f; particleLife = 1.5f;
timeOffset = 0.0f; timeOffset = 0.0f;
#ifdef PREY
lowSkippable = false;
#endif
deadTime = 0.0f; deadTime = 0.0f;
distributionType = PDIST_RECT; distributionType = PDIST_RECT;
distributionParms[0] = 8.0f; distributionParms[0] = 8.0f;
+70 -45
View File
@@ -39,14 +39,23 @@ If you have questions concerning this license or the applicable additional terms
class idParticleParm { class idParticleParm {
public: public:
idParticleParm( void ) { table = NULL; from = to = 0.0f; } idParticleParm(void) {
table = NULL;
from = to = 0.0f;
#ifdef PREY
randomp = 0.0f;
#endif
}
const idDeclTable * table; const idDeclTable* table;
float from; float from;
float to; float to;
#ifdef PREY
float Eval( float frac, idRandom &rand ) const; float randomp; // HUMANHEAD bjk
float Integrate( float frac, idRandom &rand ) const; #endif
float Eval(float frac, idRandom& rand) const;
float Integrate(float frac, idRandom& rand) const;
}; };
@@ -83,15 +92,14 @@ typedef struct renderEntity_s renderEntity_t;
typedef struct renderView_s renderView_t; typedef struct renderView_s renderView_t;
typedef struct { typedef struct {
const renderEntity_t * renderEnt; // for shaderParms, etc const renderEntity_t* renderEnt; // for shaderParms, etc
const renderView_t * renderView; const renderView_t* renderView;
int index; // particle number in the system int index; // particle number in the system
float frac; // 0.0 to 1.0 float frac; // 0.0 to 1.0
idRandom random; idRandom random;
idVec3 origin; // dynamic smoke particles can have individual origins and axis idVec3 origin; // dynamic smoke particles can have individual origins and axis
idMat3 axis; idMat3 axis;
float age; // in seconds, calculated as fraction * stage->particleLife float age; // in seconds, calculated as fraction * stage->particleLife
idRandom originalRandom; // needed so aimed particles can reset the random for another origin calculation idRandom originalRandom; // needed so aimed particles can reset the random for another origin calculation
float animationFrameFrac; // set by ParticleTexCoords, used to make the cross faded version float animationFrameFrac; // set by ParticleTexCoords, used to make the cross faded version
@@ -103,33 +111,33 @@ typedef struct {
// //
class idParticleStage { class idParticleStage {
public: public:
idParticleStage( void ); idParticleStage(void);
virtual ~idParticleStage( void ) {} virtual ~idParticleStage(void) {}
void Default(); void Default();
virtual int NumQuadsPerParticle() const; // includes trails and cross faded animations virtual int NumQuadsPerParticle() const; // includes trails and cross faded animations
// returns the number of verts created, which will range from 0 to 4*NumQuadsPerParticle() // returns the number of verts created, which will range from 0 to 4*NumQuadsPerParticle()
virtual int CreateParticle( particleGen_t *g, idDrawVert *verts ) const; virtual int CreateParticle(particleGen_t* g, idDrawVert* verts) const;
void ParticleOrigin( particleGen_t *g, idVec3 &origin ) const; void ParticleOrigin(particleGen_t* g, idVec3& origin) const;
int ParticleVerts( particleGen_t *g, const idVec3 origin, idDrawVert *verts ) const; int ParticleVerts(particleGen_t* g, const idVec3 origin, idDrawVert* verts) const;
void ParticleTexCoords( particleGen_t *g, idDrawVert *verts ) const; void ParticleTexCoords(particleGen_t* g, idDrawVert* verts) const;
void ParticleColors( particleGen_t *g, idDrawVert *verts ) const; void ParticleColors(particleGen_t* g, idDrawVert* verts) const;
const char * GetCustomPathName(); const char* GetCustomPathName();
const char * GetCustomPathDesc(); const char* GetCustomPathDesc();
int NumCustomPathParms(); int NumCustomPathParms();
void SetCustomPathType( const char *p ); void SetCustomPathType(const char* p);
void operator=( const idParticleStage &src ); void operator=(const idParticleStage& src);
//------------------------------ //------------------------------
const idMaterial * material; const idMaterial* material;
int totalParticles; // total number of particles, although some may be invisible at a given time int totalParticles; // total number of particles, although some may be invisible at a given time
float cycles; // allows things to oneShot ( 1 cycle ) or run for a set number of cycles float cycles; // allows things to oneShot ( 1 cycle ) or run for a set number of cycles
// on a per stage basis // on a per stage basis
int cycleMsec; // ( particleLife + deadTime ) in msec int cycleMsec; // ( particleLife + deadTime ) in msec
@@ -137,37 +145,37 @@ public:
float particleLife; // total seconds of life for each particle float particleLife; // total seconds of life for each particle
float timeOffset; // time offset from system start for the first particle to spawn float timeOffset; // time offset from system start for the first particle to spawn
float deadTime; // time after particleLife before respawning float deadTime; // time after particleLife before respawning
//------------------------------- // standard path parms //------------------------------- // standard path parms
prtDistribution_t distributionType; prtDistribution_t distributionType;
float distributionParms[4]; float distributionParms[4];
prtDirection_t directionType; prtDirection_t directionType;
float directionParms[4]; float directionParms[4];
idParticleParm speed; idParticleParm speed;
float gravity; // can be negative to float up float gravity; // can be negative to float up
bool worldGravity; // apply gravity in world space bool worldGravity; // apply gravity in world space
bool randomDistribution; // randomly orient the quad on emission ( defaults to true ) bool randomDistribution; // randomly orient the quad on emission ( defaults to true )
bool entityColor; // force color from render entity ( fadeColor is still valid ) bool entityColor; // force color from render entity ( fadeColor is still valid )
//------------------------------ // custom path will completely replace the standard path calculations //------------------------------ // custom path will completely replace the standard path calculations
prtCustomPth_t customPathType; // use custom C code routines for determining the origin prtCustomPth_t customPathType; // use custom C code routines for determining the origin
float customPathParms[8]; float customPathParms[8];
//-------------------------------- //--------------------------------
idVec3 offset; // offset from origin to spawn all particles, also applies to customPath idVec3 offset; // offset from origin to spawn all particles, also applies to customPath
int animationFrames; // if > 1, subdivide the texture S axis into frames and crossfade int animationFrames; // if > 1, subdivide the texture S axis into frames and crossfade
float animationRate; // frames per second float animationRate; // frames per second
float initialAngle; // in degrees, random angle is used if zero ( default ) float initialAngle; // in degrees, random angle is used if zero ( default )
idParticleParm rotationSpeed; // half the particles will have negative rotation speeds idParticleParm rotationSpeed; // half the particles will have negative rotation speeds
prtOrientation_t orientation; // view, aimed, or axis fixed prtOrientation_t orientation; // view, aimed, or axis fixed
float orientationParms[4]; float orientationParms[4];
idParticleParm size; idParticleParm size;
@@ -185,6 +193,10 @@ public:
float boundsExpansion; // user tweak to fix poorly calculated bounds float boundsExpansion; // user tweak to fix poorly calculated bounds
idBounds bounds; // derived idBounds bounds; // derived
#ifdef PREY
bool lowSkippable; // HUMANHEAD bjk
#endif
}; };
@@ -194,25 +206,38 @@ public:
class idDeclParticle : public idDecl { class idDeclParticle : public idDecl {
public: public:
virtual size_t Size( void ) const; virtual size_t Size(void) const;
virtual const char * DefaultDefinition( void ) const; virtual const char* DefaultDefinition(void) const;
virtual bool Parse( const char *text, const int textLength ); virtual bool Parse(const char* text, const int textLength);
virtual void FreeData( void ); virtual void FreeData(void);
bool Save( const char *fileName = NULL ); bool Save(const char* fileName = NULL);
idList<idParticleStage *>stages; idList<idParticleStage*>stages;
idBounds bounds; idBounds bounds;
float depthHack; float depthHack;
private: private:
bool RebuildTextSource( void ); bool RebuildTextSource(void);
void GetStageBounds( idParticleStage *stage );
idParticleStage * ParseParticleStage( idLexer &src ); #ifdef PREY
void ParseParms( idLexer &src, float *parms, int maxParms ); void CalcBounds(void); // HUMANHEAD bjk
void ParseParametric( idLexer &src, idParticleParm *parm );
void WriteStage( idFile *f, idParticleStage *stage ); # ifdef PARTICLE_BOUNDS
void WriteParticleParm( idFile *f, idParticleParm *parm, const char *name ); void GetStageBounds(idParticleStage* stage, idDrawVert* particleVerts);
# else
void GetStageBounds(idParticleStage* stage);
# endif
#else
void GetStageBounds(idParticleStage* stage);
#endif
idParticleStage* ParseParticleStage(idLexer& src);
void ParseParms(idLexer& src, float* parms, int maxParms);
void ParseParametric(idLexer& src, idParticleParm* parm);
void WriteStage(idFile* f, idParticleStage* stage);
void WriteParticleParm(idFile* f, idParticleParm* parm, const char* name);
}; };
#endif /* !__DECLPARTICLE_H__ */ #endif /* !__DECLPARTICLE_H__ */