2277 lines
52 KiB
C++
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);
|
|
}
|