jkxr/Projects/Android/jni/OpenJK/code/cgame/FxPrimitives.cpp

2320 lines
54 KiB
C++
Raw Normal View History

/*
===========================================================================
Copyright (C) 2000 - 2013, Raven Software, Inc.
Copyright (C) 2001 - 2013, Activision, Inc.
Copyright (C) 2013 - 2015, OpenJK contributors
This file is part of the OpenJK source code.
OpenJK is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
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
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, see <http://www.gnu.org/licenses/>.
===========================================================================
*/
#include "common_headers.h"
#if !defined(FX_SCHEDULER_H_INC)
#include "FxScheduler.h"
#endif
#include "cg_media.h"
extern int drawnFx;
extern int mParticles;
extern int mOParticles;
extern int mLines;
extern int mTails;
extern vmCvar_t fx_expensivePhysics;
// Helper function
//-------------------------
void ClampVec( vec3_t dat, byte *res )
{
int r;
// clamp all vec values, then multiply the normalized values by 255 to maximize the result
for ( int i = 0; i < 3; i++ )
{
r = Q_ftol(dat[i] * 255.0f);
if ( r < 0 )
{
r = 0;
}
else if ( r > 255 )
{
r = 255;
}
res[i] = (unsigned char)r;
}
}
void GetOrigin( int clientID, vec3_t org )
{
if ( clientID >=0 )
{
centity_t *cent = &cg_entities[clientID];
if ( cent && cent->gent && cent->gent->client )
{
VectorCopy( cent->gent->client->renderInfo.muzzlePoint, org );
}
}
}
void GetDir( int clientID, vec3_t org )
{
if ( clientID >=0 )
{
centity_t *cent = &cg_entities[clientID];
if ( cent && cent->gent && cent->gent->client )
{
VectorCopy( cent->gent->client->renderInfo.muzzleDir, org );
}
}
}
//--------------------------
//
// Base Effect Class
//
//--------------------------
//--------------------------
//
// Derived Particle Class
//
//--------------------------
void CParticle::Die()
{
if ( mFlags & FX_DEATH_RUNS_FX && !(mFlags & FX_KILL_ON_IMPACT) )
{
vec3_t norm;
// Man, this just seems so, like, uncool and stuff...
VectorSet( norm, Q_flrand(-1.0f, 1.0f), Q_flrand(-1.0f, 1.0f), Q_flrand(-1.0f, 1.0f));
VectorNormalize( norm );
theFxScheduler.PlayEffect( mDeathFxID, mOrigin1, norm );
}
}
//----------------------------
bool CParticle::Cull()
{
vec3_t dir;
// Get the direction to the view
VectorSubtract( mOrigin1, cg.refdef.vieworg, dir );
// Check if it's behind the viewer
if ( (DotProduct( cg.refdef.viewaxis[0], dir )) < 0 )
{
return true;
}
float len = VectorLengthSquared( dir );
// Can't be too close
if ( len < 16 * 16 )
{
return true;
}
return false;
}
//----------------------------
void CParticle::Draw()
{
if ( mFlags & FX_DEPTH_HACK )
{
// Not sure if first person needs to be set, but it can't hurt?
mRefEnt.renderfx |= RF_DEPTHHACK;
}
// Add our refEntity to the scene
VectorCopy( mOrigin1, mRefEnt.origin );
theFxHelper.AddFxToScene( &mRefEnt );
drawnFx++;
mParticles++;
}
//----------------------------
// Update
//----------------------------
bool CParticle::Update()
{
// Game pausing can cause dumb time things to happen, so kill the effect in this instance
if ( mTimeStart > theFxHelper.mTime )
{
return false;
}
if ( mFlags & FX_RELATIVE )
{
if ( mClientID < 0 || mClientID >= ENTITYNUM_WORLD )
{ // we are somehow not bolted even though the flag is on?
return false;
}
vec3_t org;
vec3_t ax[3];
// Get our current position and direction
if (mModelNum>=0 && mBoltNum>=0) //bolt style
{
const centity_t &cent = cg_entities[mClientID];
if (!cent.gent->ghoul2.IsValid())
{
return false;
}
if (!theFxHelper.GetOriginAxisFromBolt(cent, mModelNum, mBoltNum, org, ax))
{ //could not get bolt
return false;
}
}
else
{//fixme change this to bolt style...
vec3_t dir, ang;
GetOrigin( mClientID, org );
GetDir( mClientID, dir );
vectoangles( dir, ang );
AngleVectors( ang, ax[0], ax[1], ax[2] );
}
vec3_t realVel, realAccel;
VectorMA( org, mOrgOffset[0], ax[0], org );
VectorMA( org, mOrgOffset[1], ax[1], org );
VectorMA( org, mOrgOffset[2], ax[2], org );
const float time = (theFxHelper.mTime - mTimeStart) * 0.001f;
// calc the real velocity and accel vectors
VectorScale( ax[0], mVel[0], realVel );
VectorMA( realVel, mVel[1], ax[1], realVel );
VectorMA( realVel, mVel[2], ax[2], realVel );
realVel[2] += 0.5f * mGravity * time;
VectorScale( ax[0], mAccel[0], realAccel );
VectorMA( realAccel, mAccel[1], ax[1], realAccel );
VectorMA( realAccel, mAccel[2], ax[2], realAccel );
// Get our real velocity at the current time, taking into account the effects of acceleartion. NOTE: not sure if this is even 100% correct math-wise
VectorMA( realVel, time, realAccel, realVel );
// Now move us to where we should be at the given time
VectorMA( org, time, realVel, mOrigin1 );
}
else if (( mTimeStart < theFxHelper.mTime ) && UpdateOrigin() == false )
{
// we are marked for death
return false;
}
if ( !Cull())
{
UpdateSize();
UpdateRGB();
UpdateAlpha();
UpdateRotation();
Draw();
}
return true;
}
//----------------------------
// Update Origin
//----------------------------
bool CParticle::UpdateOrigin()
{
vec3_t new_origin;
// float ftime, time2;
UpdateVelocity();
// Calc the time differences
// ftime = theFxHelper.mFrameTime * 0.001f;
//time2 = ftime * ftime * 0.5f;
// time2=0;
// Predict the new position
new_origin[0] = mOrigin1[0] + theFxHelper.mFloatFrameTime * mVel[0];// + time2 * mVel[0];
new_origin[1] = mOrigin1[1] + theFxHelper.mFloatFrameTime * mVel[1];// + time2 * mVel[1];
new_origin[2] = mOrigin1[2] + theFxHelper.mFloatFrameTime * mVel[2];// + time2 * mVel[2];
// Only perform physics if this object is tagged to do so
if ( (mFlags & FX_APPLY_PHYSICS) )
{
bool solid;
if ( (mFlags&FX_EXPENSIVE_PHYSICS)
&& fx_expensivePhysics.integer )
{
solid = true; // by setting this to true, we force a real trace to happen
}
else
{
// if this returns solid, we need to do a trace
solid = !!(CG_PointContents( new_origin, ENTITYNUM_WORLD ) & ( MASK_SHOT | CONTENTS_WATER ));
}
if ( solid )
{
trace_t trace;
float dot;
if ( mFlags & FX_USE_BBOX )
{
if (mFlags & FX_GHOUL2_TRACE)
{
theFxHelper.G2Trace( &trace, mOrigin1, mMin, mMax, new_origin, ENTITYNUM_NONE, ( MASK_SHOT | CONTENTS_WATER ) );
}
else
{
theFxHelper.Trace( &trace, mOrigin1, mMin, mMax, new_origin, -1, ( MASK_SHOT | CONTENTS_WATER ) );
}
}
else
{
if (mFlags & FX_GHOUL2_TRACE)
{
theFxHelper.G2Trace( &trace, mOrigin1, NULL, NULL, new_origin, ENTITYNUM_NONE, ( MASK_SHOT | CONTENTS_WATER ) );
}
else
{
theFxHelper.Trace( &trace, mOrigin1, NULL, NULL, new_origin, -1, ( MASK_SHOT | CONTENTS_WATER ) );
}
}
if ( trace.startsolid || trace.allsolid || trace.fraction == 1.0)
{
}
else
{
// Hit something
if ( mFlags & FX_IMPACT_RUNS_FX && !(trace.surfaceFlags & SURF_NOIMPACT ))
{
theFxScheduler.PlayEffect( mImpactFxID, trace.endpos, trace.plane.normal );
}
if ( mFlags & FX_KILL_ON_IMPACT )
{
// time to die
return false;
}
VectorMA( mVel, theFxHelper.mFloatFrameTime * trace.fraction, mAccel, mVel );
dot = DotProduct( mVel, trace.plane.normal );
VectorMA( mVel, -2 * dot, trace.plane.normal, mVel );
VectorScale( mVel, mElasticity, mVel );
// If the velocity is too low, make it stop moving, rotating, and turn off physics to avoid
// doing expensive operations when they aren't needed
if ( trace.plane.normal[2] > 0 && mVel[2] < 4 )
{
VectorClear( mVel );
VectorClear( mAccel );
mFlags &= ~(FX_APPLY_PHYSICS|FX_IMPACT_RUNS_FX);
}
// Set the origin to the exact impact point
VectorCopy( trace.endpos, mOrigin1 );
return true;
}
}
}
// No physics were done to this object, move it
VectorCopy( new_origin, mOrigin1 );
return true;
}
//----------------------------
// Update Size
//----------------------------
void CParticle::UpdateSize()
{
// completely biased towards start if it doesn't get overridden
float perc1 = 1.0f, perc2 = 1.0f;
if ( (mFlags & FX_SIZE_LINEAR) )
{
// calculate element biasing
perc1 = 1.0f - (float)(theFxHelper.mTime - mTimeStart)
/ (float)(mTimeEnd - mTimeStart);
}
// We can combine FX_LINEAR with _either_ FX_NONLINEAR, FX_WAVE, or FX_CLAMP
if (( mFlags & FX_SIZE_PARM_MASK ) == FX_SIZE_NONLINEAR )
{
if ( theFxHelper.mTime > mSizeParm )
{
// get percent done, using parm as the start of the non-linear fade
perc2 = 1.0f - (float)(theFxHelper.mTime - mSizeParm)
/ (float)(mTimeEnd - mSizeParm);
}
if ( mFlags & FX_SIZE_LINEAR )
{
// do an even blend
perc1 = perc1 * 0.5f + perc2 * 0.5f;
}
else
{
// just copy it over...sigh
perc1 = perc2;
}
}
else if (( mFlags & FX_SIZE_PARM_MASK ) == FX_SIZE_WAVE )
{
// wave gen, with parm being the frequency multiplier
perc1 = perc1 * (float)cos( (theFxHelper.mTime - mTimeStart) * mSizeParm );
}
else if (( mFlags & FX_SIZE_PARM_MASK ) == FX_SIZE_CLAMP )
{
if ( theFxHelper.mTime < mSizeParm )
{
// get percent done, using parm as the start of the non-linear fade
perc2 = (float)(mSizeParm - theFxHelper.mTime)
/ (float)(mSizeParm - mTimeStart);
}
else
{
perc2 = 0.0f; // make it full size??
}
if ( (mFlags & FX_SIZE_LINEAR) )
{
// do an even blend
perc1 = perc1 * 0.5f + perc2 * 0.5f;
}
else
{
// just copy it over...sigh
perc1 = perc2;
}
}
// If needed, RAND can coexist with linear and either non-linear or wave.
if (( mFlags & FX_SIZE_RAND ))
{
// Random simply modulates the existing value
perc1 = Q_flrand(0.0f, 1.0f) * perc1;
}
mRefEnt.radius = (mSizeStart * perc1) + (mSizeEnd * (1.0f - perc1));
}
//----------------------------
// Update RGB
//----------------------------
void CParticle::UpdateRGB()
{
// completely biased towards start if it doesn't get overridden
float perc1 = 1.0f, perc2 = 1.0f;
vec3_t res;
if ( (mFlags & FX_RGB_LINEAR) )
{
// calculate element biasing
perc1 = 1.0f - (float)( theFxHelper.mTime - mTimeStart )
/ (float)( mTimeEnd - mTimeStart );
}
// We can combine FX_LINEAR with _either_ FX_NONLINEAR, FX_WAVE, or FX_CLAMP
if (( mFlags & FX_RGB_PARM_MASK ) == FX_RGB_NONLINEAR )
{
if ( theFxHelper.mTime > mRGBParm )
{
// get percent done, using parm as the start of the non-linear fade
perc2 = 1.0f - (float)( theFxHelper.mTime - mRGBParm )
/ (float)( mTimeEnd - mRGBParm );
}
if ( (mFlags & FX_RGB_LINEAR) )
{
// do an even blend
perc1 = perc1 * 0.5f + perc2 * 0.5f;
}
else
{
// just copy it over...sigh
perc1 = perc2;
}
}
else if (( mFlags & FX_RGB_PARM_MASK ) == FX_RGB_WAVE )
{
// wave gen, with parm being the frequency multiplier
perc1 = perc1 * (float)cos(( theFxHelper.mTime - mTimeStart ) * mRGBParm );
}
else if (( mFlags & FX_RGB_PARM_MASK ) == FX_RGB_CLAMP )
{
if ( theFxHelper.mTime < mRGBParm )
{
// get percent done, using parm as the start of the non-linear fade
perc2 = (float)(mRGBParm - theFxHelper.mTime)
/ (float)(mRGBParm - mTimeStart);
}
else
{
perc2 = 0.0f; // make it full size??
}
if (( mFlags & FX_RGB_LINEAR ))
{
// do an even blend
perc1 = perc1 * 0.5f + perc2 * 0.5f;
}
else
{
// just copy it over...sigh
perc1 = perc2;
}
}
// If needed, RAND can coexist with linear and either non-linear or wave.
if (( mFlags & FX_RGB_RAND ))
{
// Random simply modulates the existing value
perc1 = Q_flrand(0.0f, 1.0f) * perc1;
}
// Now get the correct color
VectorScale( mRGBStart, perc1, res );
VectorMA( res, (1.0f - perc1), mRGBEnd, mRefEnt.angles ); // angles is a temp storage, will get clamped to a byte in the UpdateAlpha section
}
//----------------------------
// Update Alpha
//----------------------------
void CParticle::UpdateAlpha()
{
// completely biased towards start if it doesn't get overridden
float perc1 = 1.0f, perc2 = 1.0f;
if ( mFlags & FX_ALPHA_LINEAR )
{
// calculate element biasing
perc1 = 1.0f - (float)(theFxHelper.mTime - mTimeStart)
/ (float)(mTimeEnd - mTimeStart);
}
// We can combine FX_LINEAR with _either_ FX_NONLINEAR, FX_WAVE, or FX_CLAMP
if (( mFlags & FX_ALPHA_PARM_MASK ) == FX_ALPHA_NONLINEAR )
{
if ( theFxHelper.mTime > mAlphaParm )
{
// get percent done, using parm as the start of the non-linear fade
perc2 = 1.0f - (float)(theFxHelper.mTime - mAlphaParm)
/ (float)(mTimeEnd - mAlphaParm);
}
if ( mFlags & FX_ALPHA_LINEAR )
{
// do an even blend
perc1 = perc1 * 0.5f + perc2 * 0.5f;
}
else
{
// just copy it over...sigh
perc1 = perc2;
}
}
else if (( mFlags & FX_ALPHA_PARM_MASK ) == FX_ALPHA_WAVE )
{
// wave gen, with parm being the frequency multiplier
perc1 = perc1 * (float)cos( (theFxHelper.mTime - mTimeStart) * mAlphaParm );
}
else if (( mFlags & FX_ALPHA_PARM_MASK ) == FX_ALPHA_CLAMP )
{
if ( theFxHelper.mTime < mAlphaParm )
{
// get percent done, using parm as the start of the non-linear fade
perc2 = (float)(mAlphaParm - theFxHelper.mTime)
/ (float)(mAlphaParm - mTimeStart);
}
else
{
perc2 = 0.0f; // make it full size??
}
if ( mFlags & FX_ALPHA_LINEAR )
{
// do an even blend
perc1 = perc1 * 0.5f + perc2 * 0.5f;
}
else
{
// just copy it over...sigh
perc1 = perc2;
}
}
perc1 = (mAlphaStart * perc1) + (mAlphaEnd * (1.0f - perc1));
// We should be in the right range, but clamp to ensure
if ( perc1 < 0.0f )
{
perc1 = 0.0f;
}
else if ( perc1 > 1.0f )
{
perc1 = 1.0f;
}
// If needed, RAND can coexist with linear and either non-linear or wave.
if ( (mFlags & FX_ALPHA_RAND) )
{
// Random simply modulates the existing value
perc1 = Q_flrand(0.0f, 1.0f) * perc1;
}
if ( mFlags & FX_USE_ALPHA )
{
// should use this when using art that has an alpha channel
ClampVec( mRefEnt.angles, (byte*)(&mRefEnt.shaderRGBA) );
mRefEnt.shaderRGBA[3] = (byte)(perc1 * 0xff);
}
else
{
// Modulate the rgb fields by the alpha value to do the fade, works fine for additive blending
VectorScale( mRefEnt.angles, perc1, mRefEnt.angles );
ClampVec( mRefEnt.angles, (byte*)(&mRefEnt.shaderRGBA) );
}
}
//--------------------------------
//
// Derived Oriented Particle Class
//
//--------------------------------
//----------------------------
bool COrientedParticle::Cull()
{
vec3_t dir;
// Get the direction to the view
VectorSubtract( mOrigin1, cg.refdef.vieworg, dir );
// Check if it's behind the viewer
if ( (DotProduct( cg.refdef.viewaxis[0], dir )) < 0 )
{
return true;
}
float len = VectorLengthSquared( dir );
// Can't be too close
if ( len < 24 * 24 )
{
return true;
}
return false;
}
//----------------------------
void COrientedParticle::Draw()
{
if ( mFlags & FX_DEPTH_HACK )
{
// Not sure if first person needs to be set
mRefEnt.renderfx |= RF_DEPTHHACK;
}
// Add our refEntity to the scene
VectorCopy( mOrigin1, mRefEnt.origin );
VectorCopy( mNormal, mRefEnt.axis[0] );
theFxHelper.AddFxToScene( &mRefEnt );
drawnFx++;
mOParticles++;
}
//----------------------------
// Update
//----------------------------
bool COrientedParticle::Update()
{
// Game pausing can cause dumb time things to happen, so kill the effect in this instance
if ( mTimeStart > theFxHelper.mTime )
{
return false;
}
if ( mFlags & FX_RELATIVE )
{
if ( mClientID < 0 || mClientID >= ENTITYNUM_WORLD )
{ // we are somehow not bolted even though the flag is on?
return false;
}
vec3_t org;
vec3_t ax[3];
// Get our current position and direction
if (mModelNum>=0 && mBoltNum>=0) //bolt style
{
const centity_t &cent = cg_entities[mClientID];
if (!cent.gent->ghoul2.IsValid())
{
return false;
}
if (!theFxHelper.GetOriginAxisFromBolt(cent, mModelNum, mBoltNum, org, ax))
{ //could not get bolt
return false;
}
}
else
{//fixme change this to bolt style...
vec3_t dir, ang;
GetOrigin( mClientID, org );
GetDir( mClientID, dir );
vectoangles( dir, ang );
AngleVectors( ang, ax[0], ax[1], ax[2] );
}
vec3_t realVel, realAccel;
VectorMA( org, mOrgOffset[0], ax[0], org );
VectorMA( org, mOrgOffset[1], ax[1], org );
VectorMA( org, mOrgOffset[2], ax[2], org );
const float time = (theFxHelper.mTime - mTimeStart) * 0.001f;
// calc the real velocity and accel vectors
VectorScale( ax[0], mVel[0], realVel );
VectorMA( realVel, mVel[1], ax[1], realVel );
VectorMA( realVel, mVel[2], ax[2], realVel );
realVel[2] += 0.5f * mGravity * time;
VectorScale( ax[0], mAccel[0], realAccel );
VectorMA( realAccel, mAccel[1], ax[1], realAccel );
VectorMA( realAccel, mAccel[2], ax[2], realAccel );
// Get our real velocity at the current time, taking into account the effects of acceleartion. NOTE: not sure if this is even 100% correct math-wise
VectorMA( realVel, time, realAccel, realVel );
// Now move us to where we should be at the given time
VectorMA( org, time, realVel, mOrigin1 );
//use the normalOffset and add that to the actual normal of the bolt
//NOTE: not tested!!!
vec3_t boltAngles, offsetAngles, transformedAngles;
vectoangles( ax[0], boltAngles );
vectoangles( mNormalOffset, offsetAngles );
VectorAdd( boltAngles, offsetAngles, transformedAngles );
AngleVectors( transformedAngles, mNormal, NULL, NULL );
}
else if (( mTimeStart < theFxHelper.mTime ) && UpdateOrigin() == false )
{
// we are marked for death
return false;
}
if ( !Cull())
{
UpdateSize();
UpdateRGB();
UpdateAlpha();
UpdateRotation();
Draw();
}
return true;
}
//----------------------------
//
// Derived Line Class
//
//----------------------------
//----------------------------
void CLine::Draw()
{
if ( mFlags & FX_DEPTH_HACK )
{
// Not sure if first person needs to be set, but it can't hurt?
mRefEnt.renderfx |= RF_DEPTHHACK;
}
VectorCopy( mOrigin1, mRefEnt.origin );
VectorCopy( mOrigin2, mRefEnt.oldorigin );
theFxHelper.AddFxToScene( &mRefEnt );
drawnFx++;
mLines++;
}
//----------------------------
bool CLine::Update()
{
// Game pausing can cause dumb time things to happen, so kill the effect in this instance
if ( mTimeStart > theFxHelper.mTime )
{
return false;
}
if ( mFlags & FX_RELATIVE )
{
if ( mClientID < 0 || mClientID >= ENTITYNUM_WORLD )
{ // we are somehow not bolted even though the flag is on?
return false;
}
vec3_t ax[3] = {};
// Get our current position and direction
if (mModelNum>=0 && mBoltNum>=0) //bolt style
{
const centity_t &cent = cg_entities[mClientID];
if (!cent.gent->ghoul2.IsValid())
{
return false;
}
if (!theFxHelper.GetOriginAxisFromBolt(cent, mModelNum, mBoltNum, mOrigin1, ax))
{ //could not get bolt
return false;
}
}
else
{//fixme change this to bolt style...
// Get our current position and direction
GetOrigin( mClientID, mOrigin1 );
GetDir( mClientID, ax[0] );
}
VectorAdd(mOrigin1, mOrgOffset, mOrigin1); //add the offset to the bolt point
vec3_t end;
trace_t trace;
if ( mFlags & FX_APPLY_PHYSICS )
{
VectorMA( mOrigin1, 2048, ax[0], end );
theFxHelper.Trace( &trace, mOrigin1, NULL, NULL, end, mClientID, MASK_SHOT );
VectorCopy( trace.endpos, mOrigin2 );
if ( mImpactFxID > 0 )
{
theFxScheduler.PlayEffect( mImpactFxID, trace.endpos, trace.plane.normal );
}
}
else
{
VectorMA( mOrigin1, mVel[0], ax[0], mOrigin2 );
VectorMA( mOrigin2, mVel[1], ax[1], mOrigin2 );
VectorMA( mOrigin2, mVel[2], ax[2], mOrigin2 );
}
}
UpdateSize();
UpdateRGB();
UpdateAlpha();
Draw();
return true;
}
//----------------------------
//
// Derived Electricity Class
//
//----------------------------
void CElectricity::Initialize()
{
mRefEnt.frame = Q_flrand(0.0f, 1.0f) * 1265536;
mRefEnt.endTime = cg.time + (mTimeEnd - mTimeStart);
if ( mFlags & FX_DEPTH_HACK )
{
mRefEnt.renderfx |= RF_DEPTHHACK;
}
if ( mFlags & FX_BRANCH )
{
mRefEnt.renderfx |= RF_FORKED;
}
if ( mFlags & FX_TAPER )
{
mRefEnt.renderfx |= RF_TAPERED;
}
if ( mFlags & FX_GROW )
{
mRefEnt.renderfx |= RF_GROW;
}
}
//----------------------------
void CElectricity::Draw()
{
VectorCopy( mOrigin1, mRefEnt.origin );
VectorCopy( mOrigin2, mRefEnt.oldorigin );
mRefEnt.angles[0] = mChaos;
mRefEnt.angles[1] = mTimeEnd - mTimeStart;
theFxHelper.AddFxToScene( &mRefEnt );
drawnFx++;
mLines++; // NOT REALLY A LINE!
}
//----------------------------
bool CElectricity::Update()
{
// Game pausing can cause dumb time things to happen, so kill the effect in this instance
if ( mTimeStart > theFxHelper.mTime )
{
return false;
}
//Handle Relative and Bolted Effects
if ( mFlags & FX_RELATIVE )
{//add mOrgOffset to bolt position and store in mOrigin1
if ( mClientID < 0 || mClientID >= ENTITYNUM_WORLD )
{ // we are somehow not bolted even though the flag is on?
return false;
}
vec3_t ax[3] = {};
// Get our current position and direction
if (mModelNum>=0 && mBoltNum>=0) //bolt style
{
const centity_t &cent = cg_entities[mClientID];
if (!cent.gent->ghoul2.IsValid())
{
return false;
}
if (!theFxHelper.GetOriginAxisFromBolt(cent, mModelNum, mBoltNum, mOrigin1, ax))
{ //could not get bolt
return false;
}
}
else
{//fixme change this to bolt style...
// Get our current position and direction
GetOrigin( mClientID, mOrigin1 );
GetDir( mClientID, ax[0] );
}
//add the offset to the bolt point
VectorAdd(mOrigin1, mOrgOffset, mOrigin1);
//add the endpoint offset to the start to get the final offset
VectorMA( mOrigin1, mVel[0], ax[0], mOrigin2 );
VectorMA( mOrigin2, mVel[1], ax[1], mOrigin2 );
VectorMA( mOrigin2, mVel[2], ax[2], mOrigin2 );
}
//else just uses the static origin1 & origin2 as start and end
UpdateSize();
UpdateRGB();
UpdateAlpha();
Draw();
return true;
}
//----------------------------
//
// Derived Tail Class
//
//----------------------------
bool CTail::Cull()
{
vec3_t dir;
// Get the direction to the view
VectorSubtract( mOrigin1, cg.refdef.vieworg, dir );
// Check if it's behind the viewer
if ( (DotProduct( cg.refdef.viewaxis[0], dir )) < 0 )
{
return true;
}
return false;
}
//----------------------------
void CTail::Draw()
{
if ( mFlags & FX_DEPTH_HACK )
{
// Not sure if first person needs to be set
mRefEnt.renderfx |= RF_DEPTHHACK;
}
VectorCopy( mOrigin1, mRefEnt.origin );
theFxHelper.AddFxToScene( &mRefEnt );
drawnFx++;
mTails++;
}
//----------------------------
bool CTail::Update()
{
// Game pausing can cause dumb time things to happen, so kill the effect in this instance
if ( mTimeStart > theFxHelper.mTime )
{
return false;
}
if ( !fx_freeze.integer )
{
VectorCopy( mOrigin1, mOldOrigin );
}
if ( mFlags & FX_RELATIVE )
{
if ( mClientID < 0 || mClientID >= ENTITYNUM_WORLD )
{ // we are somehow not bolted even though the flag is on?
return false;
}
vec3_t org;
vec3_t ax[3];
if (mModelNum>=0 && mBoltNum>=0) //bolt style
{
const centity_t &cent = cg_entities[mClientID];
if (!cent.gent->ghoul2.IsValid())
{
return false;
}
if (!theFxHelper.GetOriginAxisFromBolt(cent, mModelNum, mBoltNum, org, ax))
{ //could not get bolt
return false;
}
}
else
{
vec3_t dir;
// Get our current position and direction
GetOrigin( mClientID, org );
GetDir( mClientID, dir );
vec3_t ang;
vectoangles( dir, ang );
AngleVectors( ang, ax[0], ax[1], ax[2] );
}
vec3_t realVel, realAccel;
VectorMA( org, mOrgOffset[0], ax[0], org );
VectorMA( org, mOrgOffset[1], ax[1], org );
VectorMA( org, mOrgOffset[2], ax[2], org );
// calc the real velocity and accel vectors
// FIXME: if you want right and up movement in addition to the forward movement, you'll have to convert dir into a set of perp. axes and do some extra work
VectorScale( ax[0], mVel[0], realVel );
VectorMA( realVel, mVel[1], ax[1], realVel );
VectorMA( realVel, mVel[2], ax[2], realVel );
VectorScale( ax[0], mAccel[0], realAccel );
VectorMA( realAccel, mAccel[1], ax[1], realAccel );
VectorMA( realAccel, mAccel[2], ax[2], realAccel );
const float time = (theFxHelper.mTime - mTimeStart) * 0.001f;
// Get our real velocity at the current time, taking into account the effects of acceleration. NOTE: not sure if this is even 100% correct math-wise
VectorMA( realVel, time, realAccel, realVel );
// Now move us to where we should be at the given time
VectorMA( org, time, realVel, mOrigin1 );
// Just calc an old point some time in the past, doesn't really matter when
VectorMA( org, (time - 0.003f), realVel, mOldOrigin );
}
else if (( mTimeStart < theFxHelper.mTime ) && UpdateOrigin() == false )
{
// we are marked for death
return false;
}
if ( !Cull() )
{
UpdateSize();
UpdateLength();
UpdateRGB();
UpdateAlpha();
CalcNewEndpoint();
Draw();
}
return true;
}
//----------------------------
void CTail::UpdateLength()
{
// completely biased towards start if it doesn't get overridden
float perc1 = 1.0f, perc2 = 1.0f;
if ( mFlags & FX_LENGTH_LINEAR )
{
// calculate element biasing
perc1 = 1.0f - (float)(theFxHelper.mTime - mTimeStart)
/ (float)(mTimeEnd - mTimeStart);
}
// We can combine FX_LINEAR with _either_ FX_NONLINEAR or FX_WAVE
if (( mFlags & FX_LENGTH_PARM_MASK ) == FX_LENGTH_NONLINEAR )
{
if ( theFxHelper.mTime > mLengthParm )
{
// get percent done, using parm as the start of the non-linear fade
perc2 = 1.0f - (float)(theFxHelper.mTime - mLengthParm)
/ (float)(mTimeEnd - mLengthParm);
}
if ( mFlags & FX_LENGTH_LINEAR )
{
// do an even blend
perc1 = perc1 * 0.5f + perc2 * 0.5f;
}
else
{
// just copy it over...sigh
perc1 = perc2;
}
}
else if (( mFlags & FX_LENGTH_PARM_MASK ) == FX_LENGTH_WAVE )
{
// wave gen, with parm being the frequency multiplier
perc1 = perc1 * (float)cos( (theFxHelper.mTime - mTimeStart) * mLengthParm );
}
else if (( mFlags & FX_LENGTH_PARM_MASK ) == FX_LENGTH_CLAMP )
{
if ( theFxHelper.mTime < mLengthParm )
{
// get percent done, using parm as the start of the non-linear fade
perc2 = (float)(mLengthParm - theFxHelper.mTime)
/ (float)(mLengthParm - mTimeStart);
}
else
{
perc2 = 0.0f; // make it full size??
}
if ( mFlags & FX_LENGTH_LINEAR )
{
// do an even blend
perc1 = perc1 * 0.5f + perc2 * 0.5f;
}
else
{
// just copy it over...sigh
perc1 = perc2;
}
}
// If needed, RAND can coexist with linear and either non-linear or wave.
if ( mFlags & FX_LENGTH_RAND )
{
// Random simply modulates the existing value
perc1 = Q_flrand(0.0f, 1.0f) * perc1;
}
mLength = (mLengthStart * perc1) + (mLengthEnd * (1.0f - perc1));
}
//----------------------------
void CTail::CalcNewEndpoint()
{
vec3_t temp;
// FIXME: Hmmm, this looks dumb when physics are on and a bounce happens
VectorSubtract( mOldOrigin, mOrigin1, temp );
// I wish we didn't have to do a VectorNormalize every frame.....
VectorNormalize( temp );
VectorMA( mOrigin1, mLength, temp, mRefEnt.oldorigin );
}
//----------------------------
//
// Derived Cylinder Class
//
//----------------------------
void CCylinder::Draw()
{
if ( mFlags & FX_DEPTH_HACK )
{
// Not sure if first person needs to be set, but it can't hurt?
mRefEnt.renderfx |= RF_DEPTHHACK;
}
VectorCopy( mOrigin1, mRefEnt.origin );
VectorMA( mOrigin1, mLength, mRefEnt.axis[0], mRefEnt.oldorigin );
theFxHelper.AddFxToScene( &mRefEnt );
drawnFx++;
}
//----------------------------
// Update Size2
//----------------------------
void CCylinder::UpdateSize2()
{
// completely biased towards start if it doesn't get overridden
float perc1 = 1.0f, perc2 = 1.0f;
if ( mFlags & FX_SIZE2_LINEAR )
{
// calculate element biasing
perc1 = 1.0f - (float)(theFxHelper.mTime - mTimeStart)
/ (float)(mTimeEnd - mTimeStart);
}
// We can combine FX_LINEAR with _either_ FX_NONLINEAR or FX_WAVE
if (( mFlags & FX_SIZE2_PARM_MASK ) == FX_SIZE2_NONLINEAR )
{
if ( theFxHelper.mTime > mSize2Parm )
{
// get percent done, using parm as the start of the non-linear fade
perc2 = 1.0f - (float)(theFxHelper.mTime - mSize2Parm)
/ (float)(mTimeEnd - mSize2Parm);
}
if ( (mFlags & FX_SIZE2_LINEAR) )
{
// do an even blend
perc1 = perc1 * 0.5f + perc2 * 0.5f;
}
else
{
// just copy it over...sigh
perc1 = perc2;
}
}
else if (( mFlags & FX_SIZE2_PARM_MASK ) == FX_SIZE2_WAVE )
{
// wave gen, with parm being the frequency multiplier
perc1 = perc1 * (float)cos( (theFxHelper.mTime - mTimeStart) * mSize2Parm );
}
else if (( mFlags & FX_SIZE2_PARM_MASK ) == FX_SIZE2_CLAMP )
{
if ( theFxHelper.mTime < mSize2Parm )
{
// get percent done, using parm as the start of the non-linear fade
perc2 = (float)(mSize2Parm - theFxHelper.mTime)
/ (float)(mSize2Parm - mTimeStart);
}
else
{
perc2 = 0.0f; // make it full size??
}
if ( mFlags & FX_SIZE2_LINEAR )
{
// do an even blend
perc1 = perc1 * 0.5f + perc2 * 0.5f;
}
else
{
// just copy it over...sigh
perc1 = perc2;
}
}
// If needed, RAND can coexist with linear and either non-linear or wave.
if ( mFlags & FX_SIZE2_RAND )
{
// Random simply modulates the existing value
perc1 = Q_flrand(0.0f, 1.0f) * perc1;
}
mRefEnt.backlerp = (mSize2Start * perc1) + (mSize2End * (1.0f - perc1));
}
//----------------------------
bool CCylinder::Update()
{
// Game pausing can cause dumb time things to happen, so kill the effect in this instance
if ( mTimeStart > theFxHelper.mTime )
{
return false;
}
if ( mFlags & FX_RELATIVE )
{
if ( mClientID < 0 || mClientID >= ENTITYNUM_WORLD )
{ // we are somehow not bolted even though the flag is on?
return false;
}
vec3_t ax[3] = {};
// Get our current position and direction
if (mModelNum>=0 && mBoltNum>=0) //bolt style
{
const centity_t &cent = cg_entities[mClientID];
if (!cent.gent->ghoul2.IsValid())
{
return false;
}
if (!theFxHelper.GetOriginAxisFromBolt(cent, mModelNum, mBoltNum, mOrigin1, ax))
{ //could not get bolt
return false;
}
}
else
{//fixme change this to bolt style...
// Get our current position and direction
GetOrigin( mClientID, mOrigin1 );
GetDir( mClientID, ax[0] );
}
VectorAdd(mOrigin1, mOrgOffset, mOrigin1); //add the offset to the bolt point
VectorCopy( ax[0], mRefEnt.axis[0] );
//FIXME: should mNormal be a modifier on the forward axis?
/*
VectorMA( mOrigin1, mNormal[0], ax[0], mOrigin2 );
VectorMA( mOrigin2, mNormal[1], ax[1], mOrigin2 );
VectorMA( mOrigin2, mNormal[2], ax[2], mOrigin2 );
*/
}
UpdateSize();
UpdateSize2();
UpdateLength();
UpdateRGB();
UpdateAlpha();
Draw();
return true;
}
//----------------------------
//
// Derived Emitter Class
//
//----------------------------
//----------------------------
// Draw
//----------------------------
void CEmitter::Draw()
{
// Emitters don't draw themselves, but they may need to add an attached model
if ( mFlags & FX_ATTACHED_MODEL )
{
mRefEnt.nonNormalizedAxes = qtrue;
VectorCopy( mOrigin1, mRefEnt.origin );
// ensure that we are sized
for ( int i = 0; i < 3; i++ )
{
VectorScale( mRefEnt.axis[i], mRefEnt.radius, mRefEnt.axis[i] );
}
theFxHelper.AddFxToScene( &mRefEnt );
}
// If we are emitting effects, we had better be careful because just calling it every cgame frame could
// either choke up the effects system on a fast machine, or look really nasty on a low end one.
if ( mFlags & FX_EMIT_FX )
{
vec3_t org, v;
float ftime, time2,
step;
int i, t, dif;
#define TRAIL_RATE 8 // we "think" at about a 60hz rate
// Pick a target step distance and square it
step = mDensity + Q_flrand(-1.0f, 1.0f) * mVariance;
step *= step;
dif = 0;
for ( t = mOldTime; t <= theFxHelper.mTime; t += TRAIL_RATE )
{
dif += TRAIL_RATE;
// ?Not sure if it's better to update this before or after updating the origin
VectorMA( mOldVelocity, dif * 0.001f, mAccel, v );
// Calc the time differences
ftime = dif * 0.001f;
time2 = ftime * ftime * 0.5f;
// Predict the new position
for ( i = 0 ; i < 3 ; i++ )
{
org[i] = mOldOrigin[i] + ftime * v[i] + time2 * v[i];
}
// Only perform physics if this object is tagged to do so
if ( (mFlags & FX_APPLY_PHYSICS) )
{
bool solid;
if ( (mFlags&FX_EXPENSIVE_PHYSICS)
&& fx_expensivePhysics.integer )
{
solid = true; // by setting this to true, we force a real trace to happen
}
else
{
// if this returns solid, we need to do a trace
solid = !!(CG_PointContents( org, ENTITYNUM_WORLD ) & MASK_SHOT);
}
if ( solid )
{
trace_t trace;
if ( mFlags & FX_USE_BBOX )
{
theFxHelper.Trace( &trace, mOldOrigin, mMin, mMax, org, -1, MASK_SHOT );
}
else
{
theFxHelper.Trace( &trace, mOldOrigin, NULL, NULL, org, -1, MASK_SHOT );
}
// Hit something
if ( trace.fraction < 1.0f || trace.startsolid || trace.allsolid )
{
return;
}
}
}
// Is it time to draw an effect?
if ( DistanceSquared( org, mOldOrigin ) >= step )
{
// Pick a new target step distance and square it
step = mDensity + Q_flrand(-1.0f, 1.0f) * mVariance;
step *= step;
// We met the step criteria so, we should add in the effect
theFxScheduler.PlayEffect( mEmitterFxID, org, mRefEnt.axis );
VectorCopy( org, mOldOrigin );
VectorCopy( v, mOldVelocity );
dif = 0;
mOldTime = t;
}
}
}
drawnFx++;
}
//----------------------------
bool CEmitter::Update()
{
// Game pausing can cause dumb time things to happen, so kill the effect in this instance
if ( mTimeStart > theFxHelper.mTime )
{
return false;
}
//FIXME: Handle Relative and Bolted Effects
/*
if ( mFlags & FX_RELATIVE )
{
if ( mClientID < 0 || mClientID >= ENTITYNUM_WORLD )
{ // we are somehow not bolted even though the flag is on?
return false;
}
// Get our current position and direction
if (mModelNum>=0 && mBoltNum>=0) //bolt style
{
}
}
*/
// Use this to track if we've stopped moving
VectorCopy( mOrigin1, mOldOrigin );
VectorCopy( mVel, mOldVelocity );
if (( mTimeStart < theFxHelper.mTime ) && UpdateOrigin() == false )
{ // we are marked for death
return false;
}
// If the thing is no longer moving, kill the angle delta, but don't do it too quickly or it will
// look very artificial. Don't do it too slowly or it will look like there is no friction.
if ( VectorCompare( mOldOrigin, mOrigin1 ))
{
VectorScale( mAngleDelta, 0.6f, mAngleDelta );
}
UpdateAngles();
UpdateSize();
// UpdateRGB(); // had wanted to do something slick whereby an emitted effect could somehow inherit these
// UpdateAlpha(); // values, but it's not a priority right now.
Draw();
return true;
}
//----------------------------
void CEmitter::UpdateAngles()
{
VectorMA( mAngles, theFxHelper.mFrameTime * 0.01f, mAngleDelta, mAngles ); // was 0.001f, but then you really have to jack up the delta to even notice anything
AnglesToAxis( mAngles, mRefEnt.axis );
}
//--------------------------
//
// Derived Light Class
//
//--------------------------
//----------------------------
// Update
//----------------------------
bool CLight::Update()
{
// Game pausing can cause dumb time things to happen, so kill the effect in this instance
if ( mTimeStart > theFxHelper.mTime )
{
return false;
}
//FIXME: Handle Relative and Bolted Effects
/*
if ( mFlags & FX_RELATIVE )
{
if ( mClientID < 0 || mClientID >= ENTITYNUM_WORLD )
{ // we are somehow not bolted even though the flag is on?
return false;
}
// Get our current position and direction
if (mModelNum>=0 && mBoltNum>=0) //bolt style
{
}
}
*/
UpdateSize();
UpdateRGB();
Draw();
return true;
}
//----------------------------
// Update Size
//----------------------------
void CLight::UpdateSize()
{
// completely biased towards start if it doesn't get overridden
float perc1 = 1.0f, perc2 = 1.0f;
if ( mFlags & FX_SIZE_LINEAR )
{
// calculate element biasing
perc1 = 1.0f - (float)(theFxHelper.mTime - mTimeStart)
/ (float)(mTimeEnd - mTimeStart);
}
// We can combine FX_LINEAR with _either_ FX_NONLINEAR or FX_WAVE
if (( mFlags & FX_SIZE_PARM_MASK ) == FX_SIZE_NONLINEAR )
{
if ( theFxHelper.mTime > mSizeParm )
{
// get percent done, using parm as the start of the non-linear fade
perc2 = 1.0f - (float)(theFxHelper.mTime - mSizeParm)
/ (float)(mTimeEnd - mSizeParm);
}
if ( (mFlags & FX_SIZE_LINEAR) )
{
// do an even blend
perc1 = perc1 * 0.5f + perc2 * 0.5f;
}
else
{
// just copy it over...sigh
perc1 = perc2;
}
}
else if (( mFlags & FX_SIZE_PARM_MASK ) == FX_SIZE_WAVE )
{
// wave gen, with parm being the frequency multiplier
perc1 = perc1 * (float)cos( (theFxHelper.mTime - mTimeStart) * mSizeParm );
}
else if (( mFlags & FX_SIZE_PARM_MASK ) == FX_SIZE_CLAMP )
{
if ( theFxHelper.mTime < mSizeParm )
{
// get percent done, using parm as the start of the non-linear fade
perc2 = (float)(mSizeParm - theFxHelper.mTime)
/ (float)(mSizeParm - mTimeStart);
}
else
{
perc2 = 0.0f; // make it full size??
}
if ( mFlags & FX_SIZE_LINEAR )
{
// do an even blend
perc1 = perc1 * 0.5f + perc2 * 0.5f;
}
else
{
// just copy it over...sigh
perc1 = perc2;
}
}
// If needed, RAND can coexist with linear and either non-linear or wave.
if ( mFlags & FX_SIZE_RAND )
{
// Random simply modulates the existing value
perc1 = Q_flrand(0.0f, 1.0f) * perc1;
}
mRefEnt.radius = (mSizeStart * perc1) + (mSizeEnd * (1.0f - perc1));
}
//----------------------------
// Update RGB
//----------------------------
void CLight::UpdateRGB()
{
// completely biased towards start if it doesn't get overridden
float perc1 = 1.0f, perc2 = 1.0f;
vec3_t res;
if ( mFlags & FX_RGB_LINEAR )
{
// calculate element biasing
perc1 = 1.0f - (float)( theFxHelper.mTime - mTimeStart )
/ (float)( mTimeEnd - mTimeStart );
}
// We can combine FX_LINEAR with _either_ FX_NONLINEAR or FX_WAVE
if (( mFlags & FX_RGB_PARM_MASK ) == FX_RGB_NONLINEAR )
{
if ( theFxHelper.mTime > mRGBParm )
{
// get percent done, using parm as the start of the non-linear fade
perc2 = 1.0f - (float)( theFxHelper.mTime - mRGBParm )
/ (float)( mTimeEnd - mRGBParm );
}
if ( mFlags & FX_RGB_LINEAR )
{
// do an even blend
perc1 = perc1 * 0.5f + perc2 * 0.5f;
}
else
{
// just copy it over...sigh
perc1 = perc2;
}
}
else if (( mFlags & FX_RGB_PARM_MASK ) == FX_RGB_WAVE )
{
// wave gen, with parm being the frequency multiplier
perc1 = perc1 * (float)cos(( theFxHelper.mTime - mTimeStart ) * mRGBParm );
}
else if (( mFlags & FX_RGB_PARM_MASK ) == FX_RGB_CLAMP )
{
if ( theFxHelper.mTime < mRGBParm )
{
// get percent done, using parm as the start of the non-linear fade
perc2 = (float)(mRGBParm - theFxHelper.mTime)
/ (float)(mRGBParm - mTimeStart);
}
else
{
perc2 = 0.0f; // make it full size??
}
if ( mFlags & FX_RGB_LINEAR )
{
// do an even blend
perc1 = perc1 * 0.5f + perc2 * 0.5f;
}
else
{
// just copy it over...sigh
perc1 = perc2;
}
}
// If needed, RAND can coexist with linear and either non-linear or wave.
if ( mFlags & FX_RGB_RAND )
{
// Random simply modulates the existing value
perc1 = Q_flrand(0.0f, 1.0f) * perc1;
}
// Now get the correct color
VectorScale( mRGBStart, perc1, res );
mRefEnt.lightingOrigin[0] = res[0] + ( 1.0f - perc1 ) * mRGBEnd[0];
mRefEnt.lightingOrigin[1] = res[1] + ( 1.0f - perc1 ) * mRGBEnd[1];
mRefEnt.lightingOrigin[2] = res[2] + ( 1.0f - perc1 ) * mRGBEnd[2];
}
//--------------------------
//
// Derived Trail Class
//
//--------------------------
#define NEW_MUZZLE 0
#define NEW_TIP 1
#define OLD_TIP 2
#define OLD_MUZZLE 3
//----------------------------
void CTrail::Draw()
{
polyVert_t verts[3];
// vec3_t color;
// build the first tri out of the new muzzle...new tip...old muzzle
VectorCopy( mVerts[NEW_MUZZLE].origin, verts[0].xyz );
VectorCopy( mVerts[NEW_TIP].origin, verts[1].xyz );
VectorCopy( mVerts[OLD_MUZZLE].origin, verts[2].xyz );
// VectorScale( mVerts[NEW_MUZZLE].curRGB, mVerts[NEW_MUZZLE].curAlpha, color );
verts[0].modulate[0] = mVerts[NEW_MUZZLE].rgb[0];
verts[0].modulate[1] = mVerts[NEW_MUZZLE].rgb[1];
verts[0].modulate[2] = mVerts[NEW_MUZZLE].rgb[2];
verts[0].modulate[3] = mVerts[NEW_MUZZLE].alpha;
// VectorScale( mVerts[NEW_TIP].curRGB, mVerts[NEW_TIP].curAlpha, color );
verts[1].modulate[0] = mVerts[NEW_TIP].rgb[0];
verts[1].modulate[1] = mVerts[NEW_TIP].rgb[1];
verts[1].modulate[2] = mVerts[NEW_TIP].rgb[2];
verts[1].modulate[3] = mVerts[NEW_TIP].alpha;
// VectorScale( mVerts[OLD_MUZZLE].curRGB, mVerts[OLD_MUZZLE].curAlpha, color );
verts[2].modulate[0] = mVerts[OLD_MUZZLE].rgb[0];
verts[2].modulate[1] = mVerts[OLD_MUZZLE].rgb[1];
verts[2].modulate[2] = mVerts[OLD_MUZZLE].rgb[2];
verts[2].modulate[3] = mVerts[OLD_MUZZLE].alpha;
verts[0].st[0] = mVerts[NEW_MUZZLE].curST[0];
verts[0].st[1] = mVerts[NEW_MUZZLE].curST[1];
verts[1].st[0] = mVerts[NEW_TIP].curST[0];
verts[1].st[1] = mVerts[NEW_TIP].curST[1];
verts[2].st[0] = mVerts[OLD_MUZZLE].curST[0];
verts[2].st[1] = mVerts[OLD_MUZZLE].curST[1];
// Add this tri
theFxHelper.AddPolyToScene( mShader, 3, verts );
// build the second tri out of the old muzzle...old tip...new tip
VectorCopy( mVerts[OLD_MUZZLE].origin, verts[0].xyz );
VectorCopy( mVerts[OLD_TIP].origin, verts[1].xyz );
VectorCopy( mVerts[NEW_TIP].origin, verts[2].xyz );
// VectorScale( mVerts[OLD_MUZZLE].curRGB, mVerts[OLD_MUZZLE].curAlpha, color );
verts[0].modulate[0] = mVerts[OLD_MUZZLE].rgb[0];
verts[0].modulate[1] = mVerts[OLD_MUZZLE].rgb[1];
verts[0].modulate[2] = mVerts[OLD_MUZZLE].rgb[2];
verts[0].modulate[3] = mVerts[OLD_MUZZLE].alpha;
// VectorScale( mVerts[OLD_TIP].curRGB, mVerts[OLD_TIP].curAlpha, color );
verts[1].modulate[0] = mVerts[OLD_TIP].rgb[0];
verts[1].modulate[1] = mVerts[OLD_TIP].rgb[1];
verts[1].modulate[2] = mVerts[OLD_TIP].rgb[2];
verts[0].modulate[3] = mVerts[OLD_TIP].alpha;
// VectorScale( mVerts[NEW_TIP].curRGB, mVerts[NEW_TIP].curAlpha, color );
verts[2].modulate[0] = mVerts[NEW_TIP].rgb[0];
verts[2].modulate[1] = mVerts[NEW_TIP].rgb[1];
verts[2].modulate[2] = mVerts[NEW_TIP].rgb[2];
verts[0].modulate[3] = mVerts[NEW_TIP].alpha;
verts[0].st[0] = mVerts[OLD_MUZZLE].curST[0];
verts[0].st[1] = mVerts[OLD_MUZZLE].curST[1];
verts[1].st[0] = mVerts[OLD_TIP].curST[0];
verts[1].st[1] = mVerts[OLD_TIP].curST[1];
verts[2].st[0] = mVerts[NEW_TIP].curST[0];
verts[2].st[1] = mVerts[NEW_TIP].curST[1];
// Add this tri
theFxHelper.AddPolyToScene( mShader, 3, verts );
drawnFx++;
}
//----------------------------
// Update
//----------------------------
bool CTrail::Update()
{
// Game pausing can cause dumb time things to happen, so kill the effect in this instance
if ( mTimeStart > theFxHelper.mTime )
{
return false;
}
//FIXME: Handle Relative and Bolted Effects
/*
if ( mFlags & FX_RELATIVE )
{
if ( mClientID < 0 || mClientID >= ENTITYNUM_WORLD )
{ // we are somehow not bolted even though the flag is on?
return false;
}
// Get our current position and direction
if (mModelNum>=0 && mBoltNum>=0) //bolt style
{
}
}
*/
float perc = (float)(mTimeEnd - theFxHelper.mTime) / (float)(mTimeEnd - mTimeStart);
for ( int t = 0; t < 4; t++ )
{
// mVerts[t].curAlpha = mVerts[t].alpha * perc + mVerts[t].destAlpha * ( 1.0f - perc );
// if ( mVerts[t].curAlpha < 0.0f )
// {
// mVerts[t].curAlpha = 0.0f;
// }
// VectorScale( mVerts[t].rgb, perc, mVerts[t].curRGB );
// VectorMA( mVerts[t].curRGB, ( 1.0f - perc ), mVerts[t].destrgb, mVerts[t].curRGB );
mVerts[t].curST[0] = mVerts[t].ST[0] * perc + mVerts[t].destST[0] * ( 1.0f - perc );
if ( mVerts[t].curST[0] > 1.0f )
{
mVerts[t].curST[0] = 1.0f;
}
mVerts[t].curST[1] = mVerts[t].ST[1] * perc + mVerts[t].destST[1] * ( 1.0f - perc );
}
Draw();
return true;
}
//--------------------------
//
// Derived Poly Class
//
//--------------------------
bool CPoly::Cull()
{
vec3_t dir;
// Get the direction to the view
VectorSubtract( mOrigin1, cg.refdef.vieworg, dir );
// Check if it's behind the viewer
if ( (DotProduct( cg.refdef.viewaxis[0], dir )) < 0 )
{
return true;
}
float len = VectorLengthSquared( dir );
// Can't be too close
if ( len < 24 * 24 )
{
return true;
}
return false;
}
//----------------------------
void CPoly::Draw()
{
polyVert_t verts[MAX_CPOLY_VERTS];
for ( int i = 0; i < mCount; i++ )
{
// Add our midpoint and vert offset to get the actual vertex
VectorAdd( mOrigin1, mOrg[i], verts[i].xyz );
// Assign the same color to each vert
for ( int k=0; k<4; k++ )
verts[i].modulate[k] = mRefEnt.shaderRGBA[k];
// Copy the ST coords
VectorCopy2( mST[i], verts[i].st );
}
// Add this poly
theFxHelper.AddPolyToScene( mRefEnt.customShader, mCount, verts );
drawnFx++;
}
//----------------------------
void CPoly::CalcRotateMatrix()
{
float cosX, cosZ;
float sinX, sinZ;
float rad;
// rotate around Z
rad = DEG2RAD( mRotDelta[YAW] * theFxHelper.mFrameTime * 0.01f );
cosZ = cos( rad );
sinZ = sin( rad );
// rotate around X
rad = DEG2RAD( mRotDelta[PITCH] * theFxHelper.mFrameTime * 0.01f );
cosX = cos( rad );
sinX = sin( rad );
/*Pitch - aroundx Yaw - around z
1 0 0 c -s 0
0 c -s s c 0
0 s c 0 0 1
*/
mRot[0][0] = cosZ;
mRot[1][0] = -sinZ;
mRot[2][0] = 0;
mRot[0][1] = cosX * sinZ;
mRot[1][1] = cosX * cosZ;
mRot[2][1] = -sinX;
mRot[0][2] = sinX * sinZ;
mRot[1][2] = sinX * cosZ;
mRot[2][2] = cosX;
/*
// ROLL is not supported unless anyone complains, if it needs to be added, use this format
Roll
c 0 s
0 1 0
-s 0 c
*/
mLastFrameTime = theFxHelper.mFrameTime;
}
//--------------------------------
void CPoly::Rotate()
{
vec3_t temp[MAX_CPOLY_VERTS];
float dif = abs(mLastFrameTime - theFxHelper.mFrameTime);
// Very generous check with frameTimes
if ( dif > 0.5f * mLastFrameTime )
{
CalcRotateMatrix();
}
// Multiply our rotation matrix by each of the offset verts to get their new position
for ( int i = 0; i < mCount; i++ )
{
VectorRotate( mOrg[i], mRot, temp[i] );
VectorCopy( temp[i], mOrg[i] );
}
}
//----------------------------
// Update
//----------------------------
bool CPoly::Update()
{
vec3_t mOldOrigin = { 0.0f };
//FIXME: Handle Relative and Bolted Effects
/*
if ( mFlags & FX_RELATIVE )
{
if ( mClientID < 0 || mClientID >= ENTITYNUM_WORLD )
{ // we are somehow not bolted even though the flag is on?
return false;
}
// Get our current position and direction
if (mModelNum>=0 && mBoltNum>=0) //bolt style
{
}
}
*/
// Game pausing can cause dumb time things to happen, so kill the effect in this instance
if ( mTimeStart > theFxHelper.mTime )
{
return false;
}
// If our timestamp hasn't exired yet, we won't even consider doing any kind of motion
if ( theFxHelper.mTime > mTimeStamp )
{
VectorCopy( mOrigin1, mOldOrigin );
if (( mTimeStart < theFxHelper.mTime ) && UpdateOrigin() == false )
{
// we are marked for death
return false;
}
}
if ( !Cull() )
{
// only rotate when our start timestamp has expired
if ( theFxHelper.mTime > mTimeStamp )
{
// Only rotate whilst moving
if ( !VectorCompare( mOldOrigin, mOrigin1 ))
{
Rotate();
}
}
UpdateRGB();
UpdateAlpha();
Draw();
}
return true;
}
//----------------------------
void CPoly::PolyInit()
{
if ( mCount < 3 )
{
return;
}
int i;
vec3_t org={0,0,0};
// Find our midpoint
for ( i = 0; i < mCount; i++ )
{
VectorAdd( org, mOrg[i], org );
}
VectorScale( org, (float)(1.0f / mCount), org );
// now store our midpoint for physics purposes
VectorCopy( org, mOrigin1 );
// Now we process the passed in points and make it so that they aren't actually the point...
// rather, they are the offset from mOrigin1.
for ( i = 0; i < mCount; i++ )
{
VectorSubtract( mOrg[i], mOrigin1, mOrg[i] );
}
CalcRotateMatrix();
}
/*
-------------------------
CBezier
Bezier curve line
-------------------------
*/
//----------------------------
bool CBezier::Update( void )
{
float ftime, time2;
//FIXME: Handle Relative and Bolted Effects
/*
if ( mFlags & FX_RELATIVE )
{
if ( mClientID < 0 || mClientID >= ENTITYNUM_WORLD )
{ // we are somehow not bolted even though the flag is on?
return false;
}
// Get our current position and direction
if (mModelNum>=0 && mBoltNum>=0) //bolt style
{
}
}
*/
ftime = cg.frametime * 0.001f;
time2 = ftime * ftime * 0.5f;
for ( int i = 0; i < 3; i++ )
{
mControl1[i] = mControl1[i] + ftime * mControl1Vel[i] + time2 * mControl1Vel[i];
mControl2[i] = mControl2[i] + ftime * mControl2Vel[i] + time2 * mControl2Vel[i];
}
UpdateSize();
UpdateRGB();
UpdateAlpha();
Draw();
return true;
}
//----------------------------
void CBezier::DrawSegment( vec3_t start, vec3_t end, float texcoord1, float texcoord2 )
{
vec3_t lineDir, cross, viewDir;
static vec3_t lastEnd[2];
polyVert_t verts[4];
float scale;
VectorSubtract( end, start, lineDir );
VectorSubtract( end, cg.refdef.vieworg, viewDir );
CrossProduct( lineDir, viewDir, cross );
VectorNormalize( cross );
scale = mRefEnt.radius * 0.5f;
//Construct the oriented quad
if ( mInit )
{
VectorCopy( lastEnd[0], verts[0].xyz );
VectorCopy( lastEnd[1], verts[1].xyz );
}
else
{
VectorMA( start, -scale, cross, verts[0].xyz );
VectorMA( start, scale, cross, verts[1].xyz );
}
verts[0].st[0] = 0.0f;
verts[0].st[1] = texcoord1;
verts[0].modulate[0] = mRefEnt.shaderRGBA[0] * ( 1.0f - texcoord1 );
verts[0].modulate[1] = mRefEnt.shaderRGBA[1] * ( 1.0f - texcoord1 );
verts[0].modulate[2] = mRefEnt.shaderRGBA[2] * ( 1.0f - texcoord1 );
verts[0].modulate[3] = mRefEnt.shaderRGBA[3];
verts[1].st[0] = 1.0f;
verts[1].st[1] = texcoord1;
verts[1].modulate[0] = mRefEnt.shaderRGBA[0] * ( 1.0f - texcoord1 );
verts[1].modulate[1] = mRefEnt.shaderRGBA[1] * ( 1.0f - texcoord1 );
verts[1].modulate[2] = mRefEnt.shaderRGBA[2] * ( 1.0f - texcoord1 );
verts[1].modulate[3] = mRefEnt.shaderRGBA[3];
if ( texcoord1 == 0.0f )
{
verts[0].modulate[0] = 0;
verts[0].modulate[1] = 0;
verts[0].modulate[2] = 0;
verts[0].modulate[3] = 0;
verts[1].modulate[0] = 0;
verts[1].modulate[1] = 0;
verts[1].modulate[2] = 0;
verts[1].modulate[3] = 0;
}
VectorMA( end, scale, cross, verts[2].xyz );
verts[2].st[0] = 1.0f;
verts[2].st[1] = texcoord2;
verts[2].modulate[0] = mRefEnt.shaderRGBA[0] * ( 1.0f - texcoord2 );
verts[2].modulate[1] = mRefEnt.shaderRGBA[1] * ( 1.0f - texcoord2 );
verts[2].modulate[2] = mRefEnt.shaderRGBA[2] * ( 1.0f - texcoord2 );
verts[2].modulate[3] = mRefEnt.shaderRGBA[3];
VectorMA( end, -scale, cross, verts[3].xyz );
verts[3].st[0] = 0.0f;
verts[3].st[1] = texcoord2;
verts[3].modulate[0] = mRefEnt.shaderRGBA[0] * ( 1.0f - texcoord2 );
verts[3].modulate[1] = mRefEnt.shaderRGBA[1] * ( 1.0f - texcoord2 );
verts[3].modulate[2] = mRefEnt.shaderRGBA[2] * ( 1.0f - texcoord2 );
verts[3].modulate[3] = mRefEnt.shaderRGBA[3];
cgi_R_AddPolyToScene( mRefEnt.customShader, 4, verts );
VectorCopy( verts[2].xyz, lastEnd[1] );
VectorCopy( verts[3].xyz, lastEnd[0] );
mInit = true;
}
const float BEZIER_RESOLUTION = 16.0f;
//----------------------------
void CBezier::Draw( void )
{
vec3_t pos, old_pos;
float mu, mum1;
float incr = 1.0f / BEZIER_RESOLUTION, tex = 1.0f, tc1, tc2;
int i;
VectorCopy( mOrigin1, old_pos );
mInit = false; //Signify a new batch for vert gluing
// Calculate the texture coords so the texture can stretch along the whole bezier
// if ( mFlags & FXF_WRAP )
// {
// tex = m_stScale / 1.0f;
// }
float mum13, mu3, group1, group2;
tc1 = 0.0f;
for ( mu = incr; mu <= 1.0f; mu += incr )
{
//Four point curve
mum1 = 1 - mu;
mum13 = mum1 * mum1 * mum1;
mu3 = mu * mu * mu;
group1 = 3 * mu * mum1 * mum1;
group2 = 3 * mu * mu *mum1;
for ( i = 0; i < 3; i++ )
{
pos[i] = mum13 * mOrigin1[i] + group1 * mControl1[i] + group2 * mControl2[i] + mu3 * mOrigin2[i];
}
// if ( m_flags & FXF_WRAP )
// {
tc2 = mu * tex;
// }
// else
// {
// // Texture will get mapped onto each segement
// tc1 = 0.0f;
// tc2 = 1.0f;
// }
//Draw it
DrawSegment( old_pos, pos, tc1, tc2 );
VectorCopy( pos, old_pos );
tc1 = tc2;
}
drawnFx++;
mLines++; // NOT REALLY A LINE
}
/*
-------------------------
CFlash
Full screen flash
-------------------------
*/
//----------------------------
bool CFlash::Update( void )
{
UpdateRGB();
Draw();
return true;
}
//----------------------------
void CFlash::Init( void )
{
vec3_t dif;
float mod = 1.0f, dis;
VectorSubtract( mOrigin1, cg.refdef.vieworg, dif );
dis = VectorNormalize( dif );
mod = DotProduct( dif, cg.refdef.viewaxis[0] );
if ( dis > 600 || ( mod < 0.5f && dis > 100 ))
{
mod = 0.0f;
}
else if ( mod < 0.5f && dis <= 100 )
{
mod += 1.1f;
}
mod *= (1.0f - ((dis * dis) / (600.0f * 600.0f)));
VectorScale( mRGBStart, mod, mRGBStart );
VectorScale( mRGBEnd, mod, mRGBEnd );
}
//----------------------------
void CFlash::Draw( void )
{
// Interestingly, if znear is set > than this, then the flash
// doesn't appear at all.
const float FLASH_DISTANCE_FROM_VIEWER = 8.0f;
mRefEnt.reType = RT_SPRITE;
for ( int i = 0; i < 3; i++ )
{
if ( mRefEnt.lightingOrigin[i] > 1.0f )
{
mRefEnt.lightingOrigin[i] = 1.0f;
}
else if ( mRefEnt.lightingOrigin[i] < 0.0f )
{
mRefEnt.lightingOrigin[i] = 0.0f;
}
}
mRefEnt.shaderRGBA[0] = mRefEnt.lightingOrigin[0] * 255;
mRefEnt.shaderRGBA[1] = mRefEnt.lightingOrigin[1] * 255;
mRefEnt.shaderRGBA[2] = mRefEnt.lightingOrigin[2] * 255;
mRefEnt.shaderRGBA[3] = 255;
VectorCopy( cg.refdef.vieworg, mRefEnt.origin );
VectorMA( mRefEnt.origin, FLASH_DISTANCE_FROM_VIEWER, cg.refdef.viewaxis[0], mRefEnt.origin );
// This is assuming that the screen is wider than it is tall.
mRefEnt.radius = FLASH_DISTANCE_FROM_VIEWER * tan (DEG2RAD (cg.refdef.fov_x * 0.5f));
theFxHelper.AddFxToScene( &mRefEnt );
drawnFx++;
}