jedioutcast/CODE-mp/client/FxPrimitives.cpp
2013-04-04 13:24:26 -05:00

2277 lines
52 KiB
C++

#include "client.h"
#if !defined(G2_H_INC)
#include "../ghoul2/g2_local.h"
#endif
#if !defined(FX_SCHEDULER_H_INC)
#include "FxScheduler.h"
#endif
#include <set>
void FX_AddPrimitive( CEffect **pEffect, CCloud *effectCloud, int killTime );
// 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 = dat[i] * 255.0f;
if ( r < 0 )
{
r = 0;
}
else if ( r > 255 )
{
r = 0xff;
}
res[i] = (unsigned char)r;
}
}
//--------------------------
//
// Base Effect Class
//
//--------------------------
CEffect::CEffect() :
mNext(0)
{
memset( &mRefEnt, 0, sizeof( mRefEnt ));
mBoltInterfaceValid = false;
}
//----------------------------
CEffect::~CEffect()
{
}
//----------------------------
void CEffect::Die()
{
}
//----------------------------
bool CEffect::Cull()
{
return false;
}
//----------------------------
void CEffect::Draw()
{
}
//----------------------------
bool CEffect::Update()
{
// Game pausing can cause dumb time things to happen, so kill the effect in this instance
if ( mTimeStart > theFxHelper.mTime )
{
return false;
}
return true;
}
CCloud::CCloud() :
mNumPending(0)
{
mBoltInterfaceValid = false;
//mRefEnt.reType = RT_ENT_CHAIN;
}
extern set<CCloud *> OutstandClouds;
CCloud::~CCloud()
{
OutstandClouds.erase(this);
Die();
}
void CCloud::Die()
{
CEffect *current = mNext;
CEffect *next;
while(current)
{
next = current->GetNext();
current->Die();
delete current;
current = next;
}
mNext = 0;
}
bool CCloud::Update()
{
CEffect *current = mNext;
CEffect *next, *previous;
bool willDie;
CGhoul2Info_v *g2Handle;
mdxaBone_t boltMatrix;
int entNum;
vec3_t objOrg, objAng, objScale, org;
if (mBoltInterfaceValid)
{
entNum = mBoltInterface.GetEntNum();
//Com_Printf("VALID BOLT INTERFACE ON UPDATED: %i\n", mBoltInterface.GetEntNum());
// vec3_t getOrigin1;
// mBoltInterface.GetOrigin(getOrigin1);
// current->SetOrigin1(getOrigin1);
g2Handle = mBoltInterface.GetG2Handle();
if (!g2Handle || !G2API_HaveWeGhoul2Models(*((CGhoul2Info_v *)g2Handle)))
{
return false;
}
//First update the origin of the bolt object
//VM_Call( cgvm, CG_GET_ORIGIN, mBoltInterface.GetEntNum(), objOrg );
TCGBoltPos *data = (TCGBoltPos *)cl.mSharedMemory;
data->mEntityNum = entNum;
//VM_Call( cgvm, CG_GET_BOLT_POS, entNum, objOrg, objAng);
VectorCopy(data->mPoint, objOrg);
VectorCopy(data->mPoint, objAng);
mBoltInterface.GetScale(objScale);
if (0)
{
gG2_GBMNoReconstruct = qtrue;
}
G2API_GetBoltMatrix(*((CGhoul2Info_v *)g2Handle), mBoltInterface.GetModelNum(), mBoltInterface.GetBoltNum(), &boltMatrix, objAng, objOrg, theFxHelper.mTime, NULL, objScale);
G2API_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, org);
ForceOrigin1(org);
}
if (!mNext && mNumPending <= 0)
{
return false;
}
previous = 0;
while(current)
{
next = current->GetNext();
willDie = false;
if ( theFxHelper.mTime > current->GetKillTime())
{
// Clean up old effects, calling any death effects as needed
// this flag just has to be cleared otherwise death effects might not happen correctly
current->ClearFlags(FX_KILL_ON_IMPACT);
willDie = true;
}
if (mBoltInterfaceValid)
{
current->ForceOrigin1(org);
}
if (!willDie && !current->Update())
{
willDie = true;
}
if (willDie)
{
current->Die();
delete current;
if (previous)
{
previous->SetNext(next);
}
else
{
mNext = next;
}
}
else
{
previous = current;
}
current = next;
}
if (!mNext && mNumPending <= 0)
{
return false;
}
return true;
}
bool CCloud::Cull()
{
CEffect *current = mNext;
while(current)
{
if (!current->Cull())
{
return false;
}
current = current->GetNext();
}
return true;
}
void CCloud::Draw()
{
CEffect *current = mNext;
refEntity_t tempRef;
VectorCopy(current->GetRefEnt().origin, mRefEnt.origin);
// We must clear out the full entity, otherwise the mini won't stomp everything in the refent.
memset(&tempRef, 0, sizeof(refEntity_t));
memcpy(&tempRef, &mRefEnt, sizeof(mRefEnt));
tempRef.customShader = -1;
tempRef.uRefEnt.uMini.miniStart = -1;
tempRef.uRefEnt.uMini.miniCount = 0;
while(current)
{
if (current->GetRefEnt().customShader != tempRef.customShader)
{
tempRef.customShader = current->GetRefEnt().customShader;
theFxHelper.AddFxToScene( &tempRef );
}
current->Draw();
current = current->GetNext();
}
// clear it so that no other rogue mini ents get attached to the last parent
theFxHelper.AddFxToScene((miniRefEntity_t *)0);
}
void CCloud::AddEffect(CEffect *which)
{
which->SetNext(mNext);
mNext = which;
}
//--------------------------
//
// Derived Particle Class
//
//--------------------------
CParticle::CParticle()
{
mRefEnt.reType = RT_SPRITE;
}
//----------------------------
CParticle::~CParticle()
{
}
//----------------------------
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, crandom(), crandom(), crandom());
VectorNormalize( norm );
theFxScheduler.PlayEffect( mDeathFxID, mOrigin1, norm );
}
}
//----------------------------
bool CParticle::Cull()
{
vec3_t dir;
// Get the direction to the view
VectorSubtract( mOrigin1, theFxHelper.refdef.vieworg, dir );
// Check if it's behind the viewer
if ( (DotProduct( theFxHelper.refdef.viewaxis[0], dir )) < 0 )
{
return true;
}
float len = VectorLengthSquared( dir );
// Can't be too close
if ( len < 12 * 12 )
{
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);
}
//----------------------------
// 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;
}
#ifndef EFFECTSED
#ifndef CHC // SoF2 only
if ( mFlags & FX_RELATIVE )
{
if (!mObj || !mObj->IsValid() )
{
// the thing we are bolted to is no longer valid, so we may as well just die.
return false;
}
vec3_t org;
//vec3_t realVel, realAccel;
vec3_t currentOffset, initialOffset;
vec3_t offsetOffset;
vec3_t objOrg, objAng, objScale;
CGhoul2Info_v *g2Handle;
mdxaBone_t boltMatrix;
int entNum = mObj->GetEntNum();
g2Handle = mObj->GetG2Handle();
if (!g2Handle || !G2API_HaveWeGhoul2Models(*((CGhoul2Info_v *)g2Handle)))
{
return false;
}
//First update the origin of the bolt object
//VM_Call( cgvm, CG_GET_ORIGIN, mObj->GetEntNum(), objOrg );
TCGBoltPos *data = (TCGBoltPos *)cl.mSharedMemory;
data->mEntityNum = entNum;
//VM_Call( cgvm, CG_GET_BOLT_POS, entNum, objOrg, objAng);
VectorCopy(data->mPoint, objOrg);
VectorCopy(data->mPoint, objAng);
mObj->SetOrigin(objOrg);
mObj->SetForward(objAng);
mObj->GetScale(objScale);
G2API_GetBoltMatrix(*((CGhoul2Info_v *)g2Handle), mObj->GetModelNum(), mObj->GetBoltNum(), &boltMatrix, objAng, objOrg, theFxHelper.mTime, /*MODELLIST*/NULL, objScale);
G2API_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, org);
//Get the offset from the bolt point right now, and compare it to the offset in the initial position, then add onto the current position so that our offset is equal
//FIXME: Take current offset caused by random bouncing/velocity into account somehow?
VectorSubtract(org, mOrigin1, currentOffset);
mObj->GetInitialOffset(initialOffset);
VectorSubtract(currentOffset, initialOffset, offsetOffset);
VectorAdd(mOrigin1, offsetOffset, mOrigin1);
// 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( dir, mVel[0], realVel );
// VectorScale( dir, mAccel[0], 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, (theFxHelper.mTime - mTimeStart) * 0.001f, realAccel, realVel );
// Now move us to where we should be at the given time
// VectorMA( org, (theFxHelper.mTime - mTimeStart) * 0.001f, realVel, mOrigin1 );
}
else
#endif
#endif
if ( UpdateOrigin() == false )
{
// we are marked for death
return false;
}
UpdateSize();
UpdateRGB();
UpdateAlpha();
UpdateRotation();
return true;
}
//----------------------------
// Update Origin
//----------------------------
bool CParticle::UpdateOrigin()
{
vec3_t new_origin;
float ftime, time2;
int i;
UpdateVelocity();
// Calc the time differences
ftime = theFxHelper.mFrameTime * 0.001f;
time2 = ftime * ftime * 0.5f;
// Predict the new position
for ( i = 0 ; i < 3 ; i++ )
{
new_origin[i] = mOrigin1[i] + ftime * mVel[i] + time2 * mVel[i];
}
// Only perform physics if this object is tagged to do so
if ( mFlags & FX_APPLY_PHYSICS )
{
bool solid;
if ( mFlags & FX_EXPENSIVE_PHYSICS )
{
solid = true; // by setting this to true, we force a real trace to happen
}
else
{
TCGPointContents *data = (TCGPointContents *)cl.mSharedMemory;
VectorCopy(new_origin, data->mPoint);
data->mPassEntityNum = ENTITYNUM_WORLD;
// if this returns solid, we need to do a trace
//solid = !!(VM_Call( cgvm, CG_POINT_CONTENTS, new_origin, ENTITYNUM_WORLD ) & CONTENTS_SOLID);
solid = !!(VM_Call( cgvm, CG_POINT_CONTENTS ) & CONTENTS_SOLID);
}
if ( solid )
{
trace_t trace;
float dot;
if ( mFlags & FX_USE_BBOX )
{
theFxHelper.Trace( trace, mOrigin1, mMin, mMax, new_origin, -1, CONTENTS_SOLID );
}
else
{
theFxHelper.Trace( trace, mOrigin1, NULL, NULL, new_origin, -1, CONTENTS_SOLID );
}
// Hit something
if ( trace.fraction < 1.0f )//&& !trace.startsolid && !trace.allsolid )
{
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, ftime * 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 Velocity
//----------------------------
void CParticle::UpdateVelocity()
{
VectorMA( mVel, theFxHelper.mFrameTime * 0.001f, mAccel, mVel );
}
//----------------------------
// 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 = random() * 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 = random() * perc1;
}
// Now get the correct color
VectorScale( mRGBStart, perc1, res );
VectorMA( res, (1.0f - perc1), mRGBEnd, mRefEnt.oldorigin ); // 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 = random() * perc1;
}
if ( mFlags & FX_USE_ALPHA )
{
// should use this when using art that has an alpha channel
ClampVec( mRefEnt.oldorigin, (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.oldorigin, perc1, mRefEnt.oldorigin );
ClampVec( mRefEnt.oldorigin, (byte*)(&mRefEnt.shaderRGBA) );
}
}
//--------------------------
void CParticle::UpdateRotation()
{
mRefEnt.rotation += theFxHelper.mFrameTime * 0.01f * mRotationDelta;
}
//--------------------------------
//
// Derived Oriented Particle Class
//
//--------------------------------
COrientedParticle::COrientedParticle()
{
mRefEnt.reType = RT_ORIENTED_QUAD;
}
//----------------------------
COrientedParticle::~COrientedParticle()
{
}
//----------------------------
bool COrientedParticle::Cull()
{
vec3_t dir;
// Get the direction to the view
VectorSubtract( mOrigin1, theFxHelper.refdef.vieworg, dir );
// Check if it's behind the viewer
if ( (DotProduct( theFxHelper.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 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 );
}
//----------------------------
// 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 ( UpdateOrigin() == false )
{
// we are marked for death
return false;
}
UpdateSize();
UpdateRGB();
UpdateAlpha();
UpdateRotation();
return true;
}
//----------------------------
//
// Derived Line Class
//
//----------------------------
CLine::CLine()
{
mRefEnt.reType = RT_LINE;
}
//----------------------------
CLine::~CLine()
{
}
//----------------------------
void CLine::Die()
{
}
//----------------------------
bool CLine::Cull()
{
return false;
}
//----------------------------
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);
}
//----------------------------
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;
}
UpdateSize();
UpdateRGB();
UpdateAlpha();
return true;
}
//----------------------------
//
// Derived Electricity Class
//
//----------------------------
CElectricity::CElectricity()
{
mRefEnt.reType = RT_ELECTRICITY;
}
//----------------------------
CElectricity::~CElectricity()
{
}
//----------------------------
void CElectricity::Die()
{
}
//----------------------------
bool CElectricity::Cull()
{
return false;
}
//----------------------------
void CElectricity::Initialize()
{
mRefEnt.frame = random() * 1265536;
mRefEnt.axis[0][2] = theFxHelper.mTime + (mTimeEnd - mTimeStart); // endtime
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.axis[0][0] = mChaos;
mRefEnt.axis[0][1] = mTimeEnd - mTimeStart;
theFxHelper.AddFxToScene( &mRefEnt );
}
//----------------------------
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;
}
UpdateSize();
UpdateRGB();
UpdateAlpha();
return true;
}
//----------------------------
//
// Derived Tail Class
//
//----------------------------
CTail::CTail()
{
mRefEnt.reType = RT_LINE;
}
//----------------------------
CTail::~CTail()
{
}
//----------------------------
bool CTail::Cull()
{
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);
}
//----------------------------
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;
}
VectorCopy( mOrigin1, mOldOrigin );
if ( UpdateOrigin() == false )
{
// we are marked for death
return false;
}
UpdateSize();
UpdateLength();
UpdateRGB();
UpdateAlpha();
CalcNewEndpoint();
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 = random() * 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
//
//----------------------------
CCylinder::CCylinder()
{
mRefEnt.reType = RT_CYLINDER;
}
//----------------------------
CCylinder::~CCylinder()
{
}
//----------------------------
bool CCylinder::Cull()
{
return false;
}
//----------------------------
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);
}
//----------------------------
// 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 = random() * perc1;
}
mRefEnt.rotation = (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;
}
UpdateSize();
UpdateSize2();
UpdateLength();
UpdateRGB();
UpdateAlpha();
return true;
}
//----------------------------
//
// Derived Emitter Class
//
//----------------------------
CEmitter::CEmitter()
{
// There may or may not be a model, but if there isn't one,
// we just won't bother adding the refEnt in our Draw func
mRefEnt.reType = RT_MODEL;
}
//----------------------------
CEmitter::~CEmitter()
{
}
//----------------------------
bool CEmitter::Cull()
{
return false;
}
//----------------------------
// 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 12 // we "think" at about a 60hz rate
// Pick a target step distance and square it
step = mDensity + crandom() * 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];
}
// Is it time to draw an effect?
if ( DistanceSquared( org, mOldOrigin ) >= step )
{
// Pick a new target step distance and square it
step = mDensity + crandom() * 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;
}
}
}
}
//----------------------------
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;
}
// Use this to track if we've stopped moving
VectorCopy( mOrigin1, mOldOrigin );
VectorCopy( mVel, mOldVelocity );
if ( 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.7f, 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.
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
//
//--------------------------
CLight::CLight()
{
}
//----------------------------
CLight::~CLight()
{
}
//----------------------------
bool CLight::Cull()
{
return false;
}
//----------------------------
void CLight::Draw()
{
theFxHelper.AddLightToScene( mOrigin1, mRefEnt.radius,
mRefEnt.oldorigin[0], mRefEnt.oldorigin[1], mRefEnt.oldorigin[2] );
}
//----------------------------
// 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;
}
UpdateSize();
UpdateRGB();
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 = random() * 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 = random() * perc1;
}
// Now get the correct color
VectorScale( mRGBStart, perc1, res );
mRefEnt.oldorigin[0] = res[0] + ( 1.0f - perc1 ) * mRGBEnd[0];
mRefEnt.oldorigin[1] = res[1] + ( 1.0f - perc1 ) * mRGBEnd[1];
mRefEnt.oldorigin[2] = res[2] + ( 1.0f - perc1 ) * mRGBEnd[2];
}
//#ifdef CHC
//--------------------------
//
// Derived Trail Class
//
//--------------------------
bool CTrail::Cull()
{
return false;
}
#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 );
}
//----------------------------
// 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;
}
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 );
}
return true;
}
//#endif // CHC
//--------------------------
//
// Derived Poly Class
//
//--------------------------
CPoly::CPoly()
{
}
//----------------------------
CPoly::~CPoly()
{
}
//----------------------------
bool CPoly::Cull()
{
vec3_t dir;
// Get the direction to the view
VectorSubtract( mOrigin1, theFxHelper.refdef.vieworg, dir );
// Check if it's behind the viewer
if ( (DotProduct( theFxHelper.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
verts[i].modulate[0] = mRefEnt.shaderRGBA[0];
verts[i].modulate[1] = mRefEnt.shaderRGBA[1];
verts[i].modulate[2] = mRefEnt.shaderRGBA[2];
verts[i].modulate[3] = mRefEnt.shaderRGBA[3];
// Copy the ST coords
Vector2Copy( mST[i], verts[i].st );
}
// Add this poly
theFxHelper.AddPolyToScene( mRefEnt.customShader, mCount, verts );
}
//----------------------------
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 = fabs( mLastFrameTime - theFxHelper.mFrameTime );
if ( dif > 0.1f * 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()
{
// Game pausing can cause dumb time things to happen, so kill the effect in this instance
if ( mTimeStart > theFxHelper.mTime )
{
return false;
}
UpdateRGB();
UpdateAlpha();
// If our timestamp hasn't exired yet, we won't even consider doing any kind of motion
if ( theFxHelper.mTime > mTimeStamp )
{
vec3_t mOldOrigin;
VectorCopy( mOrigin1, mOldOrigin );
if ( UpdateOrigin() == false )
{
// we are marked for death
return false;
}
// Only rotate whilst moving
if ( !VectorCompare( mOldOrigin, mOrigin1 ))
{
Rotate();
}
}
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::Cull( void )
{
vec3_t dir;
VectorSubtract( mOrigin1, theFxHelper.refdef.vieworg, dir );
//Check if it's in front of the viewer
if ( (DotProduct( theFxHelper.refdef.viewaxis[0], dir )) >= 0 )
{
return false; //don't cull
}
VectorSubtract( mOrigin2, theFxHelper.refdef.vieworg, dir );
//Check if it's in front of the viewer
if ( (DotProduct( theFxHelper.refdef.viewaxis[0], dir )) >= 0 )
{
return false;
}
VectorSubtract( mControl1, theFxHelper.refdef.vieworg, dir );
//Check if it's in front of the viewer
if ( (DotProduct( theFxHelper.refdef.viewaxis[0], dir )) >= 0 )
{
return false;
}
return true; //all points behind viewer
}
//----------------------------
bool CBezier::Update( void )
{
float ftime, time2;
ftime = theFxHelper.mFrameTime * 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();
return true;
}
//----------------------------
inline 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, theFxHelper.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];
re.AddPolyToScene( mRefEnt.customShader, 4, verts, 1 );
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;
}
}
/*
-------------------------
CFlash
Full screen flash
-------------------------
*/
//----------------------------
bool CFlash::Update( void )
{
UpdateRGB();
return true;
}
//----------------------------
void CFlash::Init( void )
{
vec3_t dif;
float mod = 1.0f, dis;
VectorSubtract( mOrigin1, theFxHelper.refdef.vieworg, dif );
dis = VectorNormalize( dif );
mod = DotProduct( dif, theFxHelper.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 )
{
mRefEnt.reType = RT_SPRITE;
for ( int i = 0; i < 3; i++ )
{
if ( mRefEnt.oldorigin[i] > 1.0f )
{
mRefEnt.oldorigin[i] = 1.0f;
}
else if ( mRefEnt.oldorigin[i] < 0.0f )
{
mRefEnt.oldorigin[i] = 0.0f;
}
}
mRefEnt.shaderRGBA[0] = mRefEnt.oldorigin[0] * 255;
mRefEnt.shaderRGBA[1] = mRefEnt.oldorigin[1] * 255;
mRefEnt.shaderRGBA[2] = mRefEnt.oldorigin[2] * 255;
mRefEnt.shaderRGBA[3] = 255;
VectorCopy( theFxHelper.refdef.vieworg, mRefEnt.origin );
VectorMA( mRefEnt.origin, 8, theFxHelper.refdef.viewaxis[0], mRefEnt.origin );
mRefEnt.radius = 7.0f;
theFxHelper.AddFxToScene( &mRefEnt );
}
void FX_FeedTrail(effectTrailArgStruct_t *a)
{
CTrail *fx = new CTrail;
int i = 0;
while (i < 4)
{
VectorCopy(a->mVerts[i].origin, fx->mVerts[i].origin);
VectorCopy(a->mVerts[i].rgb, fx->mVerts[i].rgb);
VectorCopy(a->mVerts[i].destrgb, fx->mVerts[i].destrgb);
VectorCopy(a->mVerts[i].curRGB, fx->mVerts[i].curRGB);
fx->mVerts[i].alpha = a->mVerts[i].alpha;
fx->mVerts[i].destAlpha = a->mVerts[i].destAlpha;
fx->mVerts[i].curAlpha = a->mVerts[i].curAlpha;
fx->mVerts[i].ST[0] = a->mVerts[i].ST[0];
fx->mVerts[i].ST[1] = a->mVerts[i].ST[1];
fx->mVerts[i].destST[0] = a->mVerts[i].destST[0];
fx->mVerts[i].destST[1] = a->mVerts[i].destST[1];
fx->mVerts[i].curST[0] = a->mVerts[i].curST[0];
fx->mVerts[i].curST[1] = a->mVerts[i].curST[1];
i++;
}
fx->SetFlags(a->mSetFlags);
fx->mShader = a->mShader;
FX_AddPrimitive((CEffect **)&fx, NULL, a->mKillTime);
}