#if !defined(G2_H_INC) #include "../ghoul2/ghoul2_shared.h" #endif #if !defined(FX_SYSTEM_H_INC) #include "FxSystem.h" #endif #ifndef FX_PRIMITIVES_H_INC #define FX_PRIMITIVES_H_INC #ifdef CHC #define MAX_EFFECTS 1024 #else // anything else #define MAX_EFFECTS 1800 #endif // Generic group flags, used by parser, then get converted to the appropriate specific flags #define FX_PARM_MASK 0xC // use this to mask off any transition types that use a parm #define FX_GENERIC_MASK 0xF #define FX_LINEAR 0x1 #define FX_RAND 0x2 #define FX_NONLINEAR 0x4 #define FX_WAVE 0x8 #define FX_CLAMP 0xC // Group flags #define FX_ALPHA_SHIFT 0 #define FX_ALPHA_PARM_MASK 0x0000000C #define FX_ALPHA_LINEAR 0x00000001 #define FX_ALPHA_RAND 0x00000002 #define FX_ALPHA_NONLINEAR 0x00000004 #define FX_ALPHA_WAVE 0x00000008 #define FX_ALPHA_CLAMP 0x0000000C #define FX_RGB_SHIFT 4 #define FX_RGB_PARM_MASK 0x000000C0 #define FX_RGB_LINEAR 0x00000010 #define FX_RGB_RAND 0x00000020 #define FX_RGB_NONLINEAR 0x00000040 #define FX_RGB_WAVE 0x00000080 #define FX_RGB_CLAMP 0x000000C0 #define FX_SIZE_SHIFT 8 #define FX_SIZE_PARM_MASK 0x00000C00 #define FX_SIZE_LINEAR 0x00000100 #define FX_SIZE_RAND 0x00000200 #define FX_SIZE_NONLINEAR 0x00000400 #define FX_SIZE_WAVE 0x00000800 #define FX_SIZE_CLAMP 0x00000C00 #define FX_LENGTH_SHIFT 12 #define FX_LENGTH_PARM_MASK 0x0000C000 #define FX_LENGTH_LINEAR 0x00001000 #define FX_LENGTH_RAND 0x00002000 #define FX_LENGTH_NONLINEAR 0x00004000 #define FX_LENGTH_WAVE 0x00008000 #define FX_LENGTH_CLAMP 0x0000C000 #define FX_SIZE2_SHIFT 16 #define FX_SIZE2_PARM_MASK 0x000C0000 #define FX_SIZE2_LINEAR 0x00010000 #define FX_SIZE2_RAND 0x00020000 #define FX_SIZE2_NONLINEAR 0x00040000 #define FX_SIZE2_WAVE 0x00080000 #define FX_SIZE2_CLAMP 0x000C0000 // Feature flags #define FX_DEPTH_HACK 0x00100000 #define FX_RELATIVE 0x00200000 #define FX_SET_SHADER_TIME 0x00400000 // when the shader time gets set in-game, animating textures start animating at the first frame #define FX_EXPENSIVE_PHYSICS 0x00800000 #define FX_ATTACHED_MODEL 0x01000000 #define FX_APPLY_PHYSICS 0x02000000 #define FX_USE_BBOX 0x04000000 // can make physics more accurate at the expense of speed #define FX_USE_ALPHA 0x08000000 // the FX system actually uses RGB to do fades, but this will override that // and cause it to fill in the alpha. #define FX_EMIT_FX 0x10000000 // emitters technically don't have to emit stuff, but when they do // this flag needs to be set #define FX_DEATH_RUNS_FX 0x20000000 // Normal death triggers effect, but not kill_on_impact #define FX_KILL_ON_IMPACT 0x40000000 // works just like it says, but only when physics are on. #define FX_IMPACT_RUNS_FX 0x80000000 // an effect can call another effect when it hits something. // Lightning flags, duplicates of existing flags, but lightning doesn't use those flags in that context...and nothing will ever use these in this context..so we are safe. #define FX_TAPER 0x01000000 // tapers as it moves towards its endpoint #define FX_BRANCH 0x02000000 // enables lightning branching #define FX_GROW 0x04000000 // lightning grows from start point to end point over the course of its life class CFxBoltInterface { private: bool mIsValid; vec3_t mOrg; vec3_t mFwd; vec3_t mScale; CGhoul2Info_v *mG2Inst; int mModelNum; int mBoltNum; int mEntNum; vec3_t mInitialPartOrg; vec3_t mInitialOffset; public: CFxBoltInterface() { mIsValid = true; }; virtual ~CFxBoltInterface() {}; void GetOrigin( vec3_t org ) { VectorCopy(mOrg, org); }; // the fx system will call this to get the new origin void GetForward( vec3_t dir ) { VectorCopy(mFwd, dir); }; // and this to get the new forward. Getting the whole axis is not necessary void GetScale( vec3_t scale ) { VectorCopy(mScale, scale); }; // and this to get the new forward. Getting the whole axis is not necessary void GetInitialPartOrg( vec3_t org ) { VectorCopy(mInitialPartOrg, org); }; void GetInitialOffset( vec3_t org ) { VectorCopy(mInitialOffset, org); }; CGhoul2Info_v *GetG2Handle( void ) { return mG2Inst; } int GetModelNum( void ) { return mModelNum; } int GetBoltNum( void ) { return mBoltNum; } int GetEntNum( void ) { return mEntNum; } void SetOrigin( vec3_t org ) { VectorCopy(org, mOrg); }; // the fx system will call this to get the new origin void SetForward( vec3_t dir ) { VectorCopy(dir, mFwd); }; // and this to get the new forward. Getting the whole axis is not necessary void SetScale( vec3_t scale ) { VectorCopy(scale, mScale); }; // and this to get the new forward. Getting the whole axis is not necessary void SetInitialPartOrg( vec3_t org ) { VectorCopy(org, mInitialPartOrg); }; void SetInitialOffset( vec3_t org ) { VectorCopy(org, mInitialOffset); }; void SetG2Handle( CGhoul2Info_v *g2Inst ) { mG2Inst = g2Inst; } void SetModelNum( int modelNum ) { mModelNum = modelNum; } void SetBoltNum( int boltNum ) { mBoltNum = boltNum; } void SetEntNum( int entNum ) { mEntNum = entNum; } bool IsValid( void ) { return mIsValid; } void Invalidate( void ) { mIsValid = false; } // If the thing we are bolted to is no longer valid, clue up the fx system so it doesn't try to do dumb things void Validate() { mIsValid = true; } }; //------------------------------ class CEffect { protected: vec3_t mOrigin1; int mTimeStart; int mTimeEnd; int mFlags; // Size of our object, useful for things that have physics vec3_t mMin; vec3_t mMax; int mImpactFxID; // if we have an impact event, we may have to call an effect int mDeathFxID; // if we have a death event, we may have to call an effect int mKillTime; CFxBoltInterface mBoltInterface; bool mBoltInterfaceValid; miniRefEntity_t mRefEnt; CEffect *mNext; public: CEffect(); virtual ~CEffect(); virtual void Die(); virtual bool Update(); virtual bool Cull(); virtual void Draw(); inline miniRefEntity_t &GetRefEnt(void) { return mRefEnt; } inline void SetNext(CEffect *Next) { mNext = Next; } inline CEffect *GetNext(void) { return mNext; } inline void SetKillTime(int KillTime) { mKillTime = KillTime; } inline int GetKillTime(void) { return mKillTime; } inline int GetFlags(void) { return mFlags; } inline void SetSTScale(float s,float t) { mRefEnt.shaderTexCoord[0]=s;mRefEnt.shaderTexCoord[1]=t;} inline void SetMin( vec3_t min ) { if(min){VectorCopy(min,mMin);}else{VectorClear(mMin);} } inline void SetMax( vec3_t max ) { if(max){VectorCopy(max,mMax);}else{VectorClear(mMax);} } inline void SetFlags( int flags ) { mFlags = flags; } inline void AddFlags( int flags ) { mFlags |= flags; } inline void ClearFlags( int flags ) { mFlags &= ~flags; } inline void SetOrigin1( vec3_t org ) { if(org){VectorCopy(org,mOrigin1);}else{VectorClear(mOrigin1);} } inline void SetTimeStart( int time ) { mTimeStart = time; if (mFlags&FX_SET_SHADER_TIME) { mRefEnt.shaderTime = time * 0.001f; }} inline void SetTimeEnd( int time ) { mTimeEnd = time; } inline void SetImpactFxID( int id ) { mImpactFxID = id; } inline void SetDeathFxID( int id ) { mDeathFxID = id; } inline bool IsBoltInterfaceValid() { return mBoltInterfaceValid; } inline CFxBoltInterface *GetBIPointer() { return &mBoltInterface; } inline void GetOrigin1( vec3_t put_to ) { VectorCopy(mOrigin1, put_to); } inline void ForceOrigin1( vec3_t shove_in ) { VectorCopy(shove_in, mOrigin1); } inline void SetBoltInterface(CFxBoltInterface *obj) { vec3_t store; mBoltInterface.SetEntNum(obj->GetEntNum()); mBoltInterface.SetBoltNum(obj->GetBoltNum()); mBoltInterface.SetModelNum(obj->GetModelNum()); mBoltInterface.SetG2Handle(obj->GetG2Handle()); obj->GetForward(store); mBoltInterface.SetForward(store); obj->GetOrigin(store); mBoltInterface.SetOrigin(store); obj->GetScale(store); mBoltInterface.SetScale(store); obj->GetInitialOffset(store); mBoltInterface.SetInitialOffset(store); obj->GetInitialPartOrg(store); mBoltInterface.SetInitialPartOrg(store); mBoltInterfaceValid = true; } }; class CCloud : public CEffect { private: int mNumPending; public: CCloud(); virtual ~CCloud(); void IncreasePending(void) { mNumPending++; } void DecreasePending(void) { mNumPending--; } virtual void Die(); virtual bool Update(); virtual bool Cull(); virtual void Draw(); void AddEffect(CEffect *which); }; //#ifdef CHC //--------------------------------------------------- // This class is kind of an exception to the "rule". // For now it exists only for allowing an easy way // to get the saber slash trails rendered. //--------------------------------------------------- class CTrail : public CEffect { // This is such a specific case thing, just grant public access to the goods. public: typedef struct { vec3_t origin; // very specifc case, we can modulate the color and the alpha vec3_t rgb; vec3_t destrgb; vec3_t curRGB; float alpha; float destAlpha; float curAlpha; // this is a very specific case thing...allow interpolating the st coords so we can map the texture // properly as this segement progresses through it's life float ST[2]; float destST[2]; float curST[2]; } TVert; TVert mVerts[4]; qhandle_t mShader; CTrail() {}; virtual ~CTrail() {}; virtual bool Update(); virtual bool Cull(); virtual void Draw(); }; //#endif // CHC //------------------------------ class CLight : public CEffect { protected: float mSizeStart; float mSizeEnd; float mSizeParm; vec3_t mRGBStart; vec3_t mRGBEnd; float mRGBParm; void UpdateSize(); void UpdateRGB(); public: CLight(); virtual ~CLight(); virtual bool Update(); virtual bool Cull(); virtual void Draw(); inline void SetSizeStart( float sz ) { mSizeStart = sz; } inline void SetSizeEnd( float sz ) { mSizeEnd = sz; } inline void SetSizeParm( float parm ) { mSizeParm = parm; } inline void SetRGBStart( vec3_t rgb ) { if(rgb){VectorCopy(rgb,mRGBStart);}else{VectorClear(mRGBStart);} } inline void SetRGBEnd( vec3_t rgb ) { if(rgb){VectorCopy(rgb,mRGBEnd);}else{VectorClear(mRGBEnd);} } inline void SetRGBParm( float parm ) { mRGBParm = parm; } }; //------------------------------ class CFlash : public CLight { public: CFlash() {} virtual ~CFlash() {} virtual bool Cull() { return false; } virtual bool Update(); virtual void Draw(); inline void SetShader( qhandle_t sh ) { mRefEnt.customShader = sh; } void Init( void ); }; //------------------------------ class CParticle : public CEffect { protected: vec3_t mVel; vec3_t mAccel; float mSizeStart; float mSizeEnd; float mSizeParm; vec3_t mRGBStart; vec3_t mRGBEnd; float mRGBParm; float mAlphaStart; float mAlphaEnd; float mAlphaParm; float mRotationDelta; float mElasticity; bool UpdateOrigin(); void UpdateVelocity(); void UpdateSize(); void UpdateRGB(); void UpdateAlpha(); void UpdateRotation(); #ifndef EFFECTSED #ifndef CHC CFxBoltInterface *mObj; CFxBoltInterface storedObj; #endif #endif public: CParticle(); virtual ~CParticle(); virtual void Die(); virtual bool Update(); virtual bool Cull(); virtual void Draw(); inline void SetShader( qhandle_t sh ) { mRefEnt.customShader = sh; } inline void SetVel( vec3_t vel ) { if(vel){VectorCopy(vel,mVel);}else{VectorClear(mVel);} } inline void SetAccel( vec3_t ac ) { if(ac){VectorCopy(ac,mAccel);}else{VectorClear(mAccel);} } inline void SetSizeStart( float sz ) { mSizeStart = sz; } inline void SetSizeEnd( float sz ) { mSizeEnd = sz; } inline void SetSizeParm( float parm ) { mSizeParm = parm; } inline void SetRGBStart( vec3_t rgb ) { if(rgb){VectorCopy(rgb,mRGBStart);}else{VectorClear(mRGBStart);} } inline void SetRGBEnd( vec3_t rgb ) { if(rgb){VectorCopy(rgb,mRGBEnd);}else{VectorClear(mRGBEnd);} } inline void SetRGBParm( float parm ) { mRGBParm = parm; } inline void SetAlphaStart( float al ) { mAlphaStart = al; } inline void SetAlphaEnd( float al ) { mAlphaEnd = al; } inline void SetAlphaParm( float parm ) { mAlphaParm = parm; } inline void SetRotation( float rot ) { mRefEnt.rotation = rot; } inline void SetRotationDelta( float rot ) { mRotationDelta = rot; } inline void SetElasticity( float el ) { mElasticity = el; } #ifndef EFFECTSED #ifndef CHC inline void SetObj( CFxBoltInterface *obj ) { mObj = obj; } inline void SetObjNew( CFxBoltInterface *obj ) { //This is necessary when we want to copy the contents of a temporary variable and use them in a scheduled effect vec3_t store; mObj = &storedObj; mObj->SetEntNum(obj->GetEntNum()); mObj->SetBoltNum(obj->GetBoltNum()); mObj->SetModelNum(obj->GetModelNum()); mObj->SetG2Handle(obj->GetG2Handle()); obj->GetForward(store); mObj->SetForward(store); obj->GetOrigin(store); mObj->SetOrigin(store); obj->GetScale(store); mObj->SetScale(store); obj->GetInitialOffset(store); mObj->SetInitialOffset(store); obj->GetInitialPartOrg(store); mObj->SetInitialPartOrg(store); } #endif #endif }; //------------------------------ class CLine : public CParticle { protected: vec3_t mOrigin2; public: CLine(); virtual ~CLine(); virtual void Die(); virtual bool Update(); virtual bool Cull(); virtual void Draw(); inline void SetOrigin2( vec3_t org2 ) { VectorCopy( org2, mOrigin2 ); } }; //------------------------------ class CBezier : public CLine { protected: vec3_t mControl1; vec3_t mControl1Vel; vec3_t mControl2; vec3_t mControl2Vel; bool mInit; public: CBezier(){ mInit = false; } virtual ~CBezier() {} virtual void Die() {} virtual bool Update(); virtual bool Cull(); virtual void Draw(); void DrawSegment( vec3_t start, vec3_t end, float texcoord1, float texcoord2 ); inline void SetControlPoints( vec3_t ctrl1, vec3_t ctrl2 ) { VectorCopy( ctrl1, mControl1 ); VectorCopy( ctrl2, mControl2 ); } inline void SetControlVel( vec3_t ctrl1v, vec3_t ctrl2v ) { VectorCopy( ctrl1v, mControl1Vel ); VectorCopy( ctrl2v, mControl2Vel ); } }; //------------------------------ class CElectricity : public CLine { protected: float mChaos; public: CElectricity(); virtual ~CElectricity(); virtual void Die(); virtual bool Update(); virtual bool Cull(); virtual void Draw(); void Initialize(); inline void SetChaos( float chaos ) { mChaos = chaos; } }; // Oriented quad //------------------------------ class COrientedParticle : public CParticle { protected: vec3_t mNormal; public: COrientedParticle(); virtual ~COrientedParticle(); virtual bool Update(); virtual bool Cull(); virtual void Draw(); inline void SetNormal( vec3_t norm ) { VectorCopy( norm, mNormal ); } }; //------------------------------ class CTail : public CParticle { protected: vec3_t mOldOrigin; float mLengthStart; float mLengthEnd; float mLengthParm; float mLength; void UpdateLength(); void CalcNewEndpoint(); public: CTail(); virtual ~CTail(); virtual bool Update(); virtual bool Cull(); virtual void Draw(); inline void SetLengthStart( float len ) { mLengthStart = len; } inline void SetLengthEnd( float len ) { mLengthEnd = len; } inline void SetLengthParm( float len ) { mLengthParm = len; } }; //------------------------------ class CCylinder : public CTail { protected: float mSize2Start; float mSize2End; float mSize2Parm; void UpdateSize2(); public: CCylinder(); virtual ~CCylinder(); virtual bool Update(); virtual bool Cull(); virtual void Draw(); inline void SetSize2Start( float sz ) { mSize2Start = sz; } inline void SetSize2End( float sz ) { mSize2End = sz; } inline void SetSize2Parm( float parm ) { mSize2Parm = parm; } inline void SetNormal( vec3_t norm ) { VectorCopy( norm, mRefEnt.axis[0] ); } }; //------------------------------ // Emitters are derived from particles because, although they don't draw, any effect called // from them can borrow an initial or ending value from the emitters current alpha, rgb, etc.. class CEmitter : public CParticle { protected: vec3_t mOldOrigin; // we use these to do some nice vec3_t mLastOrigin; // tricks... vec3_t mOldVelocity; // int mOldTime; vec3_t mAngles; // for a rotating thing, using a delta vec3_t mAngleDelta; // as opposed to an end angle is probably much easier int mEmitterFxID; // if we have emitter fx, this is our id float mDensity; // controls how often emitter chucks an effect float mVariance; // density sloppiness void UpdateAngles(); public: CEmitter(); virtual ~CEmitter(); virtual bool Update(); virtual bool Cull(); virtual void Draw(); inline void SetModel( qhandle_t model ) { mRefEnt.hModel = model; } inline void SetAngles( vec3_t ang ) { if(ang){VectorCopy(ang,mAngles);}else{VectorClear(mAngles);} } inline void SetAngleDelta( vec3_t ang ) { if(ang){VectorCopy(ang,mAngleDelta);}else{VectorClear(mAngleDelta);} } inline void SetEmitterFxID( int id ) { mEmitterFxID = id; } inline void SetDensity( float density ) { mDensity = density; } inline void SetVariance( float var ) { mVariance = var; } inline void SetOldTime( int time ) { mOldTime = time; } inline void SetLastOrg( vec3_t org ) { if(org){VectorCopy(org,mLastOrigin);}else{VectorClear(mLastOrigin);} } inline void SetLastVel( vec3_t vel ) { if(vel){VectorCopy(vel,mOldVelocity);}else{VectorClear(mOldVelocity);} } }; // We're getting pretty low level here, not the kind of thing to abuse considering how much overhead this // adds to a SINGLE triangle or quad.... // The editor doesn't need to see or do anything with this //------------------------------ #define MAX_CPOLY_VERTS 5 class CPoly : public CParticle { protected: int mCount; vec3_t mRotDelta; int mTimeStamp; public: vec3_t mOrg[MAX_CPOLY_VERTS]; vec2_t mST[MAX_CPOLY_VERTS]; float mRot[3][3]; int mLastFrameTime; CPoly(); virtual ~CPoly(); virtual bool Update(); virtual bool Cull(); virtual void Draw(); void PolyInit(); void CalcRotateMatrix(); void Rotate(); inline void SetNumVerts( int c ) { mCount = c; } inline void SetRot( vec3_t r ) { if(r){VectorCopy(r,mRotDelta);}else{VectorClear(mRotDelta);}} inline void SetMotionTimeStamp( int t ) { mTimeStamp = theFxHelper.GetTime() + t; } inline int GetMotionTimeStamp() { return mTimeStamp; } }; #endif //FX_PRIMITIVES_H_INC