mirror of
https://github.com/jmarshall23/Quake_4_Alpha.git
synced 2026-08-12 04:01:12 +02:00
BSE fixes.
This commit is contained in:
+3
-4
@@ -218,11 +218,12 @@ enum { STFLAG_ENABLED = BIT( 0 ),
|
||||
STFLAG_ATTENUATE_EMITTER = BIT( 6 ),
|
||||
STFLAG_INVERSE_ATTENUATE = BIT( 7 ),
|
||||
STFLAG_TEMPORARY = BIT( 8 ),
|
||||
STFLAG_USEMATCOLOR = BIT( 9 ),
|
||||
// Quake 4 uses bit 9 to select the generic particle service path.
|
||||
// This covers physics, trails, timeout effects, electricity and lights.
|
||||
STFLAG_COMPLEX = BIT( 9 ),
|
||||
STFLAG_DEPTH_SORT = BIT( 10 ),
|
||||
STFLAG_INVERSE_DRAWORDER = BIT( 11 ),
|
||||
STFLAG_ORIENTATE_IDENTITY = BIT( 12 ),
|
||||
STFLAG_COMPLEX = BIT( 13 ),
|
||||
STFLAG_CALCULATE_DURATION = BIT( 14 ),
|
||||
};
|
||||
|
||||
@@ -254,7 +255,6 @@ public:
|
||||
bool GetInfiniteDuration( void ) const { return( !!( mFlags & STFLAG_INFINITE_DURATION ) ); }
|
||||
bool GetAttenuateEmitter( void ) const { return( !!( mFlags & STFLAG_ATTENUATE_EMITTER ) ); }
|
||||
bool GetInverseAttenuate( void ) const { return( !!( mFlags & STFLAG_INVERSE_ATTENUATE ) ); }
|
||||
bool GetUseMaterialColor( void ) const { return( !!( mFlags & STFLAG_USEMATCOLOR ) ); }
|
||||
bool GetTemporary( void ) const { return( !!( mFlags & STFLAG_TEMPORARY ) ); }
|
||||
bool GetDepthSort( void ) const { return( !!( mFlags & STFLAG_DEPTH_SORT ) ); }
|
||||
bool GetInverseDrawOrder( void ) const { return( !!( mFlags & STFLAG_INVERSE_DRAWORDER ) ); }
|
||||
@@ -270,7 +270,6 @@ public:
|
||||
void SetInfiniteDuration( bool infiniteDuration ) { SetFlag( infiniteDuration, STFLAG_INFINITE_DURATION ); }
|
||||
void SetAttenuateEmitter( bool attenuate ) { SetFlag( attenuate, STFLAG_ATTENUATE_EMITTER ); }
|
||||
void SetInverseAttenuate( bool attenuate ) { SetFlag( attenuate, STFLAG_INVERSE_ATTENUATE ); }
|
||||
void SetUseMaterialColor( bool attenuate ) { SetFlag( attenuate, STFLAG_USEMATCOLOR ); }
|
||||
void SetTemporary( bool persistent ) { SetFlag( persistent, STFLAG_TEMPORARY ); }
|
||||
void SetDepthSort( bool locked ) { SetFlag( locked, STFLAG_DEPTH_SORT ); }
|
||||
void SetInverseDrawOrder( bool inverseDrawOrder ) { SetFlag( inverseDrawOrder, STFLAG_INVERSE_DRAWORDER ); }
|
||||
|
||||
@@ -85,7 +85,7 @@ bool rvLightParticle::InitLight( rvBSE *effect, rvSegmentTemplate *st, float tim
|
||||
EvaluateSize( evalTime, size.ToFloatPtr() );
|
||||
|
||||
idVec3 position;
|
||||
EvaluatePosition( effect, position, evalTime - mMotionStartTime );
|
||||
EvaluatePosition( effect, position, time - mMotionStartTime );
|
||||
mLight.origin = effect->GetCurrentOrigin() + position * effect->GetCurrentAxis();
|
||||
mLight.lightRadius.Set(
|
||||
size[0] < 1.0f ? 1.0f : size[0],
|
||||
@@ -118,7 +118,7 @@ bool rvLightParticle::PresentLight( rvBSE *effect, float time, bool infinite ) {
|
||||
EvaluateSize( evalTime, size.ToFloatPtr() );
|
||||
|
||||
idVec3 position;
|
||||
EvaluatePosition( effect, position, evalTime - mMotionStartTime );
|
||||
EvaluatePosition( effect, position, time - mMotionStartTime );
|
||||
mLight.origin = effect->GetCurrentOrigin() + position * effect->GetCurrentAxis();
|
||||
mLight.lightRadius.Set(
|
||||
size[0] < 1.0f ? 1.0f : size[0],
|
||||
|
||||
+337
-20
@@ -45,6 +45,333 @@ bool rvParticleTemplate::GetVector( idLexer *lexer, int count, idVec3 &result )
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rvParticleTemplate::ParseMotionParms( idLexer *lexer, int count,
|
||||
rvEnvParms &envelope ) {
|
||||
idToken token;
|
||||
if ( !lexer->ExpectTokenString( "{" ) || !lexer->ReadToken( &token ) ) {
|
||||
return false;
|
||||
}
|
||||
while ( token.Cmp( "}" ) != 0 ) {
|
||||
if ( token.Icmp( "envelope" ) == 0 ) {
|
||||
if ( !lexer->ReadToken( &token ) ) {
|
||||
return false;
|
||||
}
|
||||
envelope.SetType( token.c_str() );
|
||||
} else if ( token.Icmp( "rate" ) == 0 ) {
|
||||
if ( !GetVector( lexer, count, envelope.GetRateRef() ) ) {
|
||||
return false;
|
||||
}
|
||||
envelope.SetIsCount( false );
|
||||
} else if ( token.Icmp( "count" ) == 0 ) {
|
||||
if ( !GetVector( lexer, count, envelope.GetRateRef() ) ) {
|
||||
return false;
|
||||
}
|
||||
envelope.SetIsCount( true );
|
||||
} else if ( token.Icmp( "offset" ) == 0 ) {
|
||||
if ( !GetVector( lexer, count, envelope.GetOffsetRef() ) ) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
common->Warning( "^4BSE:^1 Invalid motion parameter '%s' (file: %s, line: %d)",
|
||||
token.c_str(), lexer->GetFileName(), lexer->GetLineNum() );
|
||||
}
|
||||
if ( !lexer->ReadToken( &token ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rvParticleTemplate::ParseMotionDomains( rvDeclEffect *effect,
|
||||
idLexer *lexer ) {
|
||||
idToken token;
|
||||
if ( !lexer->ExpectTokenString( "{" ) || !lexer->ReadToken( &token ) ) {
|
||||
return false;
|
||||
}
|
||||
while ( token.Cmp( "}" ) != 0 ) {
|
||||
bool parsed = true;
|
||||
if ( token.Icmp( "tint" ) == 0 ) {
|
||||
parsed = ParseMotionParms( lexer, 3, mTintEnvelope );
|
||||
} else if ( token.Icmp( "fade" ) == 0 ) {
|
||||
parsed = ParseMotionParms( lexer, 1, mFadeEnvelope );
|
||||
} else if ( token.Icmp( "size" ) == 0 ) {
|
||||
parsed = ParseMotionParms( lexer, mNumSizeParms, mSizeEnvelope );
|
||||
} else if ( token.Icmp( "rotate" ) == 0 ) {
|
||||
parsed = ParseMotionParms( lexer, mNumRotateParms, mRotateEnvelope );
|
||||
} else if ( token.Icmp( "angle" ) == 0 ) {
|
||||
parsed = ParseMotionParms( lexer, 3, mAngleEnvelope );
|
||||
} else if ( token.Icmp( "offset" ) == 0 ) {
|
||||
parsed = ParseMotionParms( lexer, 3, mOffsetEnvelope );
|
||||
} else if ( token.Icmp( "length" ) == 0 ) {
|
||||
parsed = ParseMotionParms( lexer, 3, mLengthEnvelope );
|
||||
} else {
|
||||
common->Warning( "^4BSE:^1 Invalid motion domain '%s' in '%s' (file: %s, line: %d)",
|
||||
token.c_str(), effect ? effect->GetName() : "<unknown>",
|
||||
lexer->GetFileName(), lexer->GetLineNum() );
|
||||
lexer->SkipBracedSection( true );
|
||||
}
|
||||
if ( !parsed || !lexer->ReadToken( &token ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rvParticleTemplate::CheckCommonParms( idLexer *lexer,
|
||||
rvParticleParms &parms ) {
|
||||
idToken token;
|
||||
if ( !lexer->ReadToken( &token ) ) {
|
||||
return false;
|
||||
}
|
||||
while ( token.Cmp( "}" ) != 0 ) {
|
||||
if ( token.Icmp( "surface" ) == 0 ) {
|
||||
parms.mFlags |= PPFLAG_SURFACE;
|
||||
} else if ( token.Icmp( "useEndOrigin" ) == 0 ) {
|
||||
parms.mFlags |= PPFLAG_USEENDORIGIN;
|
||||
} else if ( token.Icmp( "cone" ) == 0 ) {
|
||||
parms.mFlags |= PPFLAG_CONE;
|
||||
} else if ( token.Icmp( "relative" ) == 0 ) {
|
||||
parms.mFlags |= PPFLAG_RELATIVE;
|
||||
} else if ( token.Icmp( "linearSpacing" ) == 0 ) {
|
||||
parms.mFlags |= PPFLAG_LINEARSPACING;
|
||||
} else if ( token.Icmp( "attenuate" ) == 0 ) {
|
||||
parms.mFlags |= PPFLAG_ATTENUATE;
|
||||
} else if ( token.Icmp( "inverseAttenuate" ) == 0 ) {
|
||||
parms.mFlags |= PPFLAG_INV_ATTENUATE;
|
||||
}
|
||||
if ( !lexer->ReadToken( &token ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rvParticleTemplate::ParseSpawnParms( rvDeclEffect *effect,
|
||||
idLexer *lexer, rvParticleParms &parms, int count ) {
|
||||
idToken token;
|
||||
if ( !lexer->ExpectTokenString( "{" ) || !lexer->ReadToken( &token ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( token.Cmp( "}" ) == 0 ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fixup = true;
|
||||
if ( token.Icmp( "point" ) == 0 ) {
|
||||
parms.mSpawnType = SPF_POINT_0 + count;
|
||||
if ( !GetVector( lexer, count, parms.mMins ) ) { return false; }
|
||||
} else if ( token.Icmp( "line" ) == 0 ) {
|
||||
parms.mSpawnType = SPF_LINEAR_0 + count;
|
||||
if ( !GetVector( lexer, count, parms.mMins ) ||
|
||||
!lexer->ExpectTokenString( "," ) ||
|
||||
!GetVector( lexer, count, parms.mMaxs ) ) { return false; }
|
||||
} else if ( token.Icmp( "box" ) == 0 || token.Icmp( "sphere" ) == 0 ||
|
||||
token.Icmp( "cylinder" ) == 0 ) {
|
||||
const int base = token.Icmp( "box" ) == 0 ? SPF_BOX_0 :
|
||||
( token.Icmp( "sphere" ) == 0 ? SPF_SPHERE_0 : SPF_CYLINDER_0 );
|
||||
parms.mSpawnType = base + count;
|
||||
if ( !GetVector( lexer, count, parms.mMins ) ||
|
||||
!lexer->ExpectTokenString( "," ) ||
|
||||
!GetVector( lexer, count, parms.mMaxs ) ) { return false; }
|
||||
} else if ( token.Icmp( "spiral" ) == 0 ) {
|
||||
parms.mSpawnType = SPF_SPIRAL_0 + count;
|
||||
if ( !GetVector( lexer, count, parms.mMins ) ||
|
||||
!lexer->ExpectTokenString( "," ) ||
|
||||
!GetVector( lexer, count, parms.mMaxs ) ||
|
||||
!lexer->ExpectTokenString( "," ) ) { return false; }
|
||||
parms.mRange = lexer->ParseFloat();
|
||||
} else if ( token.Icmp( "model" ) == 0 ) {
|
||||
parms.mSpawnType = SPF_MODEL_0 + count;
|
||||
if ( !lexer->ReadToken( &token ) ) { return false; }
|
||||
idRenderModel *model = renderModelManager->FindModel( token.c_str() );
|
||||
if ( model == NULL || model->NumSurfaces() == 0 ) {
|
||||
common->Warning( "^4BSE:^1 No surfaces defined in model '%s' in '%s' (file: %s, line: %d)",
|
||||
token.c_str(), effect ? effect->GetName() : "<unknown>",
|
||||
lexer->GetFileName(), lexer->GetLineNum() );
|
||||
model = renderModelManager->FindModel( "_default" );
|
||||
}
|
||||
parms.mMisc = model;
|
||||
if ( !lexer->ExpectTokenString( "," ) ||
|
||||
!GetVector( lexer, count, parms.mMins ) ||
|
||||
!lexer->ExpectTokenString( "," ) ||
|
||||
!GetVector( lexer, count, parms.mMaxs ) ) { return false; }
|
||||
fixup = false;
|
||||
} else {
|
||||
common->Warning( "^4BSE:^1 Invalid spawn domain '%s' in '%s' (file: %s, line: %d)",
|
||||
token.c_str(), effect ? effect->GetName() : "<unknown>",
|
||||
lexer->GetFileName(), lexer->GetLineNum() );
|
||||
return CheckCommonParms( lexer, parms );
|
||||
}
|
||||
|
||||
if ( !CheckCommonParms( lexer, parms ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( parms.mFlags & PPFLAG_SURFACE ) {
|
||||
if ( parms.mSpawnType >= SPF_BOX_0 && parms.mSpawnType <= SPF_BOX_3 ) {
|
||||
parms.mSpawnType += SPF_SURFACE_BOX_0 - SPF_BOX_0;
|
||||
} else if ( parms.mSpawnType >= SPF_SPHERE_0 && parms.mSpawnType <= SPF_SPHERE_3 ) {
|
||||
parms.mSpawnType += SPF_SURFACE_SPHERE_0 - SPF_SPHERE_0;
|
||||
} else if ( parms.mSpawnType >= SPF_CYLINDER_0 && parms.mSpawnType <= SPF_CYLINDER_3 ) {
|
||||
parms.mSpawnType += SPF_SURFACE_CYLINDER_0 - SPF_CYLINDER_0;
|
||||
}
|
||||
}
|
||||
if ( fixup ) {
|
||||
FixupParms( parms );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rvParticleTemplate::ParseSpawnDomains( rvDeclEffect *effect,
|
||||
idLexer *lexer ) {
|
||||
idToken token;
|
||||
if ( !lexer->ExpectTokenString( "{" ) || !lexer->ReadToken( &token ) ) {
|
||||
return false;
|
||||
}
|
||||
while ( token.Cmp( "}" ) != 0 ) {
|
||||
bool parsed = true;
|
||||
if ( token.Icmp( "position" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mSpawnPosition, 3 );
|
||||
} else if ( token.Icmp( "direction" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mSpawnDirection, 3 );
|
||||
SetCalculatedNormal( true );
|
||||
} else if ( token.Icmp( "velocity" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mSpawnVelocity, 3 );
|
||||
} else if ( token.Icmp( "acceleration" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mSpawnAcceleration, 3 );
|
||||
} else if ( token.Icmp( "friction" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mSpawnFriction, 3 );
|
||||
} else if ( token.Icmp( "tint" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mSpawnTint, 3 );
|
||||
} else if ( token.Icmp( "fade" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mSpawnFade, 1 );
|
||||
} else if ( token.Icmp( "size" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mSpawnSize, mNumSizeParms );
|
||||
} else if ( token.Icmp( "rotate" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mSpawnRotate, mNumRotateParms );
|
||||
} else if ( token.Icmp( "angle" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mSpawnAngle, 3 );
|
||||
} else if ( token.Icmp( "offset" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mSpawnOffset, 3 );
|
||||
} else if ( token.Icmp( "length" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mSpawnLength, 3 );
|
||||
} else {
|
||||
common->Warning( "^4BSE:^1 Invalid spawn type '%s' in '%s' (file: %s, line: %d)",
|
||||
token.c_str(), effect ? effect->GetName() : "<unknown>",
|
||||
lexer->GetFileName(), lexer->GetLineNum() );
|
||||
lexer->SkipBracedSection( true );
|
||||
}
|
||||
if ( !parsed || !lexer->ReadToken( &token ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rvParticleTemplate::ParseDeathDomains( rvDeclEffect *effect,
|
||||
idLexer *lexer ) {
|
||||
idToken token;
|
||||
if ( !lexer->ExpectTokenString( "{" ) || !lexer->ReadToken( &token ) ) {
|
||||
return false;
|
||||
}
|
||||
while ( token.Cmp( "}" ) != 0 ) {
|
||||
bool parsed = true;
|
||||
rvEnvParms *envelope = NULL;
|
||||
if ( token.Icmp( "tint" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mDeathTint, 3 ); envelope = &mTintEnvelope;
|
||||
} else if ( token.Icmp( "fade" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mDeathFade, 1 ); envelope = &mFadeEnvelope;
|
||||
} else if ( token.Icmp( "size" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mDeathSize, mNumSizeParms ); envelope = &mSizeEnvelope;
|
||||
} else if ( token.Icmp( "rotate" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mDeathRotate, mNumRotateParms ); envelope = &mRotateEnvelope;
|
||||
} else if ( token.Icmp( "angle" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mDeathAngle, 3 ); envelope = &mAngleEnvelope;
|
||||
} else if ( token.Icmp( "offset" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mDeathOffset, 3 ); envelope = &mOffsetEnvelope;
|
||||
} else if ( token.Icmp( "length" ) == 0 ) {
|
||||
parsed = ParseSpawnParms( effect, lexer, mDeathLength, 3 ); envelope = &mLengthEnvelope;
|
||||
} else {
|
||||
common->Warning( "^4BSE:^1 Invalid end type '%s' in '%s' (file: %s, line: %d)",
|
||||
token.c_str(), effect ? effect->GetName() : "<unknown>",
|
||||
lexer->GetFileName(), lexer->GetLineNum() );
|
||||
lexer->SkipBracedSection( true );
|
||||
}
|
||||
if ( envelope != NULL ) {
|
||||
envelope->SetDefaultType();
|
||||
}
|
||||
if ( !parsed || !lexer->ReadToken( &token ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rvParticleTemplate::ParseImpact( rvDeclEffect *effect, idLexer *lexer ) {
|
||||
idToken token;
|
||||
if ( !lexer->ExpectTokenString( "{" ) || !lexer->ReadToken( &token ) ) {
|
||||
return false;
|
||||
}
|
||||
SetHasPhysics( true );
|
||||
while ( token.Cmp( "}" ) != 0 ) {
|
||||
if ( token.Icmp( "effect" ) == 0 ) {
|
||||
if ( !lexer->ReadToken( &token ) ) { return false; }
|
||||
if ( mNumImpactEffects < BSE_NUM_SPAWNABLE ) {
|
||||
mImpactEffects[mNumImpactEffects++] = declManager->FindEffect( token.c_str() );
|
||||
} else {
|
||||
common->Warning( "^4BSE:^1 Too many impact effects '%s' in '%s'",
|
||||
token.c_str(), effect ? effect->GetName() : "<unknown>" );
|
||||
}
|
||||
} else if ( token.Icmp( "remove" ) == 0 ) {
|
||||
SetDeleteOnImpact( lexer->ParseInt() != 0 );
|
||||
} else if ( token.Icmp( "bounce" ) == 0 ) {
|
||||
mBounce = lexer->ParseFloat();
|
||||
} else {
|
||||
common->Warning( "^4BSE:^1 Invalid impact parameter '%s' in '%s'",
|
||||
token.c_str(), effect ? effect->GetName() : "<unknown>" );
|
||||
}
|
||||
if ( !lexer->ReadToken( &token ) ) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rvParticleTemplate::ParseTimeout( rvDeclEffect *effect, idLexer *lexer ) {
|
||||
idToken token;
|
||||
if ( !lexer->ExpectTokenString( "{" ) || !lexer->ReadToken( &token ) ) {
|
||||
return false;
|
||||
}
|
||||
while ( token.Cmp( "}" ) != 0 ) {
|
||||
if ( token.Icmp( "effect" ) == 0 ) {
|
||||
if ( !lexer->ReadToken( &token ) ) { return false; }
|
||||
if ( mNumTimeoutEffects < BSE_NUM_SPAWNABLE ) {
|
||||
mTimeoutEffects[mNumTimeoutEffects++] = declManager->FindEffect( token.c_str() );
|
||||
} else {
|
||||
common->Warning( "^4BSE:^1 Too many timeout effects '%s' in '%s'",
|
||||
token.c_str(), effect ? effect->GetName() : "<unknown>" );
|
||||
}
|
||||
} else {
|
||||
common->Warning( "^4BSE:^1 Invalid timeout parameter '%s' in '%s'",
|
||||
token.c_str(), effect ? effect->GetName() : "<unknown>" );
|
||||
}
|
||||
if ( !lexer->ReadToken( &token ) ) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rvParticleTemplate::ParseBlendParms( rvDeclEffect *effect, idLexer *lexer ) {
|
||||
idToken token;
|
||||
if ( !lexer->ReadToken( &token ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( token.Icmp( "add" ) == 0 ) {
|
||||
SetAdditive( true );
|
||||
} else {
|
||||
common->Warning( "^4BSE:^1 Invalid blend type '%s' in '%s' (file: %s, line: %d)",
|
||||
token.c_str(), effect ? effect->GetName() : "<unknown>",
|
||||
lexer->GetFileName(), lexer->GetLineNum() );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void rvParticleTemplate::SetParameterCounts() {
|
||||
int sizeParms;
|
||||
int rotateParms;
|
||||
@@ -63,7 +390,7 @@ void rvParticleTemplate::SetParameterCounts() {
|
||||
sizeParms = 3; rotateParms = 3; sizeSpawnType = SPF_ONE_3; break;
|
||||
case PTYPE_LIGHT:
|
||||
sizeParms = 3; rotateParms = 0; sizeSpawnType = SPF_ONE_3; break;
|
||||
case PTYPE_ORIENTEDLINKED:
|
||||
case PTYPE_DEBRIS:
|
||||
sizeParms = 0; rotateParms = 3; sizeSpawnType = SPF_ONE_3; break;
|
||||
default:
|
||||
return;
|
||||
@@ -284,7 +611,7 @@ void rvParticleTemplate::Finish() {
|
||||
mVertexCount = 4 * ( 5 * mNumForks + 5 );
|
||||
mIndexCount = 60 * ( mNumForks + 1 );
|
||||
break;
|
||||
case PTYPE_ORIENTEDLINKED:
|
||||
case PTYPE_DEBRIS:
|
||||
mVertexCount = mIndexCount = 0;
|
||||
break;
|
||||
default:
|
||||
@@ -318,11 +645,11 @@ bool rvParticleTemplate::Parse( rvDeclEffect *effect, idLexer *lexer ) {
|
||||
}
|
||||
while ( token.Cmp( "}" ) != 0 ) {
|
||||
if ( token.Icmp( "start" ) == 0 ) {
|
||||
lexer->SkipBracedSection();
|
||||
if ( !ParseSpawnDomains( effect, lexer ) ) { return false; }
|
||||
} else if ( token.Icmp( "end" ) == 0 ) {
|
||||
lexer->SkipBracedSection();
|
||||
if ( !ParseDeathDomains( effect, lexer ) ) { return false; }
|
||||
} else if ( token.Icmp( "motion" ) == 0 ) {
|
||||
lexer->SkipBracedSection();
|
||||
if ( !ParseMotionDomains( effect, lexer ) ) { return false; }
|
||||
} else if ( token.Icmp( "generatedNormal" ) == 0 ) {
|
||||
SetGeneratedNormal( true );
|
||||
} else if ( token.Icmp( "generatedOriginNormal" ) == 0 ) {
|
||||
@@ -374,8 +701,7 @@ bool rvParticleTemplate::Parse( rvDeclEffect *effect, idLexer *lexer ) {
|
||||
lexer->ReadToken( &token );
|
||||
mJitterTable = declManager->FindTable( token );
|
||||
} else if ( token.Icmp( "blend" ) == 0 ) {
|
||||
lexer->ReadToken( &token );
|
||||
SetAdditive( token.Icmp( "add" ) == 0 );
|
||||
if ( !ParseBlendParms( effect, lexer ) ) { return false; }
|
||||
} else if ( token.Icmp( "shadows" ) == 0 ) {
|
||||
SetShadows( true );
|
||||
} else if ( token.Icmp( "specular" ) == 0 ) {
|
||||
@@ -384,19 +710,10 @@ bool rvParticleTemplate::Parse( rvDeclEffect *effect, idLexer *lexer ) {
|
||||
lexer->ReadToken( &token );
|
||||
mModelName = token;
|
||||
renderModelManager->FindModel( token );
|
||||
} else if ( token.Icmp( "impact" ) == 0 || token.Icmp( "timeout" ) == 0 ) {
|
||||
const bool impact = token.Icmp( "impact" ) == 0;
|
||||
if ( !lexer->ExpectTokenString( "{" ) ) { return false; }
|
||||
while ( lexer->ReadToken( &token ) && token.Cmp( "}" ) != 0 ) {
|
||||
if ( token.Icmp( "effect" ) == 0 && lexer->ReadToken( &token ) ) {
|
||||
if ( impact && mNumImpactEffects < BSE_NUM_SPAWNABLE ) { mImpactEffects[mNumImpactEffects++] = declManager->FindEffect( token ); }
|
||||
else if ( !impact && mNumTimeoutEffects < BSE_NUM_SPAWNABLE ) { mTimeoutEffects[mNumTimeoutEffects++] = declManager->FindEffect( token ); }
|
||||
} else if ( impact && token.Icmp( "remove" ) == 0 ) {
|
||||
SetDeleteOnImpact( lexer->ParseInt() != 0 );
|
||||
} else if ( impact && token.Icmp( "bounce" ) == 0 ) {
|
||||
mBounce = lexer->ParseFloat();
|
||||
}
|
||||
}
|
||||
} else if ( token.Icmp( "impact" ) == 0 ) {
|
||||
if ( !ParseImpact( effect, lexer ) ) { return false; }
|
||||
} else if ( token.Icmp( "timeout" ) == 0 ) {
|
||||
if ( !ParseTimeout( effect, lexer ) ) { return false; }
|
||||
} else {
|
||||
common->Warning( "^4BSE:^1 Invalid particle keyword '%s' in '%s' (file: %s, line: %d)",
|
||||
token.c_str(), effect ? effect->GetName() : "<unknown>", lexer->GetFileName(), lexer->GetLineNum() );
|
||||
|
||||
@@ -61,7 +61,6 @@ enum
|
||||
PTYPE_LIGHT, // Dynamic light - very expensive
|
||||
PTYPE_ELECTRICITY, // A bolt of electricity
|
||||
PTYPE_LINKED, // A series of linked lines
|
||||
PTYPE_ORIENTEDLINKED,
|
||||
PTYPE_DEBRIS, // A client side moveable entity spawned in the game
|
||||
PTYPE_COUNT
|
||||
};
|
||||
|
||||
+136
-22
@@ -205,7 +205,7 @@ rvParticle *rvSegment::GetFreeParticle( rvBSE *effect ) {
|
||||
void rvSegment::InsertParticle( rvParticle *particle, rvSegmentTemplate *st ) {
|
||||
if ( st->GetTemporary() ) { return; }
|
||||
++mActiveCount;
|
||||
if ( st->GetUseMaterialColor() ) {
|
||||
if ( st->GetComplexParticle() ) {
|
||||
particle->SetNext( mUsedHead );
|
||||
mUsedHead = particle;
|
||||
return;
|
||||
@@ -225,7 +225,10 @@ rvParticle *rvSegment::SpawnParticle( rvBSE *effect, rvSegmentTemplate *st, floa
|
||||
if ( !GetSegmentTemplate() ) { return NULL; }
|
||||
rvParticle *particle = st->GetTemporary() ? mParticles : GetFreeParticle( effect );
|
||||
if ( !particle ) { return NULL; }
|
||||
particle->FinishSpawn( effect, this, birthTime, 0.0f, initPos, initAxis );
|
||||
// Quake 4 feeds the single-particle birth time through as the spawn
|
||||
// fraction as well. Several locked/constant weapon effects use that
|
||||
// value to seed their linear spacing and envelope evaluation.
|
||||
particle->FinishSpawn( effect, this, birthTime, birthTime, initPos, initAxis );
|
||||
InsertParticle( particle, st );
|
||||
return particle;
|
||||
}
|
||||
@@ -294,29 +297,66 @@ void rvSegment::CalcTrailCounts( rvBSE *effect, rvSegmentTemplate *st, rvParticl
|
||||
void rvSegment::CalcCounts( rvBSE *effect, float time ) {
|
||||
rvSegmentTemplate *st = GetSegmentTemplate();
|
||||
if ( !st || st->GetType() == SEG_TRAIL || st->GetParticleTemplate()->GetType() == PTYPE_NONE ) { return; }
|
||||
const float particleDuration = st->GetParticleTemplate()->GetMaxDuration() + BSE_FUTURE;
|
||||
|
||||
rvParticleTemplate *pt = st->GetParticleTemplate();
|
||||
const float particleMaxDuration = pt->GetMaxDuration() + BSE_FUTURE;
|
||||
const float effectMinDuration = mEffectDecl->GetMinDuration();
|
||||
int particleCount = 0;
|
||||
int loopParticleCount = 0;
|
||||
float duration = 0.0f;
|
||||
|
||||
switch ( st->GetType() ) {
|
||||
case SEG_EMITTER:
|
||||
duration = Min( particleDuration, st->mLocalDuration.y ) + BSE_FUTURE;
|
||||
mParticleCount = mLoopParticleCount = (int)ceilf( duration / mSecondsPerParticle.y ) + 1;
|
||||
if ( pt->GetType() == PTYPE_DEBRIS ) {
|
||||
particleCount = loopParticleCount = 1;
|
||||
break;
|
||||
}
|
||||
duration = Min( particleMaxDuration, st->mLocalDuration.y ) + BSE_FUTURE;
|
||||
particleCount = loopParticleCount = (int)ceilf( duration / mSecondsPerParticle.y ) + 1;
|
||||
if ( effectMinDuration < particleMaxDuration ) {
|
||||
loopParticleCount = (int)ceilf( ( (float)particleCount / effectMinDuration ) * particleMaxDuration ) + 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case SEG_SPAWNER:
|
||||
mParticleCount = mLoopParticleCount = (int)ceilf( mCount.y );
|
||||
if ( pt->GetType() == PTYPE_DEBRIS ) {
|
||||
particleCount = loopParticleCount = 1;
|
||||
break;
|
||||
}
|
||||
particleCount = loopParticleCount = (int)ceilf( mCount.y );
|
||||
if ( effectMinDuration != 0.0f && !st->GetInfiniteDuration() && effectMinDuration < particleMaxDuration ) {
|
||||
loopParticleCount = particleCount * ( (int)ceilf( particleMaxDuration / effectMinDuration ) + 1 ) + 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case SEG_DECAL:
|
||||
case SEG_LIGHT:
|
||||
mParticleCount = mLoopParticleCount = 1;
|
||||
particleCount = loopParticleCount = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
mParticleCount = mLoopParticleCount = 0;
|
||||
break;
|
||||
}
|
||||
if ( st->GetHasParticles() && ( st->GetType() == SEG_EMITTER || st->GetType() == SEG_SPAWNER ) ) {
|
||||
CalcTrailCounts( effect, st, st->GetParticleTemplate(), duration );
|
||||
|
||||
mParticleCount = particleCount;
|
||||
mLoopParticleCount = loopParticleCount;
|
||||
|
||||
if ( st->GetHasParticles() ) {
|
||||
if ( particleCount == 0 || loopParticleCount == 0 ) {
|
||||
common->Warning( "^4BSE:^1 '%s'\nSegment with no particles for effect", mEffectDecl->GetName() );
|
||||
}
|
||||
if ( st->GetType() == SEG_EMITTER || st->GetType() == SEG_SPAWNER ) {
|
||||
CalcTrailCounts( effect, st, pt, duration );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !effect->GetLooping() ) {
|
||||
effect->SetDuration( mSegEndTime - time + st->GetParticleTemplate()->GetMaxDuration() );
|
||||
effect->SetDuration( mSegEndTime - time + pt->GetMaxDuration() );
|
||||
}
|
||||
|
||||
if ( bse_debug.GetInteger() == 2 ) {
|
||||
common->Printf( "BSE: Segment %s: Count: %d Looping: %d\n",
|
||||
rvBSEManagerLocal::mSegmentNames[st->GetType()], particleCount, loopParticleCount );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,20 +400,34 @@ void rvSegment::Handle( rvBSE *effect, float time ) {
|
||||
}
|
||||
|
||||
void rvSegment::UpdateGenericParticles( rvBSE *effect, rvSegmentTemplate *st, float time ) {
|
||||
const bool infinite = st->GetInfiniteDuration();
|
||||
const bool smoker = st->GetSmoker();
|
||||
rvParticle *previous = NULL;
|
||||
rvParticle *particle = mUsedHead;
|
||||
while ( particle ) {
|
||||
rvParticle *next = particle->GetNext();
|
||||
bool remove = particle->Expired( time );
|
||||
if ( st->GetHasPhysics() ) { remove = particle->RunPhysics( effect, st, time ); }
|
||||
if ( effect->GetStopped() && !particle->GetPersist() ) { remove = true; }
|
||||
if ( remove ) {
|
||||
bool remove = false;
|
||||
if ( infinite ) {
|
||||
particle->RunPhysics( effect, st, time );
|
||||
remove = effect->GetStopped();
|
||||
} else if ( particle->Expired( time ) ) {
|
||||
particle->CheckTimeoutEffect( effect, st, time );
|
||||
particle->Destroy();
|
||||
remove = true;
|
||||
} else {
|
||||
remove = particle->RunPhysics( effect, st, time );
|
||||
}
|
||||
if ( effect->GetStopped() && !particle->GetPersist() ) { remove = true; }
|
||||
if ( smoker && st->mTrailSegmentIndex >= 0 &&
|
||||
st->mTrailSegmentIndex < effect->mSegments.Num() ) {
|
||||
particle->EmitSmokeParticles( effect,
|
||||
&effect->mSegments[st->mTrailSegmentIndex], time );
|
||||
}
|
||||
if ( remove ) {
|
||||
if ( previous ) { previous->SetNext( next ); } else { mUsedHead = next; }
|
||||
particle->SetNext( mFreeHead );
|
||||
mFreeHead = particle;
|
||||
--mActiveCount;
|
||||
particle->SetNext( mFreeHead );
|
||||
particle->Destroy();
|
||||
mFreeHead = particle;
|
||||
} else {
|
||||
previous = particle;
|
||||
}
|
||||
@@ -385,7 +439,7 @@ bool rvSegment::UpdateParticles( rvBSE *effect, float time ) {
|
||||
rvSegmentTemplate *st = GetSegmentTemplate();
|
||||
if ( !st ) { return false; }
|
||||
Handle( effect, time );
|
||||
if ( effect->GetStopped() || st->GetHasPhysics() ) { UpdateGenericParticles( effect, st, time ); }
|
||||
if ( effect->GetStopped() || st->GetComplexParticle() ) { UpdateGenericParticles( effect, st, time ); }
|
||||
else { UpdateSimpleParticles( time ); }
|
||||
if ( st->GetParticleTemplate()->GetType() == PTYPE_ELECTRICITY ) { UpdateElectricityParticles( time ); }
|
||||
if ( com_debugHudActive ) {
|
||||
@@ -403,10 +457,70 @@ void rvSegment::PlayEffect( rvBSE *effect, rvSegmentTemplate *st ) {
|
||||
}
|
||||
|
||||
void rvSegment::CreateDecal( rvBSE *effect, float time ) {
|
||||
// Decal geometry is projected by the render world; spawning the temporary
|
||||
// particle preserves the retail timing and randomized particle parameters.
|
||||
static const idVec3 decalWinding[4] = {
|
||||
idVec3( 1.0f, 1.0f, 0.0f ),
|
||||
idVec3( -1.0f, 1.0f, 0.0f ),
|
||||
idVec3( -1.0f, -1.0f, 0.0f ),
|
||||
idVec3( 1.0f, -1.0f, 0.0f )
|
||||
};
|
||||
|
||||
if ( !bse_render.GetBool() || effect == NULL || session == NULL ||
|
||||
session->rw == NULL ) {
|
||||
return;
|
||||
}
|
||||
|
||||
rvSegmentTemplate *st = GetSegmentTemplate();
|
||||
if ( bse_render.GetBool() && st ) { SpawnParticle( effect, st, time ); }
|
||||
if ( st == NULL ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( bse_debug.GetBool() ) {
|
||||
static int decalCount;
|
||||
const idMaterial *material = st->GetParticleTemplate()->GetMaterial();
|
||||
common->Printf( "BSE: Decal %d: %s\n", ++decalCount,
|
||||
material != NULL ? material->GetName() : "<null>" );
|
||||
}
|
||||
|
||||
rvParticle *particle = SpawnParticle( effect, st, time,
|
||||
vec3_origin, mat3_identity );
|
||||
if ( particle == NULL ) {
|
||||
return;
|
||||
}
|
||||
|
||||
idVec4 tint;
|
||||
idVec3 size;
|
||||
idVec3 rotation;
|
||||
particle->GetSpawnInfo( tint, size, rotation );
|
||||
|
||||
float sine;
|
||||
float cosine;
|
||||
idMath::SinCos( rotation.x, sine, cosine );
|
||||
|
||||
// BSE decals project opposite the effect's forward axis. The retail code
|
||||
// uses a fixed eight-unit projection depth and rotates the winding around
|
||||
// that direction by the particle's randomized rotation parameter.
|
||||
idMat3 axis;
|
||||
axis[2] = -effect->GetCurrentAxis()[0];
|
||||
axis[2].Normalize();
|
||||
idVec3 tangent0;
|
||||
idVec3 tangent1;
|
||||
axis[2].NormalVectors( tangent0, tangent1 );
|
||||
axis[0] = tangent0 * cosine + tangent1 * -sine;
|
||||
axis[1] = tangent0 * -sine + tangent1 * -cosine;
|
||||
|
||||
const idVec3 &origin = effect->GetCurrentOrigin();
|
||||
const idVec3 windingOrigin = origin + axis[2] * 8.0f;
|
||||
const idVec3 projectionOrigin = origin - axis[2] * 8.0f;
|
||||
|
||||
idFixedWinding winding;
|
||||
for ( int point = 0; point < 4; ++point ) {
|
||||
winding += idVec5( windingOrigin + ( axis * decalWinding[point] ) * size.x,
|
||||
idVec2( point == 0 || point == 3 ? 1.0f : 0.0f,
|
||||
point < 2 ? 1.0f : 0.0f ) );
|
||||
}
|
||||
|
||||
session->rw->ProjectDecalOntoWorld( winding, projectionOrigin, true, 8.0f,
|
||||
st->GetParticleTemplate()->GetMaterial(), idMath::Ftoi( time * 1000.0f ) );
|
||||
}
|
||||
|
||||
bool rvSegment::Check( rvBSE *effect, float time ) {
|
||||
|
||||
@@ -204,8 +204,7 @@ bool rvSegmentTemplate::Finish( rvDeclEffect *effect ) {
|
||||
mParticleTemplate.GetHasPhysics() ||
|
||||
mParticleTemplate.GetNumTimeoutEffects() != 0 ||
|
||||
particleType == PTYPE_ELECTRICITY || particleType == PTYPE_LIGHT ) {
|
||||
// In the retail executable the complex-update flag occupies bit 9.
|
||||
mFlags |= BIT( 9 );
|
||||
mFlags |= STFLAG_COMPLEX;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -802,7 +802,13 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
|
||||
tri->primBatchMesh->SetupForDrawRender( NULL );
|
||||
}
|
||||
} else {
|
||||
qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), (void *)&ac->color );
|
||||
if ( surf->mFlags & 1 ) {
|
||||
const byte *decalColors = reinterpret_cast<const byte *>( ac ) +
|
||||
tri->numVerts * sizeof( idDrawVert ) + stage * tri->numVerts * 4;
|
||||
qglColorPointer( 4, GL_UNSIGNED_BYTE, 4, decalColors );
|
||||
} else {
|
||||
qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), (void *)&ac->color );
|
||||
}
|
||||
qglVertexAttribPointerARB( 9, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[0].ToFloatPtr() );
|
||||
qglVertexAttribPointerARB( 10, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[1].ToFloatPtr() );
|
||||
qglNormalPointer( GL_FLOAT, sizeof( idDrawVert ), ac->normal.ToFloatPtr() );
|
||||
@@ -869,10 +875,14 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
|
||||
//--------------------------
|
||||
|
||||
// set the color
|
||||
color[0] = regs[ pStage->color.registers[0] ];
|
||||
color[1] = regs[ pStage->color.registers[1] ];
|
||||
color[2] = regs[ pStage->color.registers[2] ];
|
||||
color[3] = regs[ pStage->color.registers[3] ];
|
||||
if ( surf->mFlags & 1 ) {
|
||||
color[0] = color[1] = color[2] = color[3] = 1.0f;
|
||||
} else {
|
||||
color[0] = regs[ pStage->color.registers[0] ];
|
||||
color[1] = regs[ pStage->color.registers[1] ];
|
||||
color[2] = regs[ pStage->color.registers[2] ];
|
||||
color[3] = regs[ pStage->color.registers[3] ];
|
||||
}
|
||||
|
||||
// skip the entire stage if an add would be black
|
||||
if ( ( pStage->drawStateBits & (GLS_SRCBLEND_BITS|GLS_DSTBLEND_BITS) ) == ( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE )
|
||||
@@ -890,7 +900,13 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
|
||||
if ( pStage->vertexColor == SVC_IGNORE ) {
|
||||
qglColor4fv( color );
|
||||
} else if ( !tri->primBatchMesh ) {
|
||||
qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), (void *)&ac->color );
|
||||
if ( surf->mFlags & 1 ) {
|
||||
const byte *decalColors = reinterpret_cast<const byte *>( ac ) +
|
||||
tri->numVerts * sizeof( idDrawVert ) + stage * tri->numVerts * 4;
|
||||
qglColorPointer( 4, GL_UNSIGNED_BYTE, 4, decalColors );
|
||||
} else {
|
||||
qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), (void *)&ac->color );
|
||||
}
|
||||
GL_EnableVertexAttribState( 4 );
|
||||
|
||||
if ( pStage->vertexColor == SVC_INVERSE_MODULATE ) {
|
||||
@@ -905,7 +921,7 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
|
||||
|
||||
// for vertex color and modulated color, we need to enable a second
|
||||
// texture stage
|
||||
if ( color[0] != 1 || color[1] != 1 || color[2] != 1 || color[3] != 1 ) {
|
||||
if ( color[0] != 1 || color[1] != 1 || color[2] != 1 || color[3] != 1 || ( surf->mFlags & 1 ) ) {
|
||||
GL_SelectTexture( 1 );
|
||||
|
||||
globalImages->whiteImage->Bind();
|
||||
|
||||
Reference in New Issue
Block a user