Initial commit

This commit is contained in:
Eagle517
2025-02-17 23:17:30 -06:00
commit 7cad314c94
4726 changed files with 1145203 additions and 0 deletions

154
engine/game/fx/cameraFXMgr.cc Executable file
View File

@ -0,0 +1,154 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "game/fx/cameraFXMgr.h"
#include "math/mRandom.h"
#include "math/mMatrix.h"
#include "dgl/dgl.h"
// global cam fx
CameraFXManager gCamFXMgr;
//**************************************************************************
// Camera effect
//**************************************************************************
CameraFX::CameraFX()
{
mElapsedTime = 0.0;
mDuration = 1.0;
}
//--------------------------------------------------------------------------
// Update
//--------------------------------------------------------------------------
void CameraFX::update( F32 dt )
{
mElapsedTime += dt;
}
//**************************************************************************
// Camera shake effect
//**************************************************************************
CameraShake::CameraShake()
{
mFreq.zero();
mAmp.zero();
mStartAmp.zero();
mTimeOffset.zero();
mCamFXTrans.identity();
mFalloff = 10.0;
}
//--------------------------------------------------------------------------
// Update
//--------------------------------------------------------------------------
void CameraShake::update( F32 dt )
{
Parent::update( dt );
fadeAmplitude();
VectorF camOffset;
camOffset.x = mAmp.x * sin( M_2PI * (mTimeOffset.x + mElapsedTime) * mFreq.x );
camOffset.y = mAmp.y * sin( M_2PI * (mTimeOffset.y + mElapsedTime) * mFreq.y );
camOffset.z = mAmp.z * sin( M_2PI * (mTimeOffset.z + mElapsedTime) * mFreq.z );
VectorF rotAngles;
rotAngles.x = camOffset.x * 10.0 * M_PI/180.0;
rotAngles.y = camOffset.y * 10.0 * M_PI/180.0;
rotAngles.z = camOffset.z * 10.0 * M_PI/180.0;
MatrixF rotMatrix( EulerF( rotAngles.x, rotAngles.y, rotAngles.z ) );
mCamFXTrans = rotMatrix;
mCamFXTrans.setPosition( camOffset );
}
//--------------------------------------------------------------------------
// Fade out the amplitude over time
//--------------------------------------------------------------------------
void CameraShake::fadeAmplitude()
{
F32 percentDone = (mElapsedTime / mDuration);
if( percentDone > 1.0 ) percentDone = 1.0;
F32 time = 1 + percentDone * mFalloff;
time = 1 / (time * time);
mAmp = mStartAmp * time;
}
//--------------------------------------------------------------------------
// Initialize
//--------------------------------------------------------------------------
void CameraShake::init()
{
mTimeOffset.x = 0.0;
mTimeOffset.y = gRandGen.randF();
mTimeOffset.z = gRandGen.randF();
}
//**************************************************************************
// CameraFXManager
//**************************************************************************
CameraFXManager::CameraFXManager()
{
mCamFXTrans.identity();
}
//--------------------------------------------------------------------------
// Destructor
//--------------------------------------------------------------------------
CameraFXManager::~CameraFXManager()
{
clear();
}
//--------------------------------------------------------------------------
// Add new effect to currently running list
//--------------------------------------------------------------------------
void CameraFXManager::addFX( CameraFX *newFX )
{
mFXList.link( newFX );
}
//--------------------------------------------------------------------------
// Clear all currently running camera effects
//--------------------------------------------------------------------------
void CameraFXManager::clear()
{
mFXList.free();
}
//--------------------------------------------------------------------------
// Update camera effects
//--------------------------------------------------------------------------
void CameraFXManager::update( F32 dt )
{
CameraFXPtr *cur = NULL;
mCamFXTrans.identity();
for( cur = mFXList.next( cur ); cur; cur = mFXList.next( cur ) )
{
CameraFX * curFX = *cur;
curFX->update( dt );
MatrixF fxTrans = curFX->getTrans();
mCamFXTrans.mul( fxTrans );
if( curFX->isExpired() )
{
CameraFXPtr *prev = mFXList.prev( cur );
mFXList.free( cur );
cur = prev;
}
}
}

89
engine/game/fx/cameraFXMgr.h Executable file
View File

@ -0,0 +1,89 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _CAMERAFXMGR_H_
#define _CAMERAFXMGR_H_
#ifndef _LLIST_H_
#include "core/llist.h"
#endif
#ifndef _MPOINT_H_
#include "math/mPoint.h"
#endif
#ifndef _MMATRIX_H_
#include "math/mMatrix.h"
#endif
//**************************************************************************
// Abstract camera effect template
//**************************************************************************
class CameraFX
{
protected:
F32 mElapsedTime;
F32 mDuration;
MatrixF mCamFXTrans;
public:
CameraFX();
MatrixF & getTrans(){ return mCamFXTrans; }
bool isExpired(){ return mElapsedTime >= mDuration; }
void setDuration( F32 duration ){ mDuration = duration; }
virtual void update( F32 dt );
};
//--------------------------------------------------------------------------
// Camera shake effect
//--------------------------------------------------------------------------
class CameraShake : public CameraFX
{
typedef CameraFX Parent;
VectorF mFreq; // these are vectors to represent these values in 3D
VectorF mStartAmp;
VectorF mAmp;
VectorF mTimeOffset;
F32 mFalloff;
public:
CameraShake();
void init();
void fadeAmplitude();
void setFalloff( F32 falloff ){ mFalloff = falloff; }
void setFrequency( VectorF &freq ){ mFreq = freq; }
void setAmplitude( VectorF &amp ){ mStartAmp = amp; }
virtual void update( F32 dt );
};
//**************************************************************************
// CameraFXManager
//**************************************************************************
class CameraFXManager
{
typedef CameraFX * CameraFXPtr;
LList< CameraFXPtr > mFXList;
MatrixF mCamFXTrans;
public:
void addFX( CameraFX *newFX );
void clear();
MatrixF & getTrans(){ return mCamFXTrans; }
void update( F32 dt );
CameraFXManager();
~CameraFXManager();
};
extern CameraFXManager gCamFXMgr;
#endif

1125
engine/game/fx/explosion.cc Executable file

File diff suppressed because it is too large Load Diff

172
engine/game/fx/explosion.h Executable file
View File

@ -0,0 +1,172 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _EXPLOSION_H_
#define _EXPLOSION_H_
#ifndef _GAMEBASE_H_
#include "game/gameBase.h"
#endif
#ifndef _TSSHAPE_H_
#include "ts/tsShape.h"
#endif
class ParticleEmitter;
class ParticleEmitterData;
class TSThread;
class AudioProfile;
struct DebrisData;
class ShockwaveData;
//--------------------------------------------------------------------------
class ExplosionData : public GameBaseData {
public:
typedef GameBaseData Parent;
enum ExplosionConsts
{
EC_NUM_DEBRIS_TYPES = 1,
EC_NUM_EMITTERS = 4,
EC_MAX_SUB_EXPLOSIONS = 5,
EC_NUM_TIME_KEYS = 4,
};
public:
StringTableEntry dtsFileName;
bool faceViewer;
S32 particleDensity;
F32 particleRadius;
AudioProfile* soundProfile;
ParticleEmitterData* particleEmitter;
S32 soundProfileId;
S32 particleEmitterId;
Point3F explosionScale;
F32 playSpeed;
Resource<TSShape> explosionShape;
S32 explosionAnimation;
ParticleEmitterData* emitterList[EC_NUM_EMITTERS];
S32 emitterIDList[EC_NUM_EMITTERS];
ShockwaveData * shockwave;
S32 shockwaveID;
bool shockwaveOnTerrain;
DebrisData * debrisList[EC_NUM_DEBRIS_TYPES];
S32 debrisIDList[EC_NUM_DEBRIS_TYPES];
F32 debrisThetaMin;
F32 debrisThetaMax;
F32 debrisPhiMin;
F32 debrisPhiMax;
S32 debrisNum;
S32 debrisNumVariance;
F32 debrisVelocity;
F32 debrisVelocityVariance;
// sub - explosions
ExplosionData* explosionList[EC_MAX_SUB_EXPLOSIONS];
S32 explosionIDList[EC_MAX_SUB_EXPLOSIONS];
S32 delayMS;
S32 delayVariance;
S32 lifetimeMS;
S32 lifetimeVariance;
F32 offset;
Point3F sizes[ EC_NUM_TIME_KEYS ];
F32 times[ EC_NUM_TIME_KEYS ];
// camera shake data
bool shakeCamera;
VectorF camShakeFreq;
VectorF camShakeAmp;
F32 camShakeDuration;
F32 camShakeRadius;
F32 camShakeFalloff;
// Dynamic Lighting. The light is smoothly
// interpolated from start to end time.
F32 lightStartRadius;
F32 lightEndRadius;
ColorF lightStartColor;
ColorF lightEndColor;
ExplosionData();
DECLARE_CONOBJECT(ExplosionData);
bool onAdd();
bool preload(bool server, char errorBuffer[256]);
static void initPersistFields();
virtual void packData(BitStream* stream);
virtual void unpackData(BitStream* stream);
};
DECLARE_CONSOLETYPE(ExplosionData)
//--------------------------------------------------------------------------
class Explosion : public GameBase
{
typedef GameBase Parent;
private:
ExplosionData* mDataBlock;
TSShapeInstance* mExplosionInstance;
TSThread* mExplosionThread;
ParticleEmitter * mEmitterList[ ExplosionData::EC_NUM_EMITTERS ];
U32 mCurrMS;
U32 mEndingMS;
F32 mRandAngle;
LightInfo mLight;
protected:
Point3F mInitialNormal;
F32 mFade;
F32 mFog;
bool mActive;
S32 mDelayMS;
F32 mRandomVal;
U32 mCollideType;
protected:
bool onAdd();
void onRemove();
bool explode();
void processTick(const Move *move);
void advanceTime(F32 dt);
void updateEmitters( F32 dt );
void launchDebris( Point3F &axis );
void spawnSubExplosions();
void setCurrentScale();
// Rendering
protected:
bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState);
void renderObject(SceneState *state, SceneRenderImage *image);
void prepModelView(SceneState*);
void registerLights(LightManager * lm, bool lightingScene);
public:
Explosion();
~Explosion();
void setInitialState(const Point3F& point, const Point3F& normal, const F32 fade = 1.0);
bool onNewDataBlock(GameBaseData* dptr);
void setCollideType( U32 cType ){ mCollideType = cType; }
DECLARE_CONOBJECT(Explosion);
static void initPersistFields();
};
#endif // _H_EXPLOSION

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,308 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Written by Melvyn May, 4th August 2002.
//-----------------------------------------------------------------------------
#ifndef _FOLIAGEREPLICATOR_H_
#define _FOLIAGEREPLICATOR_H_
#ifndef _SCENEOBJECT_H_
#include "sim/sceneObject.h"
#endif
#define AREA_ANIMATION_ARC (1.0f / 360.0f)
#define FXFOLIAGEREPLICATOR_COLLISION_MASK ( TerrainObjectType | \
InteriorObjectType | \
StaticObjectType | \
WaterObjectType )
#define FXFOLIAGEREPLICATOR_NOWATER_COLLISION_MASK ( TerrainObjectType | \
InteriorObjectType | \
StaticObjectType )
#define FXFOLIAGE_ALPHA_EPSILON 1e-4
//------------------------------------------------------------------------------
// Class: fxFoliageItem
//------------------------------------------------------------------------------
class fxFoliageItem
{
public:
MatrixF Transform;
F32 Width;
F32 Height;
Box3F FoliageBox;
bool Flipped;
F32 SwayPhase;
F32 SwayTimeRatio;
F32 LightPhase;
F32 LightTimeRatio;
U32 LastFrameSerialID;
};
//------------------------------------------------------------------------------
// Class: fxFoliageCulledList
//------------------------------------------------------------------------------
class fxFoliageCulledList
{
public:
fxFoliageCulledList() {};
fxFoliageCulledList(Box3F SearchBox, fxFoliageCulledList* InVec);
~fxFoliageCulledList() {};
void FindCandidates(Box3F SearchBox, fxFoliageCulledList* InVec);
U32 GetListCount(void) { return mCulledObjectSet.size(); };
fxFoliageItem* GetElement(U32 index) { return mCulledObjectSet[index]; };
Vector<fxFoliageItem*> mCulledObjectSet; // Culled Object Set.
};
//------------------------------------------------------------------------------
// Class: fxFoliageQuadNode
//------------------------------------------------------------------------------
class fxFoliageQuadrantNode
{
public:
U32 Level;
Box3F QuadrantBox;
fxFoliageQuadrantNode* QuadrantChildNode[4];
Vector<fxFoliageItem*> RenderList;
fxFoliageQuadrantNode()
{
}
};
//------------------------------------------------------------------------------
// Class: fxFoliageRenderList
//------------------------------------------------------------------------------
class fxFoliageRenderList
{
public:
Point3F FarPosLeftUp; // View Frustum.
Point3F FarPosLeftDown;
Point3F FarPosRightUp;
Point3F FarPosRightDown;
Point3F CameraPosition; // Camera Position.
Box3F mBox; // Clipping Box.
PlaneF ViewPlanes[5]; // Clipping View-Planes.
Vector<fxFoliageItem*> mVisObjectSet; // Visible Object Set.
F32 mHeightLerp; // Height Lerp.
public:
bool IsQuadrantVisible(const Box3F VisBox, const MatrixF& RenderTransform);
void SetupClipPlanes(SceneState* state, const F32 FarClipPlane);
void CompileVisibleSet(const fxFoliageQuadrantNode* pNode, const MatrixF& RenderTransform, const bool UseDebug);
void DrawQuadBox(const Box3F& QuadBox, const ColorF Colour);
};
//------------------------------------------------------------------------------
// Class: fxFoliageReplicator
//------------------------------------------------------------------------------
class fxFoliageReplicator : public SceneObject
{
private:
typedef SceneObject Parent;
protected:
void CreateFoliage(void);
void DestroyFoliage(void);
void SyncFoliageReplicators(void);
Box3F FetchQuadrant(Box3F Box, U32 Quadrant);
void ProcessQuadrant(fxFoliageQuadrantNode* pParentNode, fxFoliageCulledList* pCullList, U32 Quadrant);
void ProcessNodeChildren(fxFoliageQuadrantNode* pParentNode, fxFoliageCulledList* pCullList);
enum { FoliageReplicationMask = (1 << 0) };
U32 mCreationAreaAngle;
bool mClientReplicationStarted;
bool mAddedToScene;
U32 mCurrentFoliageCount;
Vector<fxFoliageQuadrantNode*> mFoliageQuadTree;
Vector<fxFoliageItem*> mReplicatedFoliage;
fxFoliageRenderList mFrustumRenderSet;
MRandomLCG RandomGen;
F32 mFadeInGradient;
F32 mFadeOutGradient;
S32 mLastRenderTime;
F32 mGlobalSwayPhase;
F32 mGlobalSwayTimeRatio;
F32 mGlobalLightPhase;
F32 mGlobalLightTimeRatio;
U32 mFrameSerialID;
U32 mQuadTreeLevels; // Quad-Tree Levels.
U32 mPotentialFoliageNodes; // Potential Foliage Nodes.
U32 mNextAllocatedNodeIdx; // Next Allocated Node Index.
U32 mBillboardsAcquired; // Billboards Acquired.
public:
fxFoliageReplicator();
~fxFoliageReplicator();
void StartUp(void);
void ShowReplication(void);
void HideReplication(void);
// SceneObject
void renderObject(SceneState *state, SceneRenderImage *image);
virtual bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone,
const bool modifyBaseZoneState = false);
// SimObject
bool onAdd();
void onRemove();
void onEditorEnable();
void onEditorDisable();
void inspectPostApply();
// NetObject
U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
void unpackUpdate(NetConnection *conn, BitStream *stream);
// ConObject.
static void initPersistFields();
// Field Data.
class tagFieldData
{
public:
bool mUseDebugInfo;
F32 mDebugBoxHeight;
U32 mSeed;
StringTableEntry mFoliageFile;
TextureHandle mFoliageTexture;
U32 mFoliageCount;
U32 mFoliageRetries;
U32 mInnerRadiusX;
U32 mInnerRadiusY;
U32 mOuterRadiusX;
U32 mOuterRadiusY;
F32 mMinWidth;
F32 mMaxWidth;
F32 mMinHeight;
F32 mMaxHeight;
bool mFixAspectRatio;
bool mFixSizeToMax;
F32 mOffsetZ;
bool mRandomFlip;
bool mUseCulling;
U32 mCullResolution;
F32 mViewDistance;
F32 mViewClosest;
F32 mFadeInRegion;
F32 mFadeOutRegion;
F32 mAlphaCutoff;
F32 mGroundAlpha;
bool mSwayOn;
bool mSwaySync;
F32 mSwayMagnitudeSide;
F32 mSwayMagnitudeFront;
F32 mMinSwayTime;
F32 mMaxSwayTime;
bool mLightOn;
bool mLightSync;
F32 mMinLuminance;
F32 mMaxLuminance;
F32 mLightTime;
bool mAllowOnTerrain;
bool mAllowOnInteriors;
bool mAllowStatics;
bool mAllowOnWater;
bool mAllowWaterSurface;
S32 mAllowedTerrainSlope;
bool mHideFoliage;
bool mShowPlacementArea;
U32 mPlacementBandHeight;
ColorF mPlaceAreaColour;
tagFieldData()
{
// Set Defaults.
mUseDebugInfo = false;
mDebugBoxHeight = 1.0f;
mSeed = 1376312589;
mFoliageFile = StringTable->insert("");
mFoliageTexture = TextureHandle();
mFoliageCount = 10;
mFoliageRetries = 100;
mInnerRadiusX = 0;
mInnerRadiusY = 0;
mOuterRadiusX = 128;
mOuterRadiusY = 128;
mMinWidth = 1;
mMaxWidth = 3;
mMinHeight = 1;
mMaxHeight = 5;
mFixAspectRatio = true;
mFixSizeToMax = false;
mOffsetZ = 0;
mRandomFlip = true;
mUseCulling = true;
mCullResolution = 64;
mViewDistance = 50.0f;
mViewClosest = 1.0f;
mFadeInRegion = 10.0f;
mFadeOutRegion = 1.0f;
mAlphaCutoff = 0.2f;
mGroundAlpha = 1.0f;
mSwayOn = false;
mSwaySync = false;
mSwayMagnitudeSide = 0.1f;
mSwayMagnitudeFront = 0.2f;
mMinSwayTime = 3.0f;
mMaxSwayTime = 10.0f;
mLightOn = false;
mLightSync = false;
mMinLuminance = 0.7f;
mMaxLuminance = 1.0f;
mLightTime = 5.0f;
mAllowOnTerrain = true;
mAllowOnInteriors = true;
mAllowStatics = true;
mAllowOnWater = false;
mAllowWaterSurface = false;
mAllowedTerrainSlope = 90;
mHideFoliage = false;
mShowPlacementArea = true;
mPlacementBandHeight = 25;
mPlaceAreaColour .set(0.4f, 0, 0.8f);
}
} mFieldData;
// Declare Console Object.
DECLARE_CONOBJECT(fxFoliageReplicator);
};
#endif // _FOLIAGEREPLICATOR_H_

1674
engine/game/fx/fxLight.cc Executable file

File diff suppressed because it is too large Load Diff

219
engine/game/fx/fxLight.h Executable file
View File

@ -0,0 +1,219 @@
//-----------------------------------------------------------------------------
// Torque Game Engine - fxLight
//
// Written by Melvyn May, 4th May 2002.
//-----------------------------------------------------------------------------
#ifndef _FXLIGHTDB_H_
#define _FXLIGHTDB_H_
#ifndef _GAMEBASE_H_
#include "game/gameBase.h"
#endif
#ifndef _SCENEOBJECT_H_
#include "sim/sceneObject.h"
#endif
#define FXLIGHTDBICONTEXTURE "common/editor/fxlighticon.png"
class fxLightData : public GameBaseData
{
public:
typedef GameBaseData Parent;
fxLightData();
bool onAdd();
static void initPersistFields();
virtual void packData(BitStream* stream);
virtual void unpackData(BitStream* stream);
// Datablock Flare.
StringTableEntry mFlareTextureName; // Flare Texture Name.
// Datablock Light.
bool mLightOn; // Light On Flag.
F32 mRadius; // Radius.
F32 mBrightness; // Brightness.
ColorF mColour; // Colour.
// Datablock Flare.
bool mFlareOn; // Flare On Flag.
bool mFlareTP; // Flare Third Person Flag.
ColorF mFlareColour; // Flare Colour.
bool mConstantSizeOn; // Flare Use Constant Size Flag.
F32 mConstantSize; // Flare Constant Size.
F32 mNearSize; // Near Size.
F32 mFarSize; // Far Size.
F32 mNearDistance; // Near Distance.
F32 mFarDistance; // Far Distance.
F32 mFadeTime; // Fade Time.
U32 mBlendMode; // Blend Mode.
bool mLinkFlare; // Link Flare Animation.
bool mLinkFlareSize; // Link Flare Size Animation.
// Datablock Animation.
ColorF mMinColour; // Minimum Colour.
ColorF mMaxColour; // Maximum Colour.
F32 mMinBrightness; // Minimum Brightness.
F32 mMaxBrightness; // Maximum Brightness.
F32 mMinRadius; // Minimum Radius.
F32 mMaxRadius; // Maximum Radius.
Point3F mStartOffset; // Start Offset.
Point3F mEndOffset; // Stop Offset.
F32 mMinRotation; // Minimum Rotation.
F32 mMaxRotation; // Maximum Rotation.
bool mSingleColourKeys; // Single-Channel Colour Keys.
StringTableEntry mRedKeys; // Red Animation Keys.
StringTableEntry mGreenKeys; // Green Animation Keys.
StringTableEntry mBlueKeys; // Blue Animation Keys.
StringTableEntry mBrightnessKeys; // Brightness Animation Keys.
StringTableEntry mRadiusKeys; // Radius Animation Keys.
StringTableEntry mOffsetKeys; // Offset Animation Keys.
StringTableEntry mRotationKeys; // Rotation Animation Keys.
F32 mColourTime; // Colour Time (Seconds).
F32 mBrightnessTime; // Brightness Time.
F32 mRadiusTime; // Radius Time.
F32 mOffsetTime; // Offset Time.
F32 mRotationTime; // Rotation Time.
bool mLerpColour; // Lerp Colour Flag.
bool mLerpBrightness; // Lerp Brightness Flag.
bool mLerpRadius; // Lerp Radius Flag.
bool mLerpOffset; // Lerp Offset Flag.
bool mLerpRotation; // Lerp Rotation Flag.
bool mUseColour; // Use Colour Flag.
bool mUseBrightness; // Use Brightness Flag.
bool mUseRadius; // Use Radius Flag.
bool mUseOffsets; // Use Position Offsets Flag.
bool mUseRotation; // Use Rotation Flag.
DECLARE_CONOBJECT(fxLightData);
};
DECLARE_CONSOLETYPE(fxLightData)
//------------------------------------------------------------------------------
// Class: fxLight
//------------------------------------------------------------------------------
class fxLight : public GameBase
{
private:
typedef GameBase Parent;
fxLightData* mDataBlock;
U32 CheckKeySyntax(StringTableEntry Key);
void CheckAnimationKeys(void);
F32 GetLerpKey(StringTableEntry Key, U32 PosFrom, U32 PosTo, F32 ValueFrom, F32 ValueTo, F32 Lerp);
void AnimateLight(void);
void InitialiseAnimation(void);
void ResetAnimation(void);
bool TestLOS(const Point3F& ObjectPosition, SceneObject* AttachedObj);
protected:
enum {
fxLightConfigChangeMask = BIT(0),
fxLightAttachChange = BIT(1)
};
bool mAddedToScene;
MRandomLCG RandomGen;
// Textures.
TextureHandle mIconTextureHandle;
TextureHandle mFlareTextureHandle;
LightInfo mLight;
U32 mLastAnimateTime;
U32 mLastRenderTime;
F32 mFlareScale;
bool mAttached;
GameBase* mpAttachedObject;
bool mAttachWait;
bool mAttachValid;
public:
fxLight();
~fxLight();
// *********************************
// Configuration Interface.
// *********************************
// Light.
void setEnable(bool Status);
void setFlareBitmap(const char* Name);
// Misc,
void reset(void);
void attachToObject(const char* ObjectName);
void detachFromObject(void);
// GameBase.
bool onNewDataBlock(GameBaseData* dptr);
void processTick(const Move*);
// SceneObject
bool prepRenderImage(SceneState* state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState);
void renderObject(SceneState *state, SceneRenderImage *image);
// SimObject
bool onAdd();
void onRemove();
void onDeleteNotify(SimObject*);
void inspectPostApply();
void registerLights(LightManager * lightManager, bool lightingScene);
// NetObject
U32 packUpdate(NetConnection *, U32, BitStream *);
void unpackUpdate(NetConnection *, BitStream *);
// ConObject
static void initPersistFields();
// Field Data.
bool mEnable; // Light Enable.
// Basis Light.
F32 mIconSize; // Icon Size.
// Current Animation.
ColorF mAnimationColour; // Current Colour.
F32 mAnimationBrightness; // Current Brightness.
F32 mAnimationRadius; // Current Radius.
Point3F mAnimationPosition; // Current Position.
Point3F mAnimationOffset; // Current Offset.
F32 mAnimationRotation; // Current Rotation.
// Elapsed Times.
F32 mColourElapsedTime; // Colour Elapsed Time.
F32 mBrightnessElapsedTime; // Brightness Elapsed Time.
F32 mRadiusElapsedTime; // Radius Elapsed Time.
F32 mOffsetElapsedTime; // Offset Elapsed Time.
F32 mRotationElapsedTime; // Rotation Elapsed Time.
// Time Scales.
F32 mColourTimeScale; // Colour Time Scale.
F32 mBrightnessTimeScale; // Brightness Time Scale.
F32 mRadiusTimeScale; // Radius Time Scale.
F32 mOffsetTimeScale; // Offset Time Scale.
F32 mRotationTimeScale; // Rotation Time Scale.
// Key Lengths (Validity).
U32 mRedKeysLength; // Red Keys Length.
U32 mGreenKeysLength; // Green Keys Length.
U32 mBlueKeysLength; // Blue Keys Length.
U32 mBrightnessKeysLength; // Brightness Keys Length.
U32 mRadiusKeysLength; // Radius Keys Length.
U32 mOffsetKeysLength; // Offset Keys Length.
U32 mRotationKeysLength; // Rotation Keys Length.
// Declare Console Object.
DECLARE_CONOBJECT(fxLight);
};
#endif // _FXLIGHTDB_H_

View File

@ -0,0 +1,818 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Written by Melvyn May, Started on 13th March 2002, finished 27th March 2002.
//
// "My code is written for the Torque community, so do your worst with it,
// just don't rip-it-off and call it your own without even thanking me".
//
// - Melv (working hard to become an associate ... hint).
//
//-----------------------------------------------------------------------------
#include "dgl/dgl.h"
#include "console/consoleTypes.h"
#include "core/bitStream.h"
#include "math/mRandom.h"
#include "math/mathIO.h"
#include "terrain/terrData.h"
#include "game/gameConnection.h"
#include "console/simBase.h"
#include "sceneGraph/sceneGraph.h"
#include "fxShapeReplicator.h"
//------------------------------------------------------------------------------
//
// Put this in /example/common/editor/editor.cs in function [Editor::create()] (around line 66).
//
// // Ignore Replicated fxStatic Instances.
// EWorldEditor.ignoreObjClass("fxShapeReplicatedStatic");
//
//------------------------------------------------------------------------------
//
// Put this in /example/common/editor/EditorGui.cs in [function Creator::init( %this )]
//
// %Environment_Item[8] = "fxShapeReplicator"; <-- ADD THIS.
//
//------------------------------------------------------------------------------
//
// Put the function in /example/common/editor/ObjectBuilderGui.gui [around line 458] ...
//
// function ObjectBuilderGui::buildfxShapeReplicator(%this)
// {
// %this.className = "fxShapeReplicator";
// %this.process();
// }
//
//------------------------------------------------------------------------------
//
// Put this in /example/common/client/missionDownload.cs in [function clientCmdMissionStartPhase3(%seq,%missionName)] (line 65)
// after codeline 'onPhase2Complete();'.
//
// StartClientReplication();
//
//------------------------------------------------------------------------------
//
// Put this in /engine/console/simBase.h (around line 509) in
//
// namespace Sim
// {
// DeclareNamedSet(fxReplicatorSet) <-- ADD THIS (Note no semi-colon).
//
//------------------------------------------------------------------------------
//
// Put this in /engine/console/simBase.cc (around line 19) in
//
// ImplementNamedSet(fxReplicatorSet) <-- ADD THIS
//
//------------------------------------------------------------------------------
//
// Put this in /engine/console/simManager.cc [function void init()] (around line 269).
//
// namespace Sim
// {
// InstantiateNamedSet(fxReplicatorSet); <-- ADD THIS
//
//------------------------------------------------------------------------------
extern bool gEditingMission;
//------------------------------------------------------------------------------
IMPLEMENT_CO_NETOBJECT_V1(fxShapeReplicator);
IMPLEMENT_CO_NETOBJECT_V1(fxShapeReplicatedStatic);
//------------------------------------------------------------------------------
// Class: fxShapeReplicator
//------------------------------------------------------------------------------
fxShapeReplicator::fxShapeReplicator()
{
// Setup NetObject.
mTypeMask |= StaticObjectType | StaticTSObjectType | StaticRenderedObjectType;
mAddedToScene = false;
mNetFlags.set(Ghostable | ScopeAlways);
// Reset Client Replication Started.
mClientReplicationStarted = false;
// Reset Shape Count.
mCurrentShapeCount = 0;
// Reset Creation Area Angle Animation.
mCreationAreaAngle = 0;
}
//------------------------------------------------------------------------------
fxShapeReplicator::~fxShapeReplicator()
{
}
//------------------------------------------------------------------------------
void fxShapeReplicator::initPersistFields()
{
// Initialise parents' persistent fields.
Parent::initPersistFields();
// Add out own persistent fields.
addGroup( "Debugging" );
addField( "HideReplications", TypeBool, Offset( mFieldData.mHideReplications, fxShapeReplicator ) );
addField( "ShowPlacementArea", TypeBool, Offset( mFieldData.mShowPlacementArea, fxShapeReplicator ) );
addField( "PlacementAreaHeight",TypeS32, Offset( mFieldData.mPlacementBandHeight, fxShapeReplicator ) );
addField( "PlacementColour", TypeColorF, Offset( mFieldData.mPlaceAreaColour, fxShapeReplicator ) );
endGroup( "Debugging" );
addGroup( "Media" );
addField( "ShapeFile", TypeFilename, Offset( mFieldData.mShapeFile, fxShapeReplicator ) );
endGroup( "Media" );
addGroup( "Replications" );
addField( "Seed", TypeS32, Offset( mFieldData.mSeed, fxShapeReplicator ) );
addField( "ShapeCount", TypeS32, Offset( mFieldData.mShapeCount, fxShapeReplicator ) );
addField( "ShapeRetries", TypeS32, Offset( mFieldData.mShapeRetries, fxShapeReplicator ) );
endGroup( "Replications" );
addGroup( "Placement Radius" );
addField( "InnerRadiusX", TypeS32, Offset( mFieldData.mInnerRadiusX, fxShapeReplicator ) );
addField( "InnerRadiusY", TypeS32, Offset( mFieldData.mInnerRadiusY, fxShapeReplicator ) );
addField( "OuterRadiusX", TypeS32, Offset( mFieldData.mOuterRadiusX, fxShapeReplicator ) );
addField( "OuterRadiusY", TypeS32, Offset( mFieldData.mOuterRadiusY, fxShapeReplicator ) );
endGroup( "Placement Radius" );
addGroup( "Restraints" );
addField( "AllowOnTerrain", TypeBool, Offset( mFieldData.mAllowOnTerrain, fxShapeReplicator ) );
addField( "AllowOnInteriors", TypeBool, Offset( mFieldData.mAllowOnInteriors, fxShapeReplicator ) );
addField( "AllowOnStatics", TypeBool, Offset( mFieldData.mAllowStatics, fxShapeReplicator ) );
addField( "AllowOnWater", TypeBool, Offset( mFieldData.mAllowOnWater, fxShapeReplicator ) );
addField( "AllowWaterSurface", TypeBool, Offset( mFieldData.mAllowWaterSurface, fxShapeReplicator ) );
addField( "AlignToTerrain", TypeBool, Offset( mFieldData.mAlignToTerrain, fxShapeReplicator ) );
addField( "Interactions", TypeBool, Offset( mFieldData.mInteractions, fxShapeReplicator ) );
addField( "AllowedTerrainSlope",TypeS32, Offset( mFieldData.mAllowedTerrainSlope, fxShapeReplicator ) );
addField( "TerrainAlignment", TypePoint3F, Offset( mFieldData.mTerrainAlignment, fxShapeReplicator ) );
endGroup( "Restraints" );
addGroup( "Object Transforms" );
addField( "FixShapeAspect", TypeBool, Offset( mFieldData.mFixShapeAspect, fxShapeReplicator ) );
addField( "ShapeScaleMin", TypePoint3F, Offset( mFieldData.mShapeScaleMin, fxShapeReplicator ) );
addField( "ShapeScaleMax", TypePoint3F, Offset( mFieldData.mShapeScaleMax, fxShapeReplicator ) );
addField( "ShapeRotateMin", TypePoint3F, Offset( mFieldData.mShapeRotateMin, fxShapeReplicator ) );
addField( "ShapeRotateMax", TypePoint3F, Offset( mFieldData.mShapeRotateMax, fxShapeReplicator ) );
addField( "OffsetZ", TypeS32, Offset( mFieldData.mOffsetZ, fxShapeReplicator ) );
endGroup( "Object Transforms" );
}
//------------------------------------------------------------------------------
void fxShapeReplicator::CreateShapes(void)
{
F32 HypX, HypY;
F32 Angle;
U32 RelocationRetry;
Point3F ShapePosition;
Point3F ShapeStart;
Point3F ShapeEnd;
Point3F ShapeScale;
EulerF ShapeRotation;
QuatF QRotation;
bool CollisionResult;
RayInfo RayEvent;
TSShape* pShape;
Container* pContainer;
// Don't create shapes if we are hiding replications.
if (mFieldData.mHideReplications) return;
// Cannot continue without shapes!
if (mFieldData.mShapeFile == "") return;
// Check that we can position somewhere!
if (!( mFieldData.mAllowOnTerrain ||
mFieldData.mAllowOnInteriors ||
mFieldData.mAllowStatics ||
mFieldData.mAllowOnWater))
{
// Problem ...
Con::warnf(ConsoleLogEntry::General, "[%s] - Could not place object, All alloweds are off!", getName());
// Return here.
return;
}
// Check Shapes.
AssertFatal(mCurrentShapeCount==0,"Shapes already present, this should not be possible!")
// Check that we have a shape...
if (!mFieldData.mShapeFile) return;
// Set Seed.
RandomGen.setSeed(mFieldData.mSeed);
// Set shape vector.
mReplicatedShapes.reserve(mFieldData.mShapeCount);
// Add shapes.
for (U32 idx = 0; idx < mFieldData.mShapeCount; idx++)
{
fxShapeReplicatedStatic* fxStatic;
// Create our static shape.
fxStatic = new fxShapeReplicatedStatic();
// Set the 'shapeName' field.
fxStatic->setField("shapeName", mFieldData.mShapeFile);
// Is this Replicator on the Server?
if (isServerObject())
// Yes, so stop it from Ghosting. (Hack, Hack, Hack!)
fxStatic->touchNetFlags(Ghostable, false);
else
// No, so flag as ghost object. (Another damn Hack!)
fxStatic->touchNetFlags(IsGhost, true);
// Register the Object.
if (!fxStatic->registerObject())
{
// Problem ...
Con::warnf(ConsoleLogEntry::General, "[%s] - Could not load shape file '%s'!", getName(), mFieldData.mShapeFile);
// Destroy Shape.
delete fxStatic;
// Destroy existing hapes.
DestroyShapes();
// Quit.
return;
}
// Get Allocated Shape.
pShape = fxStatic->getShape();
// Reset Relocation Retry.
RelocationRetry = mFieldData.mShapeRetries;
// Find it a home ...
do
{
// Calculate a random offset
HypX = RandomGen.randF(mFieldData.mInnerRadiusX, mFieldData.mOuterRadiusX);
HypY = RandomGen.randF(mFieldData.mInnerRadiusY, mFieldData.mOuterRadiusY);
Angle = RandomGen.randF(0, M_2PI);
// Calculate the new random position (in local space).
Point3F randomShapePosLocal;
randomShapePosLocal.x = HypX * mCos(Angle);
randomShapePosLocal.y = HypY * mSin(Angle);
// Transform into world space coordinates
Point3F shapePosWorld;
MatrixF objToWorld = getRenderTransform();
objToWorld.mulP(randomShapePosLocal, &shapePosWorld);
ShapePosition = shapePosWorld;
// Initialise RayCast Search Start/End Positions.
ShapeStart = ShapeEnd = ShapePosition;
ShapeStart.z = 2000.f;
ShapeEnd.z= -2000.f;
// Is this the Server?
if (isServerObject())
// Perform Ray Cast Collision on Server Terrain.
CollisionResult = gServerContainer.castRay(ShapeStart, ShapeEnd, FXREPLICATOR_COLLISION_MASK, &RayEvent);
else
// Perform Ray Cast Collision on Client Terrain.
CollisionResult = gClientContainer.castRay( ShapeStart, ShapeEnd, FXREPLICATOR_COLLISION_MASK, &RayEvent);
// Did we hit anything?
if (CollisionResult)
{
// For now, let's pretend we didn't get a collision.
CollisionResult = false;
// Yes, so get it's type.
U32 CollisionType = RayEvent.object->getTypeMask();
// Check Illegal Placements.
if (((CollisionType & TerrainObjectType) && !mFieldData.mAllowOnTerrain) ||
((CollisionType & InteriorObjectType) && !mFieldData.mAllowOnInteriors) ||
((CollisionType & StaticTSObjectType) && !mFieldData.mAllowStatics) ||
((CollisionType & WaterObjectType) && !mFieldData.mAllowOnWater) ) continue;
// If we collided with water and are not allowing on the water surface then let's find the
// terrain underneath and pass this on as the original collision else fail.
//
// NOTE:- We need to do this on the server/client as appropriate.
if ((CollisionType & WaterObjectType) && !mFieldData.mAllowWaterSurface)
{
// Is this the Server?
if (isServerObject())
{
// Yes, so do it on the server container.
if (!gServerContainer.castRay( ShapeStart, ShapeEnd, FXREPLICATOR_NOWATER_COLLISION_MASK, &RayEvent)) continue;
}
else
{
// No, so do it on the client container.
if (!gClientContainer.castRay( ShapeStart, ShapeEnd, FXREPLICATOR_NOWATER_COLLISION_MASK, &RayEvent)) continue;
}
}
// We passed with flying colours so carry on.
CollisionResult = true;
}
// Invalidate if we are below Allowed Terrain Angle.
if (RayEvent.normal.z < mSin(mDegToRad(90.0f-mFieldData.mAllowedTerrainSlope))) CollisionResult = false;
// Wait until we get a collision.
} while(!CollisionResult && --RelocationRetry);
// Check for Relocation Problem.
if (RelocationRetry > 0)
{
// Adjust Impact point.
RayEvent.point.z += mFieldData.mOffsetZ;
// Set New Position.
ShapePosition = RayEvent.point;
}
else
{
// Warning.
Con::warnf(ConsoleLogEntry::General, "[%s] - Could not find satisfactory position for shape '%s' on %s!", getName(), mFieldData.mShapeFile,isServerObject()?"Server":"Client");
// Unregister Object.
fxStatic->unregisterObject();
// Destroy Shape.
delete fxStatic;
// Skip to next.
continue;
}
// Get Shape Transform.
MatrixF XForm = fxStatic->getTransform();
// Are we aligning to Terrain?
if (mFieldData.mAlignToTerrain)
{
// Yes, so set rotation to Terrain Impact Normal.
ShapeRotation = RayEvent.normal * mFieldData.mTerrainAlignment;
}
else
{
// No, so choose a new Rotation (in Radians).
ShapeRotation.set( mDegToRad(RandomGen.randF(mFieldData.mShapeRotateMin.x, mFieldData.mShapeRotateMax.x)),
mDegToRad(RandomGen.randF(mFieldData.mShapeRotateMin.y, mFieldData.mShapeRotateMax.y)),
mDegToRad(RandomGen.randF(mFieldData.mShapeRotateMin.z, mFieldData.mShapeRotateMax.z)));
}
// Set Quaternion Roation.
QRotation.set(ShapeRotation);
// Set Transform Rotation.
QRotation.setMatrix(&XForm);
// Set Position.
XForm.setColumn(3, ShapePosition);
// Set Shape Position / Rotation.
fxStatic->setTransform(XForm);
// Choose a new Scale.
F32 scaleX, scaleY, scaleZ;
// Choose a random X-Scale.
scaleX = RandomGen.randF(mFieldData.mShapeScaleMin.x, mFieldData.mShapeScaleMax.x);
// Fix Shape Aspect?
if ( mFieldData.mFixShapeAspect )
{
// Yes, so use Scale-X for all axis.
scaleY = scaleZ = scaleX;
}
else
{
// No, so choose Scale-Y/Z.
scaleY = RandomGen.randF(mFieldData.mShapeScaleMin.y, mFieldData.mShapeScaleMax.y);
scaleZ = RandomGen.randF(mFieldData.mShapeScaleMin.z, mFieldData.mShapeScaleMax.z);
}
// Set Shape Scale.
ShapeScale.set( scaleX, scaleY, scaleZ );
// Set Shape Scale.
fxStatic->setScale(ShapeScale);
// Lock it.
fxStatic->setLocked(true);
// Store Shape in Replicated Shapes Vector.
mReplicatedShapes[mCurrentShapeCount++] = fxStatic;
}
}
//------------------------------------------------------------------------------
void fxShapeReplicator::DestroyShapes(void)
{
// Finish if we didn't create any shapes.
if (mCurrentShapeCount == 0) return;
// Remove shapes.
for (U32 idx = 0; idx < mCurrentShapeCount; idx++)
{
fxShapeReplicatedStatic* fxStatic;
// Fetch the Shape Object.
fxStatic = mReplicatedShapes[idx];
// Got a Shape?
if (fxStatic)
{
// Unlock it.
fxStatic->setLocked(false);
// Unregister the object.
fxStatic->unregisterObject();
// Delete it.
delete fxStatic;
}
}
// Empty the Replicated Shapes Vector.
mReplicatedShapes.empty();
// Reset Shape Count.
mCurrentShapeCount = 0;
}
//------------------------------------------------------------------------------
void fxShapeReplicator::RenewShapes(void)
{
// Destroy any shapes.
DestroyShapes();
// Don't create shapes on the Server if we don't need interactions.
if (isServerObject() && !mFieldData.mInteractions) return;
// Create Shapes.
CreateShapes();
}
//------------------------------------------------------------------------------
void fxShapeReplicator::StartUp(void)
{
// Flag, Client Replication Started.
mClientReplicationStarted = true;
// Renew shapes.
RenewShapes();
}
//------------------------------------------------------------------------------
bool fxShapeReplicator::onAdd()
{
if(!Parent::onAdd()) return(false);
// Add the Replicator to the Replicator Set.
dynamic_cast<SimSet*>(Sim::findObject("fxReplicatorSet"))->addObject(this);
// Set Default Object Box.
mObjBox.min.set( -0.5, -0.5, -0.5 );
mObjBox.max.set( 0.5, 0.5, 0.5 );
// Reset the World Box.
resetWorldBox();
// Are we editing the Mission?
if(gEditingMission)
{
// Yes, so set the Render Transform.
setRenderTransform(mObjToWorld);
// Add to Scene.
addToScene();
mAddedToScene = true;
// If we are in the editor and we are on the client then
// we can manually startup replication.
if (isClientObject()) mClientReplicationStarted = true;
}
// Renew shapes on Server.
if (isServerObject()) RenewShapes();
// Return OK.
return(true);
}
//------------------------------------------------------------------------------
void fxShapeReplicator::onRemove()
{
// Remove the Replicator from the Replicator Set.
dynamic_cast<SimSet*>(Sim::findObject("fxReplicatorSet"))->removeObject(this);
// Are we editing the Mission?
if(gEditingMission)
{
// Yes, so remove from Scene.
removeFromScene();
mAddedToScene = false;
}
// Destroy Shapes.
DestroyShapes();
// Do Parent.
Parent::onRemove();
}
//------------------------------------------------------------------------------
void fxShapeReplicator::inspectPostApply()
{
// Set Parent.
Parent::inspectPostApply();
// Renew Shapes.
RenewShapes();
// Set Replication Mask.
setMaskBits(ReplicationMask);
}
//------------------------------------------------------------------------------
void fxShapeReplicator::onEditorEnable()
{
// Are we in the Scene?
if(!mAddedToScene)
{
// No, so add to scene.
addToScene();
mAddedToScene = true;
}
}
//------------------------------------------------------------------------------
void fxShapeReplicator::onEditorDisable()
{
// Are we in the Scene?
if(mAddedToScene)
{
// Yes, so remove from scene.
removeFromScene();
mAddedToScene = false;
}
}
//------------------------------------------------------------------------------
ConsoleFunction(StartClientReplication, void, 1, 1, "StartClientReplication()")
{
// Find the Replicator Set.
SimSet *fxReplicatorSet = dynamic_cast<SimSet*>(Sim::findObject("fxReplicatorSet"));
// Return if Error.
if (!fxReplicatorSet) return;
// StartUp Replication Object.
for (SimSetIterator itr(fxReplicatorSet); *itr; ++itr)
{
// Fetch the Replicator Object.
fxShapeReplicator* Replicator = static_cast<fxShapeReplicator*>(*itr);
// Start Client Objects Only.
if (Replicator->isClientObject()) Replicator->StartUp();
}
// Info ...
Con::printf("Client Replication Startup has Happened!");
}
//------------------------------------------------------------------------------
bool fxShapeReplicator::prepRenderImage(SceneState* state, const U32 stateKey, const U32 startZone,
const bool modifyBaseZoneState)
{
// Return if last state.
if (isLastState(state, stateKey)) return false;
// Set Last State.
setLastState(state, stateKey);
// Is Object Rendered?
if (state->isObjectRendered(this))
{
// Yes, so get a SceneRenderImage.
SceneRenderImage* image = new SceneRenderImage;
// Populate it.
image->obj = this;
image->sortType = SceneRenderImage::Normal;
// Insert it into the scene images.
state->insertRenderImage(image);
}
return false;
}
//------------------------------------------------------------------------------
void fxShapeReplicator::renderObject(SceneState* state, SceneRenderImage*)
{
// Return if placement area not needed.
if (!mFieldData.mShowPlacementArea) return;
// Check we are in Canonical State.
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
// Setup out the Projection Matrix/Viewport.
RectI viewport;
glMatrixMode(GL_PROJECTION);
glPushMatrix();
dglGetViewport(&viewport);
state->setupBaseProjection();
// Setup our rendering state.
glPushMatrix();
dglMultMatrix(&getTransform());
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
// Do we need to draw the Inner Radius?
if (mFieldData.mInnerRadiusX || mFieldData.mInnerRadiusY)
{
// Yes, so draw Inner Radius.
glBegin(GL_TRIANGLE_STRIP);
for (U32 Angle = mCreationAreaAngle; Angle < (mCreationAreaAngle+360); Angle++)
{
F32 XPos, YPos;
// Calculate Position.
XPos = mFieldData.mInnerRadiusX * mCos(mDegToRad(-(F32)Angle));
YPos = mFieldData.mInnerRadiusY * mSin(mDegToRad(-(F32)Angle));
// Set Colour.
glColor4f( mFieldData.mPlaceAreaColour.red,
mFieldData.mPlaceAreaColour.green,
mFieldData.mPlaceAreaColour.blue,
AREA_ANIMATION_ARC * (Angle-mCreationAreaAngle));
// Draw Arc Line.
glVertex3f( XPos, YPos, -(F32)mFieldData.mPlacementBandHeight/2.0f);
glVertex3f( XPos, YPos, +(F32)mFieldData.mPlacementBandHeight/2.0f);
}
glEnd();
}
// Do we need to draw the Outer Radius?
if (mFieldData.mOuterRadiusX || mFieldData.mOuterRadiusY)
{
// Yes, so draw Outer Radius.
glBegin(GL_TRIANGLE_STRIP);
for (U32 Angle = mCreationAreaAngle; Angle < (mCreationAreaAngle+360); Angle++)
{
F32 XPos, YPos;
// Calculate Position.
XPos = mFieldData.mOuterRadiusX * mCos(mDegToRad(-(F32)Angle));
YPos = mFieldData.mOuterRadiusY * mSin(mDegToRad(-(F32)Angle));
// Set Colour.
glColor4f( mFieldData.mPlaceAreaColour.red,
mFieldData.mPlaceAreaColour.green,
mFieldData.mPlaceAreaColour.blue,
AREA_ANIMATION_ARC * (Angle-mCreationAreaAngle));
// Draw Arc Line.
glVertex3f( XPos, YPos, -(F32)mFieldData.mPlacementBandHeight/2.0f);
glVertex3f( XPos, YPos, +(F32)mFieldData.mPlacementBandHeight/2.0f);
}
glEnd();
}
// Restore rendering state.
glDisable(GL_BLEND);
glPopMatrix();
// Animate Area Selection.
mCreationAreaAngle += 20;
mCreationAreaAngle = mCreationAreaAngle % 360;
// Restore out nice and friendly canonical state.
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
dglSetViewport(viewport);
// Check we have restored Canonical State.
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
}
//------------------------------------------------------------------------------
U32 fxShapeReplicator::packUpdate(NetConnection * con, U32 mask, BitStream * stream)
{
// Pack Parent.
U32 retMask = Parent::packUpdate(con, mask, stream);
// Write Replication Flag.
if (stream->writeFlag(mask & ReplicationMask))
{
stream->writeAffineTransform(mObjToWorld); // Replicator Position.
stream->writeInt(mFieldData.mSeed, 32); // Replicator Seed.
stream->writeInt(mFieldData.mShapeCount, 32); // Shapes Count.
stream->writeInt(mFieldData.mShapeRetries, 32); // Shapes Retries.
stream->writeString(mFieldData.mShapeFile);
stream->writeInt(mFieldData.mInnerRadiusX, 32); // Shapes Inner Radius X.
stream->writeInt(mFieldData.mInnerRadiusY, 32); // Shapes Inner Radius Y.
stream->writeInt(mFieldData.mOuterRadiusX, 32); // Shapes Outer Radius X.
stream->writeInt(mFieldData.mOuterRadiusY, 32); // Shapes Outer Radius Y.
mathWrite(*stream, mFieldData.mShapeScaleMin); // Shapes Scale Min.
mathWrite(*stream, mFieldData.mShapeScaleMax); // Shapes Scale Max.
mathWrite(*stream, mFieldData.mShapeRotateMin); // Shapes Rotate Min.
mathWrite(*stream, mFieldData.mShapeRotateMax); // Shapes Rotate Max.
stream->writeSignedInt(mFieldData.mOffsetZ, 32); // Shapes Offset Z.
stream->writeFlag(mFieldData.mAllowOnTerrain); // Allow on Terrain.
stream->writeFlag(mFieldData.mAllowOnInteriors); // Allow on Interiors.
stream->writeFlag(mFieldData.mAllowStatics); // Allow on Statics.
stream->writeFlag(mFieldData.mAllowOnWater); // Allow on Water.
stream->writeFlag(mFieldData.mAllowWaterSurface); // Allow on Water Surface.
stream->writeSignedInt(mFieldData.mAllowedTerrainSlope, 32); // Shapes Offset Z.
stream->writeFlag(mFieldData.mAlignToTerrain); // Shapes AlignToTerrain.
mathWrite(*stream, mFieldData.mTerrainAlignment); // Write Terrain Alignment.
stream->writeFlag(mFieldData.mHideReplications); // Hide Replications.
stream->writeFlag(mFieldData.mInteractions); // Shape Interactions.
stream->writeFlag(mFieldData.mShowPlacementArea); // Show Placement Area Flag.
stream->writeInt(mFieldData.mPlacementBandHeight, 32); // Placement Area Height.
stream->write(mFieldData.mPlaceAreaColour);
}
// Were done ...
return(retMask);
}
//------------------------------------------------------------------------------
void fxShapeReplicator::unpackUpdate(NetConnection * con, BitStream * stream)
{
// Unpack Parent.
Parent::unpackUpdate(con, stream);
// Read Replication Details.
if(stream->readFlag())
{
MatrixF ReplicatorObjectMatrix;
stream->readAffineTransform(&ReplicatorObjectMatrix); // Replication Position.
mFieldData.mSeed = stream->readInt(32); // Replicator Seed.
mFieldData.mShapeCount = stream->readInt(32); // Shapes Count.
mFieldData.mShapeRetries = stream->readInt(32); // Shapes Retries.
mFieldData.mShapeFile = stream->readSTString(); // Shape File.
mFieldData.mInnerRadiusX = stream->readInt(32); // Shapes Inner Radius X.
mFieldData.mInnerRadiusY = stream->readInt(32); // Shapes Inner Radius Y.
mFieldData.mOuterRadiusX = stream->readInt(32); // Shapes Outer Radius X.
mFieldData.mOuterRadiusY = stream->readInt(32); // Shapes Outer Radius Y.
mathRead(*stream, &mFieldData.mShapeScaleMin); // Shapes Scale Min.
mathRead(*stream, &mFieldData.mShapeScaleMax); // Shapes Scale Max.
mathRead(*stream, &mFieldData.mShapeRotateMin); // Shapes Rotate Min.
mathRead(*stream, &mFieldData.mShapeRotateMax); // Shapes Rotate Max.
mFieldData.mOffsetZ = stream->readSignedInt(32); // Shapes Offset Z.
mFieldData.mAllowOnTerrain = stream->readFlag(); // Allow on Terrain.
mFieldData.mAllowOnInteriors = stream->readFlag(); // Allow on Interiors.
mFieldData.mAllowStatics = stream->readFlag(); // Allow on Statics.
mFieldData.mAllowOnWater = stream->readFlag(); // Allow on Water.
mFieldData.mAllowWaterSurface = stream->readFlag(); // Allow on Water Surface.
mFieldData.mAllowedTerrainSlope = stream->readSignedInt(32); // Allowed Terrain Slope.
mFieldData.mAlignToTerrain = stream->readFlag(); // Read AlignToTerrain.
mathRead(*stream, &mFieldData.mTerrainAlignment); // Read Terrain Alignment.
mFieldData.mHideReplications = stream->readFlag(); // Hide Replications.
mFieldData.mInteractions = stream->readFlag(); // Read Interactions.
mFieldData.mShowPlacementArea = stream->readFlag(); // Show Placement Area Flag.
mFieldData.mPlacementBandHeight = stream->readInt(32); // Placement Area Height.
stream->read(&mFieldData.mPlaceAreaColour);
// Set Transform.
setTransform(ReplicatorObjectMatrix);
// Renew Shapes (only if replication has started).
if (mClientReplicationStarted) RenewShapes();
}
}

View File

@ -0,0 +1,178 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Written by Melvyn May, 13th March 2002.
//-----------------------------------------------------------------------------
#ifndef _SHAPEREPLICATOR_H_
#define _SHAPEREPLICATOR_H_
#ifndef _SCENEOBJECT_H_
#include "sim/sceneObject.h"
#endif
#ifndef _TSSTATIC_H_
#include "game/tsStatic.h"
#endif
#ifndef _TSSHAPEINSTANCE_H_
#include "ts/tsShapeInstance.h"
#endif
#define AREA_ANIMATION_ARC (1.0f / 360.0f)
#define FXREPLICATOR_COLLISION_MASK ( TerrainObjectType | \
InteriorObjectType | \
StaticObjectType | \
WaterObjectType )
#define FXREPLICATOR_NOWATER_COLLISION_MASK ( TerrainObjectType | \
InteriorObjectType | \
StaticObjectType )
//------------------------------------------------------------------------------
// Class: fxShapeReplicatedStatic
//------------------------------------------------------------------------------
class fxShapeReplicatedStatic : public TSStatic
{
private:
typedef SceneObject Parent;
public:
fxShapeReplicatedStatic() {};
~fxShapeReplicatedStatic() {};
void touchNetFlags(const U32 m, bool setflag = true) { if (setflag) mNetFlags.set(m); else mNetFlags.clear(m); };
TSShape* getShape(void) { return mShapeInstance->getShape(); };
void setTransform(const MatrixF & mat) { Parent::setTransform(mat); setRenderTransform(mat); };
DECLARE_CONOBJECT(fxShapeReplicatedStatic);
};
//------------------------------------------------------------------------------
// Class: fxShapeReplicator
//------------------------------------------------------------------------------
class fxShapeReplicator : public SceneObject
{
private:
typedef SceneObject Parent;
protected:
void CreateShapes(void);
void DestroyShapes(void);
void RenewShapes(void);
enum { ReplicationMask = (1 << 0) };
U32 mCreationAreaAngle;
bool mClientReplicationStarted;
bool mAddedToScene;
U32 mCurrentShapeCount;
Vector<fxShapeReplicatedStatic*> mReplicatedShapes;
MRandomLCG RandomGen;
public:
fxShapeReplicator();
~fxShapeReplicator();
void StartUp(void);
void ShowReplication(void);
void HideReplication(void);
// SceneObject
void renderObject(SceneState *state, SceneRenderImage *image);
virtual bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone,
const bool modifyBaseZoneState = false);
// SimObject
bool onAdd();
void onRemove();
void onEditorEnable();
void onEditorDisable();
void inspectPostApply();
// NetObject
U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
void unpackUpdate(NetConnection *conn, BitStream *stream);
// ConObject.
static void initPersistFields();
// Field Data.
class tagFieldData
{
public:
U32 mSeed;
StringTableEntry mShapeFile;
U32 mShapeCount;
U32 mShapeRetries;
bool mFixShapeAspect;
Point3F mShapeScaleMin;
Point3F mShapeScaleMax;
Point3F mShapeRotateMin;
Point3F mShapeRotateMax;
U32 mInnerRadiusX;
U32 mInnerRadiusY;
U32 mOuterRadiusX;
U32 mOuterRadiusY;
S32 mOffsetZ;
bool mAllowOnTerrain;
bool mAllowOnInteriors;
bool mAllowStatics;
bool mAllowOnWater;
S32 mAllowedTerrainSlope;
bool mAlignToTerrain;
bool mAllowWaterSurface;
Point3F mTerrainAlignment;
bool mInteractions;
bool mHideReplications;
bool mShowPlacementArea;
U32 mPlacementBandHeight;
ColorF mPlaceAreaColour;
tagFieldData()
{
// Set Defaults.
mSeed = 1376312589;
mShapeFile = StringTable->insert("");
mShapeCount = 10;
mShapeRetries = 100;
mInnerRadiusX = 0;
mInnerRadiusY = 0;
mOuterRadiusX = 100;
mOuterRadiusY = 100;
mOffsetZ = 0;
mAllowOnTerrain = true;
mAllowOnInteriors = true;
mAllowStatics = true;
mAllowOnWater = false;
mAllowWaterSurface = false;
mAllowedTerrainSlope= 90;
mAlignToTerrain = false;
mInteractions = true;
mHideReplications = false;
mShowPlacementArea = true;
mPlacementBandHeight = 25;
mPlaceAreaColour .set(0.4f, 0, 0.8f);
mFixShapeAspect = false;
mShapeScaleMin .set(1, 1, 1);
mShapeScaleMax .set(1, 1, 1);
mShapeRotateMin .set(0, 0, 0);
mShapeRotateMax .set(0, 0, 0);
mTerrainAlignment .set(1, 1, 1);
}
} mFieldData;
// Declare Console Object.
DECLARE_CONOBJECT(fxShapeReplicator);
};
#endif // _SHAPEREPLICATOR_H_

2172
engine/game/fx/fxSunLight.cc Executable file

File diff suppressed because it is too large Load Diff

252
engine/game/fx/fxSunLight.h Executable file
View File

@ -0,0 +1,252 @@
//-----------------------------------------------------------------------------
// Torque Game Engine - fxLight
//
// Written by Melvyn May, 27th August 2002.
//-----------------------------------------------------------------------------
#ifndef _FXSUNLIGHT_H_
#define _FXSUNLIGHT_H_
#ifndef _SCENEOBJECT_H_
#include "sim/sceneObject.h"
#endif
//------------------------------------------------------------------------------
// Class: fxSunLight
//------------------------------------------------------------------------------
class fxSunLight : public SceneObject
{
private:
typedef SceneObject Parent;
U32 CheckKeySyntax(StringTableEntry Key);
void CheckAnimationKeys(void);
F32 GetLerpKey(StringTableEntry Key, U32 PosFrom, U32 PosTo, F32 ValueFrom, F32 ValueTo, F32 Lerp);
void AnimateSun(F32 ElapsedTime);
void InitialiseAnimation(void);
void ResetAnimation(void);
bool TestLOS(const Point3F& Pos);
protected:
enum { fxSunLightConfigChangeMask = BIT(0) };
bool mAddedToScene;
MRandomLCG RandomGen;
// Textures.
TextureHandle mLocalFlareTextureHandle;
TextureHandle mRemoteFlareTextureHandle;
F32 mElapsedTime;
S32 mLastRenderTime;
F32 mLocalFlareScale;
Point3F mSunlightPosition; // Sunlight Frame Position.
public:
fxSunLight();
~fxSunLight();
// *********************************
// Configuration Interface.
// *********************************
// Debugging.
void setEnable(bool Status);
// Media.
void setFlareBitmaps(const char* LocalName, const char* RemoteName);
// Sun Orbit.
void setSunAzimuth(F32 Azimuth);
void setSunElevation(F32 Elevation);
// Flare.
void setFlareTP(bool Status);
void setFlareColour(ColorF Colour);
void setFlareBrightness(F32 Brightness);
void setFlareSize(F32 Size);
void setFadeTime(F32 Time);
void setBlendMode(U32 Mode);
// Animation Options.
void setUseColour(bool Status);
void setUseBrightness(bool Status);
void setUseRotation(bool Status);
void setUseSize(bool Status);
void setUseAzimuth(bool Status);
void setUseElevation(bool Status);
void setLerpColour(bool Status);
void setLerpBrightness(bool Status);
void setLerpRotation(bool Status);
void setLerpSize(bool Status);
void setLerpAzimuth(bool Status);
void setLerpElevation(bool Status);
void setLinkFlareSize(bool Status);
void setSingleColourKeys(bool Status);
// Animation Extents.
void setMinColour(ColorF Colour);
void setMaxColour(ColorF Colour);
void setMinBrightness(F32 Brightness);
void setMaxBrightness(F32 Brightness);
void setMinRotation(F32 Rotation);
void setMaxRotation(F32 Rotation);
void setMinSize(F32 Size);
void setMaxSize(F32 Size);
void setMinAzimuth(F32 Azimuth);
void setMaxAzimuth(F32 Azimuth);
void setMinElevation(F32 Elevation);
void setMaxElevation(F32 Elevation);
// Animation Keys.
void setRedKeys(const char* Keys);
void setGreenKeys(const char* Keys);
void setBlueKeys(const char* Keys);
void setBrightnessKeys(const char* Keys);
void setRotationKeys(const char* Keys);
void setSizeKeys(const char* Keys);
void setAzimuthKeys(const char* Keys);
void setElevationKeys(const char* Keys);
// Animation Times.
void setColourTime(F32 Time);
void setBrightnessTime(F32 Time);
void setRotationTime(F32 Time);
void setSizeTime(F32 Time);
void setAzimuthTime(F32 Time);
void setElevationTime(F32 Time);
// Misc,
void reset(void);
// SceneObject
bool prepRenderImage(SceneState* state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState);
void renderObject(SceneState *state, SceneRenderImage *image);
// SimObject
bool onAdd();
void onRemove();
void inspectPostApply();
// NetObject
U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
void unpackUpdate(NetConnection *conn, BitStream *stream);
// ConObject.
static void initPersistFields();
bool mDoneSunLock;
// Field Data.
// Debugging.
bool mEnable; // Light Enable.
// Media.
StringTableEntry mLocalFlareTextureName; // Local Flare Texture Name.
StringTableEntry mRemoteFlareTextureName; // Remote Flare Texture Name.
// Sun Orbit.
F32 mSunAzimuth;
F32 mSunElevation;
bool mLockToRealSun; // Lock to Real Sun Flag.
// Flare.
bool mFlareTP; // Flare Third Person Flag.
ColorF mFlareColour; // Flare Colour.
F32 mFlareBrightness; // Brightness.
F32 mFlareSize; // Flare Size.
F32 mFadeTime; // Fade Time.
U32 mBlendMode; // Blend Mode.
// Animation Options.
bool mUseColour; // Use Colour Flag.
bool mUseBrightness; // Use Brightness Flag.
bool mUseRotation; // Use Rotation Flag.
bool mUseSize; // Use Size Flag.
bool mUseAzimuth; // Use Azimuth Flag.
bool mUseElevation; // Use Elevation Flag.
bool mLerpColour; // Lerp Colour Flag.
bool mLerpBrightness; // Lerp Brightness Flag.
bool mLerpRotation; // Lerp Rotation Flag.
bool mLerpSize; // Lerp Size Flag.
bool mLerpAzimuth; // Lerp Azimuth Flag.
bool mLerpElevation; // Lerp Elevation Flag.
bool mLinkFlareSize; // Link Flare Size Animation.
bool mSingleColourKeys; // Single-Channel Colour Keys.
// Animation Extents.
ColorF mMinColour; // Minimum Colour.
ColorF mMaxColour; // Maximum Colour.
F32 mMinBrightness; // Minimum Brightness.
F32 mMaxBrightness; // Maximum Brightness.
F32 mMinRotation; // Minimum Rotation.
F32 mMaxRotation; // Maximum Rotation.
F32 mMinSize; // Minimum Size.
F32 mMaxSize; // Maximum Size.
F32 mMinAzimuth; // Minimum Azimuth.
F32 mMaxAzimuth; // Maximum Azimuth.
F32 mMinElevation; // Minimum Elevation.
F32 mMaxElevation; // Maximum Elevation.
// Animation Keys.
StringTableEntry mRedKeys; // Red Animation Keys.
StringTableEntry mGreenKeys; // Green Animation Keys.
StringTableEntry mBlueKeys; // Blue Animation Keys.
StringTableEntry mBrightnessKeys; // Brightness Animation Keys.
StringTableEntry mRotationKeys; // Rotation Animation Keys.
StringTableEntry mSizeKeys; // Size Animation Keys.
StringTableEntry mAzimuthKeys; // Size Azimuth Keys.
StringTableEntry mElevationKeys; // Size Elevation Keys.
// Animation Times.
F32 mColourTime; // Colour Time (Seconds).
F32 mBrightnessTime; // Brightness Time.
F32 mRotationTime; // Rotation Time.
F32 mSizeTime; // Size Time.
F32 mAzimuthTime; // Azimuth Time.
F32 mElevationTime; // Elevation Time.
// Current Animation.
ColorF mAnimationColour; // Current Colour.
F32 mAnimationBrightness; // Current Brightness.
F32 mAnimationRotation; // Current Rotation.
F32 mAnimationSize; // Current Size.
F32 mAnimationAzimuth; // Current Azimuth.
F32 mAnimationElevation; // Current Elevation.
// Elapsed Times.
F32 mColourElapsedTime; // Colour Elapsed Time.
F32 mBrightnessElapsedTime; // Brightness Elapsed Time.
F32 mRotationElapsedTime; // Rotation Elapsed Time.
F32 mSizeElapsedTime; // Size Elapsed Time.
F32 mAzimuthElapsedTime; // Azimuth Elapsed Time.
F32 mElevationElapsedTime; // Elevation Elapsed Time.
// Time Scales.
F32 mColourTimeScale; // Colour Time Scale.
F32 mBrightnessTimeScale; // Brightness Time Scale.
F32 mRotationTimeScale; // Rotation Time Scale.
F32 mSizeTimeScale; // Size Time Scale.
F32 mAzimuthTimeScale; // Azimuth Time Scale.
F32 mElevationTimeScale; // Elevation Time Scale.
// Key Lengths (Validity).
U32 mRedKeysLength; // Red Keys Length.
U32 mGreenKeysLength; // Green Keys Length.
U32 mBlueKeysLength; // Blue Keys Length.
U32 mBrightnessKeysLength; // Brightness Keys Length.
U32 mRotationKeysLength; // Rotation Keys Length.
U32 mSizeKeysLength; // Size Keys Length.
U32 mAzimuthKeysLength; // Azimuth Keys Length.
U32 mElevationKeysLength; // Elevation Keys Length.
// Declare Console Object.
DECLARE_CONOBJECT(fxSunLight);
};
#endif // _FXSUNLIGHT_H_

1226
engine/game/fx/lightning.cc Executable file

File diff suppressed because it is too large Load Diff

214
engine/game/fx/lightning.h Executable file
View File

@ -0,0 +1,214 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _LIGHTNING_H_
#define _LIGHTNING_H_
#ifndef _GAMEBASE_H_
#include "game/gameBase.h"
#endif
#ifndef _GTEXMANAGER_H_
#include "dgl/gTexManager.h"
#endif
#ifndef _LLIST_H_
#include "core/llist.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
class ShapeBase;
class LightningStrikeEvent;
class AudioProfile;
// -------------------------------------------------------------------------
class LightningData : public GameBaseData
{
typedef GameBaseData Parent;
public:
enum Constants {
MaxThunders = 8,
MaxTextures = 8
};
//-------------------------------------- Console set variables
public:
AudioProfile* thunderSounds[MaxThunders];
AudioProfile* strikeSound;
StringTableEntry strikeTextureNames[MaxTextures];
//-------------------------------------- load set variables
public:
S32 thunderSoundIds[MaxThunders];
S32 strikeSoundID;
TextureHandle strikeTextures[MaxTextures];
U32 numThunders;
protected:
bool onAdd();
public:
LightningData();
~LightningData();
void packData(BitStream*);
void unpackData(BitStream*);
bool preload(bool server, char errorBuffer[256]);
DECLARE_CONOBJECT(LightningData);
static void initPersistFields();
};
// -------------------------------------------------------------------------
struct LightningBolt
{
struct Node
{
Point3F point;
VectorF dirToMainLine;
};
struct NodeManager
{
Node nodeList[10];
Point3F startPoint;
Point3F endPoint;
U32 numNodes;
F32 maxAngle;
void generateNodes();
};
NodeManager mMajorNodes;
Vector< NodeManager > mMinorNodes;
LList< LightningBolt > splitList;
F32 lifetime;
F32 elapsedTime;
F32 fadeTime;
bool isFading;
F32 percentFade;
bool startRender;
F32 renderTime;
F32 width;
F32 chanceOfSplit;
Point3F startPoint;
Point3F endPoint;
U32 numMajorNodes;
F32 maxMajorAngle;
U32 numMinorNodes;
F32 maxMinorAngle;
LightningBolt();
~LightningBolt();
void createSplit( Point3F startPoint, Point3F endPoint, U32 depth, F32 width );
F32 findHeight( Point3F &point, SceneGraph* sceneManager );
void render( const Point3F &camPos );
void renderSegment( NodeManager &segment, const Point3F &camPos, bool renderLastPoint );
void generate();
void generateMinorNodes();
void startSplits();
void update( F32 dt );
};
// -------------------------------------------------------------------------
class Lightning : public GameBase
{
typedef GameBase Parent;
protected:
bool onAdd();
void onRemove();
bool onNewDataBlock(GameBaseData* dptr);
struct Strike {
F32 xVal; // Position in cloud layer of strike
F32 yVal; // top
bool targetedStrike; // Is this a targeted strike?
U32 targetGID;
F32 deathAge; // Age at which this strike expires
F32 currentAge; // Current age of this strike (updated by advanceTime)
LightningBolt bolt[3];
Strike* next;
};
struct Thunder {
F32 tRemaining;
Thunder* next;
};
public:
//-------------------------------------- Console set variables
public:
U32 strikesPerMinute;
F32 strikeWidth;
F32 chanceToHitTarget;
F32 strikeRadius;
F32 boltStartRadius;
ColorF color;
ColorF fadeColor;
bool useFog;
// Rendering
protected:
bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState);
void renderObject(SceneState *state, SceneRenderImage *image);
// Time management
void processTick(const Move *move);
void interpolateTick(F32 delta);
void advanceTime(F32 dt);
// Strike management
void scheduleThunder(Strike*);
// Data members
private:
LightningData* mDataBlock;
protected:
U32 mLastThink; // Valid only on server
Strike* mStrikeListHead; // Valid on on the client
Thunder* mThunderListHead;
static const U32 csmTargetMask;
public:
Lightning();
~Lightning();
void applyDamage( const Point3F& hitPosition, const Point3F& hitNormal, SceneObject* hitObject );
void warningFlashes();
void strikeRandomPoint();
void strikeObject(ShapeBase*);
void processEvent(LightningStrikeEvent*);
DECLARE_CONOBJECT(Lightning);
static void initPersistFields();
U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream);
void unpackUpdate(NetConnection *conn, BitStream *stream);
};
#endif // _H_LIGHTNING

262
engine/game/fx/particleEmitter.cc Executable file
View File

@ -0,0 +1,262 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/consoleTypes.h"
#include "core/bitStream.h"
#include "game/fx/particleEmitter.h"
#include "game/fx/particleEngine.h"
#include "math/mathIO.h"
IMPLEMENT_CO_DATABLOCK_V1(ParticleEmitterNodeData);
IMPLEMENT_CO_NETOBJECT_V1(ParticleEmitterNode);
//--------------------------------------------------------------------------
//--------------------------------------
//
ParticleEmitterNodeData::ParticleEmitterNodeData()
{
timeMultiple = 1.0;
}
ParticleEmitterNodeData::~ParticleEmitterNodeData()
{
}
//--------------------------------------------------------------------------
void ParticleEmitterNodeData::initPersistFields()
{
Parent::initPersistFields();
addField("timeMultiple", TypeF32, Offset(timeMultiple, ParticleEmitterNodeData));
}
//--------------------------------------------------------------------------
bool ParticleEmitterNodeData::onAdd()
{
if(!Parent::onAdd())
return false;
if (timeMultiple < 0.01 || timeMultiple > 100) {
Con::warnf("ParticleEmitterNodeData::onAdd(%s): timeMultiple must be between 0.01 and 100", getName());
timeMultiple = timeMultiple < 0.01 ? 0.01 : 100;
}
return true;
}
bool ParticleEmitterNodeData::preload(bool server, char errorBuffer[256])
{
if (Parent::preload(server, errorBuffer) == false)
return false;
return true;
}
//--------------------------------------------------------------------------
void ParticleEmitterNodeData::packData(BitStream* stream)
{
Parent::packData(stream);
stream->write(timeMultiple);
}
void ParticleEmitterNodeData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
stream->read(&timeMultiple);
}
//--------------------------------------------------------------------------
//--------------------------------------
//
ParticleEmitterNode::ParticleEmitterNode()
{
// Todo: ScopeAlways?
mNetFlags.set(Ghostable);
mTypeMask |= EnvironmentObjectType;
mEmitterDatablock = NULL;
mEmitterDatablockId = 0;
mEmitter = NULL;
mVelocity = 1.0;
}
ParticleEmitterNode::~ParticleEmitterNode()
{
//
}
//--------------------------------------------------------------------------
void ParticleEmitterNode::initPersistFields()
{
Parent::initPersistFields();
addField("emitter", TypeParticleEmitterDataPtr, Offset(mEmitterDatablock, ParticleEmitterNode));
addField("velocity", TypeF32, Offset(mVelocity, ParticleEmitterNode));
}
//--------------------------------------------------------------------------
bool ParticleEmitterNode::onAdd()
{
if(!Parent::onAdd())
return false;
if (!mEmitterDatablock && mEmitterDatablockId != 0)
{
if (Sim::findObject(mEmitterDatablockId, mEmitterDatablock) == false)
Con::errorf(ConsoleLogEntry::General, "ParticleEmitterNode::onAdd: Invalid packet, bad datablockId(mEmitterDatablock): %d", mEmitterDatablockId);
}
if (mEmitterDatablock == NULL)
return false;
if (isClientObject())
{
ParticleEmitter* pEmitter = new ParticleEmitter;
pEmitter->onNewDataBlock(mEmitterDatablock);
if (pEmitter->registerObject() == false)
{
Con::warnf(ConsoleLogEntry::General, "Could not register base emitter for particle of class: %s", mDataBlock->getName());
delete pEmitter;
return false;
}
mEmitter = pEmitter;
}
mObjBox.min.set(-0.5, -0.5, -0.5);
mObjBox.max.set( 0.5, 0.5, 0.5);
resetWorldBox();
addToScene();
return true;
}
void ParticleEmitterNode::onRemove()
{
removeFromScene();
if (isClientObject())
{
mEmitter->deleteWhenEmpty();
mEmitter = NULL;
}
Parent::onRemove();
}
void ParticleEmitterNode::onGroupAdd()
{
Parent::onGroupAdd();
// To make sure that we don't orphan our emitter on mission end,
// let's add it to our parent group, so it will get cleaned up.
SimGroup* myGroup = getGroup();
if (myGroup && mEmitter)
myGroup->addObject(mEmitter);
}
bool ParticleEmitterNode::onNewDataBlock(GameBaseData* dptr)
{
mDataBlock = dynamic_cast<ParticleEmitterNodeData*>(dptr);
if (!mDataBlock || !Parent::onNewDataBlock(dptr))
return false;
// Todo: Uncomment if this is a "leaf" class
scriptOnNewDataBlock();
return true;
}
//--------------------------------------------------------------------------
void ParticleEmitterNode::advanceTime(F32 dt)
{
Parent::advanceTime(dt);
Point3F emitPoint, emitVelocity;
Point3F emitAxis(0, 0, 1);
getTransform().mulV(emitAxis);
getTransform().getColumn(3, &emitPoint);
emitVelocity = emitAxis * mVelocity;
mEmitter->emitParticles(emitPoint, emitPoint,
emitAxis,
emitVelocity, (U32)(dt * mDataBlock->timeMultiple * 1000.0f));
}
//--------------------------------------------------------------------------
U32 ParticleEmitterNode::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
{
U32 retMask = Parent::packUpdate(con, mask, stream);
mathWrite(*stream, getTransform());
mathWrite(*stream, getScale());
if (stream->writeFlag(mEmitterDatablock != NULL)) {
stream->writeRangedU32(mEmitterDatablock->getId(), DataBlockObjectIdFirst,
DataBlockObjectIdLast);
}
return retMask;
}
void ParticleEmitterNode::unpackUpdate(NetConnection* con, BitStream* stream)
{
Parent::unpackUpdate(con, stream);
MatrixF temp;
Point3F tempScale;
mathRead(*stream, &temp);
mathRead(*stream, &tempScale);
if (stream->readFlag()) {
mEmitterDatablockId = stream->readRangedU32(DataBlockObjectIdFirst,
DataBlockObjectIdLast);
} else {
mEmitterDatablockId = 0;
}
setScale(tempScale);
setTransform(temp);
}
void ParticleEmitterNode::setEmitterDataBlock(ParticleEmitterData* data)
{
if (!data)
return;
if (mEmitter)
{
mEmitterDatablock = data;
ParticleEmitter* pEmitter = new ParticleEmitter;
pEmitter->onNewDataBlock(mEmitterDatablock);
if (pEmitter->registerObject() == false) {
Con::warnf(ConsoleLogEntry::General, "Could not register base emitter for particle of class: %s", mDataBlock->getName());
delete pEmitter;
pEmitter = NULL;
}
if (pEmitter)
{
mEmitter->deleteWhenEmpty();
mEmitter = pEmitter;
}
}
}
ConsoleMethod(ParticleEmitterNode, setEmitterDataBlock, void, 3, 3, "(data)")
{
ParticleEmitterData* data = dynamic_cast<ParticleEmitterData*>(Sim::findObject(dAtoi(argv[2])));
if (!data)
data = dynamic_cast<ParticleEmitterData*>(Sim::findObject(argv[2]));
if (data)
object->setEmitterDataBlock(data);
}

View File

@ -0,0 +1,81 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _PARTICLEEMITTERDUMMY_H_
#define _PARTICLEEMITTERDUMMY_H_
#ifndef _GAMEBASE_H_
#include "game/gameBase.h"
#endif
class ParticleEmitterData;
class ParticleEmitter;
// -------------------------------------------------------------------------
class ParticleEmitterNodeData : public GameBaseData
{
typedef GameBaseData Parent;
protected:
bool onAdd();
//-------------------------------------- Console set variables
public:
F32 timeMultiple;
//-------------------------------------- load set variables
public:
ParticleEmitterNodeData();
~ParticleEmitterNodeData();
void packData(BitStream*);
void unpackData(BitStream*);
bool preload(bool server, char errorBuffer[256]);
DECLARE_CONOBJECT(ParticleEmitterNodeData);
static void initPersistFields();
};
// -------------------------------------------------------------------------
class ParticleEmitterNode : public GameBase
{
typedef GameBase Parent;
private:
ParticleEmitterNodeData* mDataBlock;
protected:
bool onAdd();
void onRemove();
bool onNewDataBlock(GameBaseData *dptr);
ParticleEmitterData* mEmitterDatablock;
S32 mEmitterDatablockId;
ParticleEmitter* mEmitter;
F32 mVelocity;
public:
ParticleEmitterNode();
~ParticleEmitterNode();
// Time/Move Management
public:
void advanceTime(F32 dt);
void setEmitterDataBlock(ParticleEmitterData* data);
DECLARE_CONOBJECT(ParticleEmitterNode);
static void initPersistFields();
U32 packUpdate (NetConnection *conn, U32 mask, BitStream* stream);
void unpackUpdate(NetConnection *conn, BitStream* stream);
virtual void onGroupAdd();
};
#endif // _H_PARTICLEEMISSIONDUMMY

1641
engine/game/fx/particleEngine.cc Executable file

File diff suppressed because it is too large Load Diff

229
engine/game/fx/particleEngine.h Executable file
View File

@ -0,0 +1,229 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _PARTICLEEMITTER_H_
#define _PARTICLEEMITTER_H_
#ifndef _GAMEBASE_H_
#include "game/gameBase.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
//-------------------------------------- Engine initialization...
//
namespace ParticleEngine {
enum ParticleConsts
{
PC_COLOR_KEYS = 4,
PC_SIZE_KEYS = 4,
};
/// Initalize the particle engine
void init();
/// Destroy the particle engine
void destroy();
extern Point3F windVelocity; ///< Global wind velocity for all particles
/// Sets the wind velocity for all particles
/// @param vel Velocity
inline void setWindVelocity(const Point3F & vel) { windVelocity = vel; }
/// Returns the wind velocity
inline Point3F getWindVelocity() { return windVelocity; }
}
//--------------------------------------------------------------------------
//-------------------------------------- The data and the Emitter class
// are all that the game should deal
// with (other than initializing the
// global engine pointer of course)
//
struct Particle;
class ParticleData;
//--------------------------------------
class ParticleEmitterData : public GameBaseData
{
typedef GameBaseData Parent;
public:
ParticleEmitterData();
DECLARE_CONOBJECT(ParticleEmitterData);
static void initPersistFields();
void packData(BitStream* stream);
void unpackData(BitStream* stream);
bool preload(bool server, char errorBuffer[256]);
bool onAdd();
bool loadParameters();
bool reload();
public:
S32 ejectionPeriodMS; ///< Time, in Miliseconds, between particle ejection
S32 periodVarianceMS; ///< Varience in ejection peroid between 0 and n
F32 ejectionVelocity; ///< Ejection velocity
F32 velocityVariance; ///< Variance for velocity between 0 and n
F32 ejectionOffset; ///< Z offset from emitter point to eject from
F32 thetaMin; ///< Minimum angle, from the horizontal plane, to eject from
F32 thetaMax; ///< Maximum angle, from the horizontal plane, to eject from
F32 phiReferenceVel; ///< Reference angle, from the verticle plane, to eject from
F32 phiVariance; ///< Varience from the reference angle, from 0 to n
U32 lifetimeMS; ///< Lifetime of particles
U32 lifetimeVarianceMS; ///< Varience in lifetime from 0 to n
bool overrideAdvance; ///<
bool orientParticles; ///< Particles always face the screen
bool orientOnVelocity; ///< Particles face the screen at the start
bool useEmitterSizes; ///< Use emitter specified sizes instead of datablock sizes
bool useEmitterColors; ///< Use emitter specified colors instead of datablock colors
StringTableEntry particleString; ///< Used to load particle data directly from a string
Vector<ParticleData*> particleDataBlocks; ///< Datablocks for particle emissions
Vector<U32> dataBlockIds; ///< Datablock IDs which corospond to the particleDataBlocks
};
DECLARE_CONSOLETYPE(ParticleEmitterData)
//--------------------------------------
class ParticleEmitter : public GameBase
{
typedef GameBase Parent;
friend class PEngine;
public:
ParticleEmitter();
~ParticleEmitter();
/// Sets sizes of particles based on sizelist provided
/// @param sizeList List of sizes
void setSizes( F32 *sizeList );
/// Sets colors for particles based on color list provided
/// @param colorList List of colors
void setColors( ColorF *colorList );
ParticleEmitterData *getDataBlock(){ return mDataBlock; }
bool onNewDataBlock(GameBaseData* dptr);
/// By default, a particle renderer will wait for it's owner to delete it. When this
/// is turned on, it will delete itself as soon as it's particle count drops to zero.
void deleteWhenEmpty();
/// @name Particle Emission
/// Main interface for creating particles. The emitter does _not_ track changes
/// in axis or velocity over the course of a single update, so this should be called
/// at a fairly fine grain. The emitter will potentially track the last particle
/// to be created into the next call to this function in order to create a uniformly
/// random time distribution of the particles. If the object to which the emitter is
/// attached is in motion, it should try to ensure that for call (n+1) to this
/// function, start is equal to the end from call (n). This will ensure a uniform
/// spatial distribution.
/// @{
void emitParticles(const Point3F& start,
const Point3F& end,
const Point3F& axis,
const Point3F& velocity,
const U32 numMilliseconds);
void emitParticles(const Point3F& point,
const bool useLastPosition,
const Point3F& axis,
const Point3F& velocity,
const U32 numMilliseconds);
void emitParticles(const Point3F& rCenter,
const Point3F& rNormal,
const F32 radius,
const Point3F& velocity,
S32 count);
/// @}
virtual void setTransform(const MatrixF & mat);
protected:
/// @name Internal interface
/// @{
/// Adds a particle
/// @param pos Initial position of particle
/// @param axis
/// @param vel Initial velocity
/// @param axisx
void addParticle(const Point3F &pos, const Point3F &axis, const Point3F &vel, const Point3F &axisx);
/// Renders a particle facing the camera with a spin factor
/// @param part Particle
/// @param basePnts Base points for the quad the particle is rendered on
/// @param camView Camera view matrix
/// @param spinFactor 0.0-1.0 modifyer for
void renderBillboardParticle( Particle &part, Point3F *basePnts, MatrixF &camView, F32 spinFactor );
/// Renders a particle which will face the camera but spin itself to look
/// like it is facing a particular velocity.
/// @param part Particle
/// @param camPos Camera position
void renderOrientedParticle( Particle &part, const Point3F &camPos );
/// Updates the bounding box for the particle system
bool updateBBox(const Point3F &position);
/// @}
protected:
bool onAdd();
void onRemove();
void processTick(const Move *move);
void advanceTime(F32 dt);
// Rendering
protected:
bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState);
void renderObject(SceneState *state, SceneRenderImage *image);
// PEngine interface
private:
/// Removes the provided particle from the system and lets the caller of the
/// function assume control of it
/// @param part Particle
void stealParticle(Particle *part);
private:
ParticleEmitterData* mDataBlock;
/// This is used so we only update our transform/bounding box
/// on ticks, to minimize calls to setTransform.
bool mNeedTransformUpdate;
Particle* mParticleListHead;
U32 mInternalClock;
U32 mNextParticleTime;
Point3F mLastPosition;
bool mHasLastPosition;
bool mDeleteWhenEmpty;
bool mDeleteOnTick;
S32 mLifetimeMS;
S32 mElapsedTimeMS;
F32 sizes[ParticleEngine::PC_SIZE_KEYS];
ColorF colors[ParticleEngine::PC_COLOR_KEYS];
};
#endif // _H_PARTICLEEMITTER

989
engine/game/fx/precipitation.cc Executable file
View File

@ -0,0 +1,989 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "game/fx/precipitation.h"
#include "dgl/dgl.h"
#include "math/mathIO.h"
#include "console/consoleTypes.h"
#include "sceneGraph/sceneGraph.h"
#include "sceneGraph/sceneState.h"
#include "terrain/sky.h"
#include "game/gameConnection.h"
#include "game/player.h"
#include "core/bitStream.h"
#include "platform/profiler.h"
static const U32 dropHitMask = TerrainObjectType |
InteriorObjectType |
WaterObjectType |
StaticShapeObjectType |
StaticTSObjectType;
IMPLEMENT_CO_NETOBJECT_V1(Precipitation);
IMPLEMENT_CO_DATABLOCK_V1(PrecipitationData);
//----------------------------------------------------------
// PrecipitationData
//----------------------------------------------------------
PrecipitationData::PrecipitationData()
{
soundProfile = NULL;
soundProfileId = 0;
mDropName = StringTable->insert("");
mSplashName = StringTable->insert("");
mDropSize = 0.5;
mSplashSize = 0.5;
mUseTrueBillboards = true;
mSplashMS = 250;
}
IMPLEMENT_CONSOLETYPE(PrecipitationData)
IMPLEMENT_GETDATATYPE(PrecipitationData)
IMPLEMENT_SETDATATYPE(PrecipitationData)
void PrecipitationData::initPersistFields()
{
Parent::initPersistFields();
addField("soundProfile", TypeAudioProfilePtr, Offset(soundProfile, PrecipitationData));
addField("dropTexture", TypeFilename, Offset(mDropName, PrecipitationData));
addField("splashTexture", TypeFilename, Offset(mSplashName, PrecipitationData));
addField("dropSize", TypeF32, Offset(mDropSize, PrecipitationData));
addField("splashSize", TypeF32, Offset(mSplashSize, PrecipitationData));
addField("splashMS", TypeS32, Offset(mSplashMS, PrecipitationData));
addField("useTrueBillboards", TypeBool, Offset(mUseTrueBillboards, PrecipitationData));
}
bool PrecipitationData::onAdd()
{
if (Parent::onAdd() == false)
return false;
if (!soundProfile && soundProfileId != 0)
if (Sim::findObject(soundProfileId, soundProfile) == false)
Con::errorf(ConsoleLogEntry::General, "Error, unable to load sound profile for precipitation datablock");
return true;
}
void PrecipitationData::packData(BitStream* stream)
{
Parent::packData(stream);
if (stream->writeFlag(soundProfile != NULL))
stream->writeRangedU32(soundProfile->getId(), DataBlockObjectIdFirst,
DataBlockObjectIdLast);
stream->writeString(mDropName);
stream->writeString(mSplashName);
stream->write(mDropSize);
stream->write(mSplashSize);
stream->write(mSplashMS);
stream->writeFlag(mUseTrueBillboards);
}
void PrecipitationData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
if (stream->readFlag())
soundProfileId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
else
soundProfileId = 0;
mDropName = stream->readSTString();
mSplashName = stream->readSTString();
stream->read(&mDropSize);
stream->read(&mSplashSize);
stream->read(&mSplashMS);
mUseTrueBillboards = stream->readFlag();
}
//----------------------------------------------------------
// Precipitation!
//----------------------------------------------------------
Precipitation::Precipitation()
{
mTypeMask |= ProjectileObjectType;
mNetFlags.set(Ghostable|ScopeAlways);
mDropHead = NULL;
mSplashHead = NULL;
mNumDrops = 5000;
mPercentage = 1.0;
mMinSpeed = 1.5;
mMaxSpeed = 2.0;
mBoxWidth = 200;
mBoxHeight = 100;
mMinMass = 0.75;
mMaxMass = 0.85;
mMaxTurbulence = 0.1;
mTurbulenceSpeed = 0.2;
mUseTurbulence = false;
mRotateWithCamVel = true;
mDoCollision = true;
mStormData.valid = false;
mStormData.startPct = 0;
mStormData.endPct = 0;
mStormData.startTime = 0;
mStormData.totalTime = 0;
mAudioHandle = 0;
mDropHandle = TextureHandle();
mSplashHandle = TextureHandle();
U32 count = 0;
for (U32 v = 0; v < DROPS_PER_SIDE; v++)
{
const F32 y1 = (F32) v / DROPS_PER_SIDE;
const F32 y2 = (F32)(v+1) / DROPS_PER_SIDE;
for (U32 u = 0; u < DROPS_PER_SIDE; u++)
{
const F32 x1 = (F32) u / DROPS_PER_SIDE;
const F32 x2 = (F32)(u+1) / DROPS_PER_SIDE;
texCoords[4*count+0].x = x1;
texCoords[4*count+0].y = y1;
texCoords[4*count+1].x = x2;
texCoords[4*count+1].y = y1;
texCoords[4*count+2].x = x2;
texCoords[4*count+2].y = y2;
texCoords[4*count+3].x = x1;
texCoords[4*count+3].y = y2;
count++;
}
}
count = 0;
for (U32 v = 0; v < FRAMES_PER_SIDE; v++)
{
const F32 y1 = (F32) v / FRAMES_PER_SIDE;
const F32 y2 = (F32)(v+1) / FRAMES_PER_SIDE;
for (U32 u = 0; u < FRAMES_PER_SIDE; u++)
{
const F32 x1 = (F32) u / FRAMES_PER_SIDE;
const F32 x2 = (F32)(u+1) / FRAMES_PER_SIDE;
splashCoords[4*count+0].x = x1;
splashCoords[4*count+0].y = y1;
splashCoords[4*count+1].x = x2;
splashCoords[4*count+1].y = y1;
splashCoords[4*count+2].x = x2;
splashCoords[4*count+2].y = y2;
splashCoords[4*count+3].x = x1;
splashCoords[4*count+3].y = y2;
count++;
}
}
}
Precipitation::~Precipitation()
{
}
void Precipitation::inspectPostApply()
{
if (isClientObject())
fillDropList();
setMaskBits(DataMask);
}
//--------------------------------------------------------------------------
// Console stuff...
//--------------------------------------------------------------------------
void Precipitation::initPersistFields()
{
Parent::initPersistFields();
addGroup("Movement");
addField("minSpeed", TypeF32, Offset(mMinSpeed, Precipitation));
addField("maxSpeed", TypeF32, Offset(mMaxSpeed, Precipitation));
addField("minMass", TypeF32, Offset(mMinMass, Precipitation));
addField("maxMass", TypeF32, Offset(mMaxMass, Precipitation));
endGroup("Movement");
addGroup("Turbulence");
addField("maxTurbulence", TypeF32, Offset(mMaxTurbulence, Precipitation));
addField("turbulenceSpeed", TypeF32, Offset(mTurbulenceSpeed, Precipitation));
addField("rotateWithCamVel", TypeBool, Offset(mRotateWithCamVel, Precipitation));
addField("useTurbulence", TypeBool, Offset(mUseTurbulence, Precipitation));
endGroup("Turbulence");
addField("numDrops", TypeS32, Offset(mNumDrops, Precipitation));
addField("boxWidth", TypeF32, Offset(mBoxWidth, Precipitation));
addField("boxHeight", TypeF32, Offset(mBoxHeight, Precipitation));
addField("doCollision", TypeBool, Offset(mDoCollision, Precipitation));
}
//-----------------------------------
// Console methods...
ConsoleMethod(Precipitation, setPercentange, void, 3, 3, "precipitation.setPercentage(percentage <0.0 to 1.0>)")
{
object->setPercentage(dAtof(argv[2]));
}
ConsoleMethod(Precipitation, modifyStorm, void, 4, 4, "precipitation.modifyStorm(Percentage <0 to 1>, Time<sec>)")
{
object->modifyStorm(dAtof(argv[2]), dAtof(argv[3]) * 1000);
}
//--------------------------------------------------------------------------
// Backend
//--------------------------------------------------------------------------
bool Precipitation::onAdd()
{
if(!Parent::onAdd())
return false;
if (isClientObject())
{
if (mDataBlock->soundProfile)
mAudioHandle = alxPlay(mDataBlock->soundProfile, &getTransform() );
fillDropList();
}
mObjBox.min.set(-1e6, -1e6, -1e6);
mObjBox.max.set( 1e6, 1e6, 1e6);
if (isClientObject())
{
mDropHandle = TextureHandle(((PrecipitationData*)mDataBlock)->mDropName, MeshTexture);
mSplashHandle = TextureHandle(((PrecipitationData*)mDataBlock)->mSplashName, MeshTexture);
}
resetWorldBox();
addToScene();
return true;
}
void Precipitation::onRemove()
{
removeFromScene();
Parent::onRemove();
if (mAudioHandle)
alxStop(mAudioHandle);
mAudioHandle = 0;
if (isClientObject())
killDropList();
}
bool Precipitation::onNewDataBlock(GameBaseData* dptr)
{
mDataBlock = dynamic_cast<PrecipitationData*>(dptr);
if (!mDataBlock || !Parent::onNewDataBlock(dptr))
return false;
if (isClientObject())
{
mDropHandle = TextureHandle(((PrecipitationData*)mDataBlock)->mDropName, MeshTexture);
mSplashHandle = TextureHandle(((PrecipitationData*)mDataBlock)->mSplashName, MeshTexture);
}
scriptOnNewDataBlock();
return true;
}
U32 Precipitation::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
{
Parent::packUpdate(con, mask, stream);
if (stream->writeFlag(mask & DataMask))
{
stream->write(mNumDrops);
stream->write(mMinSpeed);
stream->write(mMaxSpeed);
stream->write(mBoxWidth);
stream->write(mBoxHeight);
stream->write(mMinMass);
stream->write(mMaxMass);
stream->write(mMaxTurbulence);
stream->write(mTurbulenceSpeed);
stream->writeFlag(mUseTurbulence);
stream->writeFlag(mRotateWithCamVel);
stream->writeFlag(mDoCollision);
}
if (stream->writeFlag(mask & PercentageMask))
{
stream->write(mPercentage);
}
if (stream->writeFlag(!(mask & ~(DataMask | PercentageMask | StormMask)) && (mask & StormMask)))
{
stream->write(mStormData.endPct);
stream->write(mStormData.totalTime);
}
return 0;
}
void Precipitation::unpackUpdate(NetConnection* con, BitStream* stream)
{
Parent::unpackUpdate(con, stream);
U32 oldDrops = mNumDrops * mPercentage;
if (stream->readFlag())
{
stream->read(&mNumDrops);
stream->read(&mMinSpeed);
stream->read(&mMaxSpeed);
stream->read(&mBoxWidth);
stream->read(&mBoxHeight);
stream->read(&mMinMass);
stream->read(&mMaxMass);
stream->read(&mMaxTurbulence);
stream->read(&mTurbulenceSpeed);
mUseTurbulence = stream->readFlag();
mRotateWithCamVel = stream->readFlag();
mDoCollision = stream->readFlag();
}
if (stream->readFlag())
{
stream->read(&mPercentage);
}
if (stream->readFlag())
{
F32 pct;
U32 time;
stream->read(&pct);
stream->read(&time);
modifyStorm(pct, time);
}
U32 newDrops = mNumDrops * mPercentage;
if (isClientObject() && oldDrops != newDrops)
fillDropList();
}
//--------------------------------------------------------------------------
// Support functions
//--------------------------------------------------------------------------
VectorF Precipitation::getWindVelocity()
{
Sky* sky = gClientSceneGraph->getCurrentSky();
return (sky && sky->mEffectPrecip) ? -sky->getWindVelocity() : VectorF(0,0,0);
}
void Precipitation::fillDropList()
{
AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
F32 density = Con::getFloatVariable("$pref::precipitationDensity", 1.0f);
U32 newDropCount = (U32)(mNumDrops * mPercentage * density);
U32 dropCount = 0;
if (mDropHead)
{
Raindrop* curr = mDropHead;
while (curr)
{
dropCount++;
curr = curr->next;
if (dropCount == newDropCount && curr)
{
//delete the remaining drops
Raindrop* next = curr->next;
curr->next = NULL;
while (next)
{
Raindrop* last = next;
next = next->next;
last->next = NULL;
destroySplash(last);
delete last;
}
break;
}
}
}
if (dropCount < newDropCount)
{
//move to the end
Raindrop* curr = mDropHead;
if (curr)
{
while (curr->next)
curr = curr->next;
}
else
{
mDropHead = curr = new Raindrop;
spawnNewDrop(curr);
dropCount++;
}
//and add onto it
while (dropCount < newDropCount)
{
curr->next = new Raindrop;
curr = curr->next;
spawnNewDrop(curr);
dropCount++;
}
}
}
void Precipitation::killDropList()
{
AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
Raindrop* curr = mDropHead;
while (curr)
{
Raindrop* next = curr->next;
delete curr;
curr = next;
}
mDropHead = NULL;
}
void Precipitation::spawnDrop(Raindrop *drop)
{
PROFILE_START(PrecipSpawnDrop);
AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
drop->velocity = Platform::getRandom() * (mMaxSpeed - mMinSpeed) + mMinSpeed;
drop->position.x = Platform::getRandom() * mBoxWidth - (mBoxWidth / 2);
drop->position.y = Platform::getRandom() * mBoxWidth - (mBoxWidth / 2);
drop->texCoordIndex = (U32)(Platform::getRandom() * ((F32)DROPS_PER_SIDE*DROPS_PER_SIDE - 0.5));
drop->valid = true;
drop->time = Platform::getRandom() * M_2PI;
drop->mass = Platform::getRandom() * (mMaxMass - mMinMass) + mMinMass;
PROFILE_END();
}
void Precipitation::spawnNewDrop(Raindrop *drop)
{
AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
spawnDrop(drop);
drop->position.z = Platform::getRandom() * mBoxHeight - (mBoxHeight / 2);
}
void Precipitation::findDropCutoff(Raindrop *drop)
{
PROFILE_START(PrecipFindDropCutoff);
AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
if (mDoCollision)
{
VectorF windVel = getWindVelocity();
VectorF velocity = windVel / drop->mass - VectorF(0, 0, drop->velocity);
velocity.normalize();
Point3F end = drop->position + 100 * velocity;
Point3F start = drop->position - 500 * velocity;
RayInfo rInfo;
if (getContainer()->castRay(start, end, dropHitMask, &rInfo))
{
drop->hitPos = rInfo.point;
drop->hitType = rInfo.object->getTypeMask();
}
else
drop->hitPos = Point3F(0,0,-1000);
drop->valid = drop->position.z > drop->hitPos.z;
}
else
{
drop->hitPos = Point3F(0,0,-1000);
drop->valid = true;
}
PROFILE_END();
}
void Precipitation::createSplash(Raindrop *drop)
{
PROFILE_START(PrecipCreateSplash);
if (drop != mSplashHead && !(drop->nextSplashDrop || drop->prevSplashDrop))
{
if (!mSplashHead)
{
mSplashHead = drop;
drop->prevSplashDrop = NULL;
drop->nextSplashDrop = NULL;
}
else
{
mSplashHead->prevSplashDrop = drop;
drop->nextSplashDrop = mSplashHead;
drop->prevSplashDrop = NULL;
mSplashHead = drop;
}
}
drop->animStartTime = Platform::getVirtualMilliseconds();
PROFILE_END();
}
void Precipitation::destroySplash(Raindrop *drop)
{
PROFILE_START(PrecipDestroySplash);
if (drop == mSplashHead)
{
mSplashHead = NULL;
}
else
{
// Unlink.
if (drop->nextSplashDrop)
drop->nextSplashDrop->prevSplashDrop = drop->prevSplashDrop;
if (drop->prevSplashDrop)
drop->prevSplashDrop->nextSplashDrop = drop->nextSplashDrop;
drop->nextSplashDrop = NULL;
drop->prevSplashDrop = NULL;
}
PROFILE_END();
}
//--------------------------------------------------------------------------
// Processing
//--------------------------------------------------------------------------
void Precipitation::setPercentage(F32 pct)
{
mPercentage = pct;
if (isServerObject())
setMaskBits(PercentageMask);
}
void Precipitation::modifyStorm(F32 pct, U32 ms)
{
pct = mClampF(pct, 0, 1);
mStormData.endPct = pct;
mStormData.totalTime = ms;
if (isServerObject())
{
setMaskBits(StormMask);
return;
}
mStormData.startTime = Platform::getVirtualMilliseconds();
mStormData.startPct = mPercentage;
mStormData.valid = true;
}
void Precipitation::interpolateTick(F32 delta)
{
PROFILE_START(PrecipInterpolate);
AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!");
Raindrop* curr = mDropHead;
VectorF windVel = getWindVelocity();
F32 dt = 1-delta;
while (curr)
{
if (!curr->toRender)
{
curr = curr->next;
continue;
}
VectorF turbulence = dt * windVel;
if (mUseTurbulence)
{
F32 renderTime = curr->time + dt * mTurbulenceSpeed;
turbulence += VectorF(mSin(renderTime), mCos(renderTime), 0) * mMaxTurbulence;
}
curr->renderPosition = curr->position + turbulence / curr->mass;
curr->renderPosition.z -= dt * curr->velocity;
curr = curr->next;
}
PROFILE_END();
}
void Precipitation::processTick(const Move *)
{
//nothing to do on the server
if (isServerObject())
return;
//we need to update positions and do some collision here
GameConnection* conn = GameConnection::getConnectionToServer();
if (!conn)
return; //need connection to server
ShapeBase* camObj = conn->getCameraObject();
if (!camObj)
return;
PROFILE_START(PrecipProcess);
//update the storm if necessary
if (mStormData.valid)
{
F32 t = (Platform::getVirtualMilliseconds() - mStormData.startTime) / (F32)mStormData.totalTime;
if (t >= 1)
{
mPercentage = mStormData.endPct;
mStormData.valid = false;
}
else
mPercentage = mStormData.startPct * (1-t) + mStormData.endPct * t;
fillDropList();
}
MatrixF camMat;
camObj->getEyeTransform(&camMat);
Point3F camPos, camDir;
camMat.getColumn(3, &camPos);
camMat.getColumn(1, &camDir);
camDir.normalize();
F32 fovDot = camObj->getCameraFov() / 180;
Raindrop* curr = mDropHead;
//make a box
Box3F box(camPos.x - mBoxWidth / 2, camPos.y - mBoxWidth / 2, camPos.z - mBoxHeight / 2,
camPos.x + mBoxWidth / 2, camPos.y + mBoxWidth / 2, camPos.z + mBoxHeight / 2);
//offset the renderbox in the direction of the camera direction
//in order to have more of the drops actually rendered
box.min.x += camDir.x * mBoxWidth / 4;
box.max.x += camDir.x * mBoxWidth / 4;
box.min.y += camDir.y * mBoxWidth / 4;
box.max.y += camDir.y * mBoxWidth / 4;
box.min.z += camDir.z * mBoxHeight / 4;
box.max.z += camDir.z * mBoxHeight / 4;
VectorF windVel = getWindVelocity();
while (curr)
{
//update position
if (mUseTurbulence)
curr->time += mTurbulenceSpeed;
curr->position += windVel / curr->mass;
curr->position.z -= curr->velocity;
//wrap position
wrapDrop(curr, box);
if (curr->valid)
{
if (curr->position.z < curr->hitPos.z)
{
curr->valid = false;
// Bump back so we don't spawn splashes where they ought not
// be. It might be better to revisit this and use the hitPos.
curr->position -= windVel / curr->mass;
curr->position.z += curr->velocity;
//do some funky effect thingy for hitting something
if (mSplashHandle.getGLName() != 0)
createSplash(curr);
}
}
//render test
VectorF lookVec = curr->position - camPos;
curr->toRender = curr->valid ? mDot(lookVec, camDir) > fovDot : false;
curr = curr->next;
}
//update splashes
curr = mSplashHead;
U32 currTime = Platform::getVirtualMilliseconds();
while (curr)
{
F32 pct = (F32)(currTime - curr->animStartTime) / mDataBlock->mSplashMS;
if (pct >= 1.0f)
{
Raindrop *next = curr->nextSplashDrop;
destroySplash(curr);
curr = next;
continue;
}
curr->splashIndex = (U32)((FRAMES_PER_SIDE*FRAMES_PER_SIDE) * pct);
curr = curr->nextSplashDrop;
}
PROFILE_END();
}
//--------------------------------------------------------------------------
// Rendering
//--------------------------------------------------------------------------
bool Precipitation::prepRenderImage(SceneState* state, const U32 stateKey,
const U32 /*startZone*/, const bool /*modifyBaseState*/)
{
if (isLastState(state, stateKey))
return false;
setLastState(state, stateKey);
// This should be sufficient for most objects that don't manage zones, and
// don't need to return a specialized RenderImage...
if (state->isObjectRendered(this)) {
SceneRenderImage* image = new SceneRenderImage;
image->obj = this;
image->isTranslucent = true;
image->sortType = SceneRenderImage::EndSort;
state->insertRenderImage(image);
}
if (!mAudioHandle && mDataBlock->soundProfile)
mAudioHandle = alxPlay(mDataBlock->soundProfile, &getTransform());
return false;
}
void Precipitation::renderObject(SceneState* state, SceneRenderImage*)
{
PROFILE_START(PrecipRender);
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
RectI viewport;
glMatrixMode(GL_PROJECTION);
glPushMatrix();
dglGetViewport(&viewport);
// Uncomment this if this is a "simple" (non-zone managing) object
state->setupObjectProjection(this);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
renderPrecip(state);
renderSplashes(state);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
dglSetViewport(viewport);
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
PROFILE_END();
}
//--------------------------------------------------------------------------
void Precipitation::renderPrecip(SceneState *state)
{
GameConnection* conn = GameConnection::getConnectionToServer();
if (!conn)
return; //need connection to server
ShapeBase* camObj = conn->getCameraObject();
if (!camObj)
return;
PROFILE_START(PrecipRenderPrecip);
Point3F camPos = state->getCameraPosition();
VectorF camVel = camObj->getVelocity();
Raindrop *curr = mDropHead;
glEnable(GL_BLEND);
glDepthMask(GL_FALSE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, mDropHandle.getGLName());
static Point3F verts[4];
glVertexPointer(3, GL_FLOAT, 0, verts);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glColor3f(1.0, 1.0, 1.0);
VectorF windVel = getWindVelocity();
while (curr)
{
if (!curr->toRender)
{
curr = curr->next;
continue;
}
Point3F pos = curr->renderPosition;
VectorF orthoDir = (camPos - pos);
VectorF velocity = windVel / curr->mass;
if (mRotateWithCamVel && camVel != VectorF(0,0,0))
{
F32 distance = orthoDir.len();
velocity -= camVel / (distance > 2 ? distance : 2) * 0.3;
}
velocity.z -= curr->velocity;
velocity.normalize();
orthoDir.normalize();
VectorF right;
VectorF up;
// two forms of billboards - true billboards (1st codeblock)
// or axis-aligned with velocity (2nd codeblock)
// the axis-aligned billboards are aligned with the velocity
// of the raindrop, and tilted slightly towards the camera
if (mDataBlock->mUseTrueBillboards)
{
state->mModelview.getRow(0,&right);
state->mModelview.getRow(2,&up);
right.normalize();
up.normalize();
}
else
{
right = mCross(-velocity, orthoDir);
right.normalize();
up = mCross(orthoDir, right) * 0.5 - velocity * 0.5;
up.normalize();
}
right *= mDataBlock->mDropSize;
up *= mDataBlock->mDropSize;
verts[0] = pos + right + up;
verts[1] = pos - right + up;
verts[2] = pos - right - up;
verts[3] = pos + right - up;
glTexCoordPointer(2, GL_FLOAT, 0, &texCoords[4*curr->texCoordIndex]);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
//debug collision render
//if (curr->cutoffHeight != -1000)
//{
// VectorF windVel = getWindVelocity();
// VectorF velocity = windVel / curr->mass - VectorF(0, 0, curr->velocity);
// velocity.normalize();
// Point3F start = curr->position;// - 10000 * velocity;
// F32 height = start.z - curr->cutoffHeight;
// F32 t = height / velocity.z;
// Point3F end = start - t * velocity;
// glDisable(GL_TEXTURE_2D);
// glDisable(GL_BLEND);
// glBegin(GL_LINES);
// glColor3f(1.0, 0.0, 0.0);
// glVertex3fv(&(start.x));
// glColor3f(0.0, 1.0, 0.0);
// glVertex3fv(&(end.x));
// glEnd();
// glBegin(GL_TRIANGLE_FAN);
// glColor3f(0.0,0.0,1.0);
// glVertex3fv(&((end - right + up).x));
// glVertex3fv(&((end + right + up).x));
// glVertex3fv(&((end + right - up).x));
// glVertex3fv(&((end - right - up).x));
// glEnd();
// glEnable(GL_TEXTURE_2D);
// glEnable(GL_BLEND);
//}
//end debug collision render
curr = curr->next;
}
glDisable(GL_TEXTURE_2D);
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
PROFILE_END();
}
void Precipitation::renderSplashes(SceneState *state)
{
PROFILE_START(PrecipRenderSplash);
//setup the billboard
VectorF right, up;
state->mModelview.getRow(0, &right);
state->mModelview.getRow(2, &up);
right.normalize();
up.normalize();
right *= mDataBlock->mSplashSize;
up *= mDataBlock->mSplashSize;
glColor3f(1.0, 1.0, 1.0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, mSplashHandle.getGLName());
glEnable(GL_BLEND);
glDepthMask(GL_FALSE);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
static Point3F verts[4];
glVertexPointer(3, GL_FLOAT, 0, verts);
Raindrop *curr = mSplashHead;
while (curr)
{
verts[0] = curr->hitPos + right + up;
verts[1] = curr->hitPos - right + up;
verts[2] = curr->hitPos - right - up;
verts[3] = curr->hitPos + right - up;
glTexCoordPointer(2, GL_FLOAT, 0, &splashCoords[4*curr->splashIndex]);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
curr = curr->nextSplashDrop;
}
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
glDisable(GL_TEXTURE_2D);
PROFILE_END();
}

237
engine/game/fx/precipitation.h Executable file
View File

@ -0,0 +1,237 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _PRECIPITATION_H_
#define _PRECIPITATION_H_
#include "game/gameBase.h"
#include "audio/audioDataBlock.h"
/// How many drops are on a side of the material texture
#define DROPS_PER_SIDE 4
/// How many frames are on a side of a splash animation
#define FRAMES_PER_SIDE 2
//--------------------------------------------------------------------------
/// Precipitation datablock.
class PrecipitationData : public GameBaseData {
typedef GameBaseData Parent;
public:
AudioProfile* soundProfile;
S32 soundProfileId; ///< Ambient sound
StringTableEntry mDropName; ///< Texture filename for raindrop
StringTableEntry mSplashName; ///< Texture filename for splash
F32 mDropSize; ///< Droplet billboard size
F32 mSplashSize; ///< Splash billboard size
bool mUseTrueBillboards; ///< True to use true billboards, false for axis-aligned billboards
S32 mSplashMS; ///< How long in milliseconds a splash will last
PrecipitationData();
DECLARE_CONOBJECT(PrecipitationData);
bool onAdd();
static void initPersistFields();
virtual void packData(BitStream* stream);
virtual void unpackData(BitStream* stream);
};
DECLARE_CONSOLETYPE(PrecipitationData)
struct Raindrop
{
F32 velocity; ///< How fast the drop is falling downwards
Point3F position; ///< Position of the drop
Point3F renderPosition; ///< Interpolated render-position of the drop
F32 time; ///< Time into the turbulence function
F32 mass; ///< Mass of drop used for how much turbulence/wind effects the drop
U32 texCoordIndex; ///< Which piece of the material will be used
bool toRender; ///< Don't want to render all drops, just the ones that pass a few tests
bool valid; ///< Drop becomes invalid after hitting something. Just keep updating
///< the position of it, but don't render until it hits the bottom
///< of the renderbox and respawns
Point3F hitPos; ///< Point at which the drop will collide with something
Raindrop *nextSplashDrop; ///< Linked list cruft for easily adding/removing stuff from the splash list
Raindrop *prevSplashDrop; ///< Same as next but previous!
SimTime animStartTime; ///< Animation time tracker
U32 splashIndex; ///< Texture index for which frame of the splash to render
U32 hitType; ///< What kind of object the drop will hit
Raindrop* next; ///< linked list cruft
Raindrop()
{
velocity = 0;
time = 0;
mass = 1;
texCoordIndex = 0;
next = NULL;
toRender = false;
valid = true;
nextSplashDrop = NULL;
prevSplashDrop = NULL;
animStartTime = 0;
splashIndex = 0;
hitType = 0;
hitPos = Point3F(0,0,0);
}
};
//--------------------------------------------------------------------------
class Precipitation : public GameBase
{
private:
typedef GameBase Parent;
PrecipitationData* mDataBlock;
Raindrop *mDropHead; ///< Drop linked list head
Raindrop *mSplashHead; ///< Splash linked list head
Point2F texCoords[4 * DROPS_PER_SIDE*DROPS_PER_SIDE]; ///< texture coords for rain texture
Point2F splashCoords[4 * FRAMES_PER_SIDE*FRAMES_PER_SIDE]; ///< texture coordinates for splash texture
AUDIOHANDLE mAudioHandle; ///< Ambient sound handle
TextureHandle mDropHandle; ///< Texture handle for raindrop
TextureHandle mSplashHandle; ///< Texture handle for splash
//console exposed variables
S32 mNumDrops; ///< Number of drops in the scene
F32 mPercentage; ///< Server-side set var (NOT exposed to console)
///< which controls how many drops are present [0,1]
F32 mMinSpeed; ///< Minimum downward speed of drops
F32 mMaxSpeed; ///< Maximum downward speed of drops
F32 mMinMass; ///< Minimum mass of drops
F32 mMaxMass; ///< Maximum mass of drops
F32 mBoxWidth; ///< How far away in the x and y directions drops will render
F32 mBoxHeight; ///< How high drops will render
F32 mMaxTurbulence; ///< Coefficient to sin/cos for adding turbulence
F32 mTurbulenceSpeed; ///< How fast the turbulence wraps in a circle
bool mUseTurbulence; ///< Whether to use turbulence or not (MAY EFFECT PERFORMANCE)
bool mRotateWithCamVel; ///< Rotate the drops relative to the camera velocity
///< This is useful for "streak" type drops
bool mDoCollision; ///< Whether or not to do collision
struct
{
bool valid;
U32 startTime;
U32 totalTime;
F32 startPct;
F32 endPct;
} mStormData;
//other functions...
void processTick(const Move*);
void interpolateTick(F32 delta);
VectorF getWindVelocity();
void fillDropList(); ///< Adds/removes drops from the list to have the right # of drops
void killDropList(); ///< Deletes the entire drop list
void spawnDrop(Raindrop *drop); ///< Fills drop info with random velocity, x/y positions, and mass
void spawnNewDrop(Raindrop *drop); ///< Same as spawnDrop except also does z position
void findDropCutoff(Raindrop *drop); ///< Casts a ray to see if/when a drop will collide
inline void wrapDrop(Raindrop *drop, Box3F &box); ///< Wraps a drop within the specified box
void createSplash(Raindrop *drop); ///< Adds a drop to the splash list
void destroySplash(Raindrop *drop); ///< Removes a drop from the splash list
protected:
bool onAdd();
void onRemove();
// Rendering
bool prepRenderImage(SceneState*, const U32, const U32, const bool);
void renderObject(SceneState*, SceneRenderImage*);
void renderPrecip(SceneState *state);
void renderSplashes(SceneState *state);
public:
Precipitation();
~Precipitation();
void inspectPostApply();
enum
{
DataMask = Parent::NextFreeMask << 0,
PercentageMask = Parent::NextFreeMask << 1,
StormMask = Parent::NextFreeMask << 2,
NextFreeMask = Parent::NextFreeMask << 3
};
bool onNewDataBlock(GameBaseData* dptr);
DECLARE_CONOBJECT(Precipitation);
static void initPersistFields();
U32 packUpdate(NetConnection*, U32 mask, BitStream* stream);
void unpackUpdate(NetConnection*, BitStream* stream);
void setPercentage(F32 pct);
void modifyStorm(F32 pct, U32 ms);
};
inline void Precipitation::wrapDrop(Raindrop *drop, Box3F &box)
{
bool recalcCutoff = false;
//could probably be slightly optimized to get rid of the while loops
if (drop->position.x < box.min.x)
{
while (drop->position.x < box.min.x)
drop->position.x += mBoxWidth;
recalcCutoff = true;
}
else if (drop->position.x > box.max.x)
{
while (drop->position.x > box.max.x)
drop->position.x -= mBoxWidth;
recalcCutoff = true;
}
if (drop->position.y < box.min.y)
{
while (drop->position.y < box.min.y)
drop->position.y += mBoxWidth;
recalcCutoff = true;
}
else if (drop->position.y > box.max.y)
{
while (drop->position.y > box.max.y)
drop->position.y -= mBoxWidth;
recalcCutoff = true;
}
if (drop->position.z < box.min.z)
{
spawnDrop(drop);
drop->position.x += box.min.x;
drop->position.y += box.min.y;
while (drop->position.z < box.min.z)
drop->position.z += mBoxHeight;
recalcCutoff = true;
}
else if (drop->position.z > box.max.z)
{
while (drop->position.z > box.max.z)
drop->position.z -= mBoxHeight;
recalcCutoff = true;
}
if(recalcCutoff)
findDropCutoff(drop);
}
#endif // PRECIPITATION_H_

818
engine/game/fx/splash.cc Executable file
View File

@ -0,0 +1,818 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "game/fx/splash.h"
#include "console/consoleTypes.h"
#include "dgl/dgl.h"
#include "platform/platformAudio.h"
#include "audio/audioDataBlock.h"
#include "sceneGraph/sceneGraph.h"
#include "sceneGraph/sceneState.h"
#include "core/bitStream.h"
#include "math/mathIO.h"
#include "terrain/terrData.h"
#include "game/fx/explosion.h"
#include "game/fx/particleEngine.h"
#include "sim/netConnection.h"
namespace
{
MRandomLCG sgRandom(0xdeadbeef);
} // namespace {}
//----------------------------------------------------------------------------
IMPLEMENT_CO_DATABLOCK_V1(SplashData);
IMPLEMENT_CO_NETOBJECT_V1(Splash);
//--------------------------------------------------------------------------
// Splash Data
//--------------------------------------------------------------------------
SplashData::SplashData()
{
soundProfile = NULL;
soundProfileId = 0;
scale.set(1, 1, 1);
dMemset( emitterList, 0, sizeof( emitterList ) );
dMemset( emitterIDList, 0, sizeof( emitterIDList ) );
delayMS = 0;
delayVariance = 0;
lifetimeMS = 1000;
lifetimeVariance = 0;
width = 4.0;
numSegments = 10;
velocity = 5.0;
height = 0.0;
acceleration = 0.0;
texWrap = 1.0;
texFactor = 3.0;
ejectionFreq = 5;
ejectionAngle = 45.0;
ringLifetime = 1.0;
startRadius = 0.5;
explosion = NULL;
explosionId = 0;
dMemset( textureName, 0, sizeof( textureName ) );
U32 i;
for( i=0; i<NUM_TIME_KEYS; i++ )
times[i] = 1.0;
times[0] = 0.0;
for( i=0; i<NUM_TIME_KEYS; i++ )
colors[i].set( 1.0, 1.0, 1.0, 1.0 );
}
IMPLEMENT_CONSOLETYPE(SplashData)
IMPLEMENT_SETDATATYPE(SplashData)
IMPLEMENT_GETDATATYPE(SplashData)
//--------------------------------------------------------------------------
// Init fields
//--------------------------------------------------------------------------
void SplashData::initPersistFields()
{
Parent::initPersistFields();
addField("soundProfile", TypeAudioProfilePtr, Offset(soundProfile, SplashData));
addField("scale", TypePoint3F, Offset(scale, SplashData));
addField("emitter", TypeParticleEmitterDataPtr, Offset(emitterList, SplashData), NUM_EMITTERS);
addField("delayMS", TypeS32, Offset(delayMS, SplashData));
addField("delayVariance", TypeS32, Offset(delayVariance, SplashData));
addField("lifetimeMS", TypeS32, Offset(lifetimeMS, SplashData));
addField("lifetimeVariance", TypeS32, Offset(lifetimeVariance, SplashData));
addField("width", TypeF32, Offset(width, SplashData));
addField("numSegments", TypeS32, Offset(numSegments, SplashData));
addField("velocity", TypeF32, Offset(velocity, SplashData));
addField("height", TypeF32, Offset(height, SplashData));
addField("acceleration", TypeF32, Offset(acceleration, SplashData));
addField("times", TypeF32, Offset(times, SplashData), NUM_TIME_KEYS);
addField("colors", TypeColorF, Offset(colors, SplashData), NUM_TIME_KEYS);
addField("texture", TypeFilename, Offset(textureName, SplashData), NUM_TEX);
addField("texWrap", TypeF32, Offset(texWrap, SplashData));
addField("texFactor", TypeF32, Offset(texFactor, SplashData));
addField("ejectionFreq", TypeF32, Offset(ejectionFreq, SplashData));
addField("ejectionAngle", TypeF32, Offset(ejectionAngle, SplashData));
addField("ringLifetime", TypeF32, Offset(ringLifetime, SplashData));
addField("startRadius", TypeF32, Offset(startRadius, SplashData));
addField("explosion", TypeExplosionDataPtr, Offset(explosion, SplashData));
}
//--------------------------------------------------------------------------
// On add - verify data settings
//--------------------------------------------------------------------------
bool SplashData::onAdd()
{
if (Parent::onAdd() == false)
return false;
return true;
}
//--------------------------------------------------------------------------
// Pack data
//--------------------------------------------------------------------------
void SplashData::packData(BitStream* stream)
{
Parent::packData(stream);
mathWrite(*stream, scale);
stream->write(delayMS);
stream->write(delayVariance);
stream->write(lifetimeMS);
stream->write(lifetimeVariance);
stream->write(width);
stream->write(numSegments);
stream->write(velocity);
stream->write(height);
stream->write(acceleration);
stream->write(texWrap);
stream->write(texFactor);
stream->write(ejectionFreq);
stream->write(ejectionAngle);
stream->write(ringLifetime);
stream->write(startRadius);
if( stream->writeFlag( explosion ) )
{
stream->writeRangedU32(explosion->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast);
}
S32 i;
for( i=0; i<NUM_EMITTERS; i++ )
{
if( stream->writeFlag( emitterList[i] != NULL ) )
{
stream->writeRangedU32( emitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast );
}
}
for( i=0; i<NUM_TIME_KEYS; i++ )
{
stream->write( colors[i] );
}
for( i=0; i<NUM_TIME_KEYS; i++ )
{
stream->write( times[i] );
}
for( i=0; i<NUM_TEX; i++ )
{
stream->writeString(textureName[i]);
}
}
//--------------------------------------------------------------------------
// Unpack data
//--------------------------------------------------------------------------
void SplashData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
mathRead(*stream, &scale);
stream->read(&delayMS);
stream->read(&delayVariance);
stream->read(&lifetimeMS);
stream->read(&lifetimeVariance);
stream->read(&width);
stream->read(&numSegments);
stream->read(&velocity);
stream->read(&height);
stream->read(&acceleration);
stream->read(&texWrap);
stream->read(&texFactor);
stream->read(&ejectionFreq);
stream->read(&ejectionAngle);
stream->read(&ringLifetime);
stream->read(&startRadius);
if( stream->readFlag() )
{
explosionId = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
}
U32 i;
for( i=0; i<NUM_EMITTERS; i++ )
{
if( stream->readFlag() )
{
emitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast );
}
}
for( i=0; i<NUM_TIME_KEYS; i++ )
{
stream->read( &colors[i] );
}
for( i=0; i<NUM_TIME_KEYS; i++ )
{
stream->read( &times[i] );
}
for( i=0; i<NUM_TEX; i++ )
{
textureName[i] = stream->readSTString();
}
}
//--------------------------------------------------------------------------
// Preload data - load resources
//--------------------------------------------------------------------------
bool SplashData::preload(bool server, char errorBuffer[256])
{
if (Parent::preload(server, errorBuffer) == false)
return false;
if (!server)
{
S32 i;
for( i=0; i<NUM_EMITTERS; i++ )
{
if( !emitterList[i] && emitterIDList[i] != 0 )
{
if( Sim::findObject( emitterIDList[i], emitterList[i] ) == false)
{
Con::errorf( ConsoleLogEntry::General, "SplashData::onAdd: Invalid packet, bad datablockId(particle emitter): 0x%x", emitterIDList[i] );
}
}
}
for( i=0; i<NUM_TEX; i++ )
{
if (textureName[i] && textureName[i][0])
{
textureHandle[i] = TextureHandle(textureName[i], MeshTexture );
}
}
}
if( !explosion && explosionId != 0 )
{
if( !Sim::findObject(explosionId, explosion) )
{
Con::errorf(ConsoleLogEntry::General, "SplashData::preload: Invalid packet, bad datablockId(explosion): %d", explosionId);
}
}
return true;
}
//--------------------------------------------------------------------------
// Splash
//--------------------------------------------------------------------------
Splash::Splash()
{
dMemset( mEmitterList, 0, sizeof( mEmitterList ) );
mDelayMS = 0;
mCurrMS = 0;
mEndingMS = 1000;
mActive = false;
mRadius = 0.0;
mVelocity = 1.0;
mHeight = 0.0;
mTimeSinceLastRing = 0.0;
mDead = false;
mElapsedTime = 0.0;
mInitialNormal.set( 0.0, 0.0, 1.0 );
}
//--------------------------------------------------------------------------
// Destructor
//--------------------------------------------------------------------------
Splash::~Splash()
{
}
//--------------------------------------------------------------------------
// Set initial state
//--------------------------------------------------------------------------
void Splash::setInitialState(const Point3F& point, const Point3F& normal, const F32 fade)
{
mInitialPosition = point;
mInitialNormal = normal;
mFade = fade;
mFog = 0.0f;
}
//--------------------------------------------------------------------------
// OnAdd
//--------------------------------------------------------------------------
bool Splash::onAdd()
{
// first check if we have a server connection, if we dont then this is on the server
// and we should exit, then check if the parent fails to add the object
NetConnection* conn = NetConnection::getConnectionToServer();
if(!conn || !Parent::onAdd())
return false;
mDelayMS = mDataBlock->delayMS + sgRandom.randI( -mDataBlock->delayVariance, mDataBlock->delayVariance );
mEndingMS = mDataBlock->lifetimeMS + sgRandom.randI( -mDataBlock->lifetimeVariance, mDataBlock->lifetimeVariance );
mVelocity = mDataBlock->velocity;
mHeight = mDataBlock->height;
mTimeSinceLastRing = 1.0 / mDataBlock->ejectionFreq;
if( isClientObject() )
{
for( U32 i=0; i<SplashData::NUM_EMITTERS; i++ )
{
if( mDataBlock->emitterList[i] != NULL )
{
ParticleEmitter * pEmitter = new ParticleEmitter;
pEmitter->onNewDataBlock( mDataBlock->emitterList[i] );
if( !pEmitter->registerObject() )
{
Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() );
delete pEmitter;
pEmitter = NULL;
}
mEmitterList[i] = pEmitter;
}
}
spawnExplosion();
}
mObjBox.min = Point3F( -1, -1, -1 );
mObjBox.max = Point3F( 1, 1, 1 );
resetWorldBox();
gClientContainer.addObject(this);
gClientSceneGraph->addObjectToScene(this);
removeFromProcessList();
gClientProcessList.addObject(this);
conn->addObject(this);
return true;
}
//--------------------------------------------------------------------------
// OnRemove
//--------------------------------------------------------------------------
void Splash::onRemove()
{
for( U32 i=0; i<SplashData::NUM_EMITTERS; i++ )
{
if( mEmitterList[i] )
{
mEmitterList[i]->deleteWhenEmpty();
mEmitterList[i] = NULL;
}
}
ringList.free();
mSceneManager->removeObjectFromScene(this);
getContainer()->removeObject(this);
Parent::onRemove();
}
//--------------------------------------------------------------------------
// On New Data Block
//--------------------------------------------------------------------------
bool Splash::onNewDataBlock(GameBaseData* dptr)
{
mDataBlock = dynamic_cast<SplashData*>(dptr);
if (!mDataBlock || !Parent::onNewDataBlock(dptr))
return false;
scriptOnNewDataBlock();
return true;
}
//--------------------------------------------------------------------------
// Prep render image
//--------------------------------------------------------------------------
bool Splash::prepRenderImage(SceneState* state, const U32 stateKey,
const U32 /*startZone*/, const bool /*modifyBaseState*/)
{
if (isLastState(state, stateKey))
return false;
setLastState(state, stateKey);
// This should be sufficient for most objects that don't manage zones, and
// don't need to return a specialized RenderImage...
if (state->isObjectRendered(this))
{
mFog = 0.0;
SceneRenderImage* image = new SceneRenderImage;
image->obj = this;
image->isTranslucent = true;
image->sortType = SceneRenderImage::Point;
image->textureSortKey = (U32)(dsize_t)mDataBlock;
state->setImageRefPoint(this, image);
state->insertRenderImage(image);
}
return false;
}
//--------------------------------------------------------------------------
// Render
//--------------------------------------------------------------------------
void Splash::renderObject(SceneState* state, SceneRenderImage*)
{
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
RectI viewport;
glMatrixMode(GL_PROJECTION);
glPushMatrix();
dglGetViewport(&viewport);
state->setupObjectProjection(this);
render();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
dglSetViewport(viewport);
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
}
//--------------------------------------------------------------------------
// Process tick
//--------------------------------------------------------------------------
void Splash::processTick(const Move*)
{
mCurrMS += TickMs;
if( isServerObject() )
{
if( mCurrMS >= mEndingMS )
{
mDead = true;
if( mCurrMS >= (mEndingMS + mDataBlock->ringLifetime * 1000) )
{
deleteObject();
}
}
}
else
{
if( mCurrMS >= mEndingMS )
{
mDead = true;
}
}
}
//--------------------------------------------------------------------------
// Advance time
//--------------------------------------------------------------------------
void Splash::advanceTime(F32 dt)
{
if (dt == 0.0)
return;
mElapsedTime += dt;
updateColor();
updateWave( dt );
updateEmitters( dt );
updateRings( dt );
if( !mDead )
{
emitRings( dt );
}
}
//----------------------------------------------------------------------------
// Update emitters
//----------------------------------------------------------------------------
void Splash::updateEmitters( F32 dt )
{
Point3F pos = getPosition();
for( U32 i=0; i<SplashData::NUM_EMITTERS; i++ )
{
if( mEmitterList[i] )
{
mEmitterList[i]->emitParticles( pos, pos, mInitialNormal, Point3F( 0.0, 0.0, 0.0 ), (S32) (dt * 1000) );
}
}
}
//----------------------------------------------------------------------------
// Update wave
//----------------------------------------------------------------------------
void Splash::updateWave( F32 dt )
{
mVelocity += mDataBlock->acceleration * dt;
mRadius += mVelocity * dt;
}
//----------------------------------------------------------------------------
// Render splash
//----------------------------------------------------------------------------
void Splash::render()
{
glDisable(GL_CULL_FACE);
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glEnable(GL_TEXTURE_2D);
SplashRing *ring = NULL;
U32 i=0;
while( bool( ring = ringList.next( ring ) ) )
{
SplashRing *next = ringList.next( ring );
if( !next )
break;
renderSegment( *ring, *next );
i++;
}
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
//----------------------------------------------------------------------------
// Render horizontal segment created from 2 sets of points that are the
// top and bottom rings of the segment.
//----------------------------------------------------------------------------
void Splash::renderSegment( SplashRing &top, SplashRing &bottom )
{
F32 texFactor = mDataBlock->texWrap;
glBindTexture( GL_TEXTURE_2D, mDataBlock->textureHandle[0].getGLName() );
F32 topAlpha = top.elapsedTime / top.lifetime;
F32 bottomAlpha = bottom.elapsedTime / bottom.lifetime;
if( topAlpha < 0.5 )
topAlpha *= 2.0;
else
topAlpha = 1.0 - topAlpha;
if( bottomAlpha < 0.5 )
bottomAlpha *= 2.0;
else
bottomAlpha = 1.0 - bottomAlpha;
top.color.alpha = topAlpha;
bottom.color.alpha = bottomAlpha;
glBegin( GL_QUAD_STRIP );
{
for( U32 i=0; i<top.points.size(); i++ )
{
F32 t = F32(i) / F32(top.points.size());
glTexCoord2f( t * texFactor, top.v );
glColor4fv( top.color );
glVertex3fv( top.points[i].position );
glTexCoord2f( t * texFactor, bottom.v );
glColor4fv( bottom.color );
glVertex3fv( bottom.points[i].position );
}
glTexCoord2f( 1.0 * texFactor, top.v );
glColor4fv( top.color );
glVertex3fv( top.points[0].position );
glTexCoord2f( 1.0 * texFactor, bottom.v );
glColor4fv( bottom.color );
glVertex3fv( bottom.points[0].position );
}
glEnd();
}
//----------------------------------------------------------------------------
// Update color
//----------------------------------------------------------------------------
void Splash::updateColor()
{
SplashRing *ring = NULL;
while( bool( ring = ringList.next( ring ) ) )
{
F32 t = F32(ring->elapsedTime) / F32(ring->lifetime);
for( U32 i = 1; i < SplashData::NUM_TIME_KEYS; i++ )
{
if( mDataBlock->times[i] >= t )
{
F32 firstPart = t - mDataBlock->times[i-1];
F32 total = (mDataBlock->times[i] -
mDataBlock->times[i-1]);
firstPart /= total;
ring->color.interpolate( mDataBlock->colors[i-1],
mDataBlock->colors[i],
firstPart);
break;
}
}
}
}
//----------------------------------------------------------------------------
// Create ring
//----------------------------------------------------------------------------
SplashRing Splash::createRing()
{
SplashRing ring;
U32 numPoints = mDataBlock->numSegments + 1;
Point3F ejectionAxis( 0.0, 0.0, 1.0 );
Point3F axisx;
if (mFabs(ejectionAxis.z) < 0.999f)
mCross(ejectionAxis, Point3F(0, 0, 1), &axisx);
else
mCross(ejectionAxis, Point3F(0, 1, 0), &axisx);
axisx.normalize();
for( U32 i=0; i<numPoints; i++ )
{
F32 t = F32(i) / F32(numPoints);
AngAxisF thetaRot( axisx, mDataBlock->ejectionAngle * (M_PI / 180.0));
AngAxisF phiRot( ejectionAxis, t * (M_PI * 2.0));
Point3F pointAxis = ejectionAxis;
MatrixF temp;
thetaRot.setMatrix(&temp);
temp.mulP(pointAxis);
phiRot.setMatrix(&temp);
temp.mulP(pointAxis);
Point3F startOffset = axisx;
temp.mulV( startOffset );
startOffset *= mDataBlock->startRadius;
SplashRingPoint point;
point.position = getPosition() + startOffset;
point.velocity = pointAxis * mDataBlock->velocity;
ring.points.push_back( point );
}
ring.color = mDataBlock->colors[0];
ring.lifetime = mDataBlock->ringLifetime;
ring.elapsedTime = 0.0;
ring.v = mDataBlock->texFactor * mFmod( mElapsedTime, 1.0 );
return ring;
}
//----------------------------------------------------------------------------
// Emit rings
//----------------------------------------------------------------------------
void Splash::emitRings( F32 dt )
{
mTimeSinceLastRing += dt;
S32 numNewRings = (S32) (mTimeSinceLastRing * F32(mDataBlock->ejectionFreq));
mTimeSinceLastRing -= numNewRings / mDataBlock->ejectionFreq;
for( S32 i=numNewRings-1; i>=0; i-- )
{
F32 t = F32(i) / F32(numNewRings);
t *= dt;
t += mTimeSinceLastRing;
SplashRing ring = createRing();
updateRing( &ring, t );
ringList.link( ring );
}
}
//----------------------------------------------------------------------------
// Update rings
//----------------------------------------------------------------------------
void Splash::updateRings( F32 dt )
{
SplashRing *ring = NULL;
while( bool( ring = ringList.next( ring ) ) )
{
ring->elapsedTime += dt;
if( !ring->isActive() )
{
SplashRing *inactiveRing = ring;
ring = ringList.prev( ring );
ringList.free( inactiveRing );
}
else
{
updateRing( ring, dt );
}
}
}
//----------------------------------------------------------------------------
// Update ring
//----------------------------------------------------------------------------
void Splash::updateRing( SplashRing *ring, F32 dt )
{
for( U32 i=0; i<ring->points.size(); i++ )
{
if( mDead )
{
Point3F vel = ring->points[i].velocity;
vel.normalize();
vel *= mDataBlock->acceleration;
ring->points[i].velocity += vel * dt;
}
ring->points[i].velocity += Point3F( 0.0, 0.0, -9.8 ) * dt;
ring->points[i].position += ring->points[i].velocity * dt;
}
}
//----------------------------------------------------------------------------
// Explode
//----------------------------------------------------------------------------
void Splash::spawnExplosion()
{
if( !mDataBlock->explosion ) return;
Explosion* pExplosion = new Explosion;
pExplosion->onNewDataBlock(mDataBlock->explosion);
MatrixF trans = getTransform();
trans.setPosition( getPosition() );
pExplosion->setTransform( trans );
pExplosion->setInitialState( trans.getPosition(), VectorF(0,0,1), 1);
if (!pExplosion->registerObject())
delete pExplosion;
}
//--------------------------------------------------------------------------
// packUpdate
//--------------------------------------------------------------------------
U32 Splash::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
{
U32 retMask = Parent::packUpdate(con, mask, stream);
if( stream->writeFlag(mask & GameBase::InitialUpdateMask) )
{
mathWrite(*stream, mInitialPosition);
}
return retMask;
}
//--------------------------------------------------------------------------
// unpackUpdate
//--------------------------------------------------------------------------
void Splash::unpackUpdate(NetConnection* con, BitStream* stream)
{
Parent::unpackUpdate(con, stream);
if( stream->readFlag() )
{
mathRead(*stream, &mInitialPosition);
setPosition( mInitialPosition );
}
}

183
engine/game/fx/splash.h Executable file
View File

@ -0,0 +1,183 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _SPLASH_H_
#define _SPLASH_H_
#ifndef _GAMEBASE_H_
#include "game/gameBase.h"
#endif
#ifndef _LLIST_H_
#include "core/llist.h"
#endif
class ParticleEmitter;
class ParticleEmitterData;
class AudioProfile;
class ExplosionData;
//--------------------------------------------------------------------------
// Ring Point
//--------------------------------------------------------------------------
struct SplashRingPoint
{
Point3F position;
Point3F velocity;
};
//--------------------------------------------------------------------------
// Splash Ring
//--------------------------------------------------------------------------
struct SplashRing
{
Vector <SplashRingPoint> points;
ColorF color;
F32 lifetime;
F32 elapsedTime;
F32 v;
SplashRing()
{
color.set( 0.0, 0.0, 0.0, 1.0 );
lifetime = 0.0;
elapsedTime = 0.0;
v = 0.0;
}
bool isActive()
{
return elapsedTime < lifetime;
}
};
//--------------------------------------------------------------------------
// Splash Data
//--------------------------------------------------------------------------
class SplashData : public GameBaseData
{
public:
typedef GameBaseData Parent;
enum Constants
{
NUM_EMITTERS = 3,
NUM_TIME_KEYS = 4,
NUM_TEX = 2,
};
public:
AudioProfile* soundProfile;
S32 soundProfileId;
ParticleEmitterData* emitterList[NUM_EMITTERS];
S32 emitterIDList[NUM_EMITTERS];
S32 delayMS;
S32 delayVariance;
S32 lifetimeMS;
S32 lifetimeVariance;
Point3F scale;
F32 width;
F32 height;
U32 numSegments;
F32 velocity;
F32 acceleration;
F32 texWrap;
F32 texFactor;
F32 ejectionFreq;
F32 ejectionAngle;
F32 ringLifetime;
F32 startRadius;
F32 times[ NUM_TIME_KEYS ];
ColorF colors[ NUM_TIME_KEYS ];
StringTableEntry textureName[NUM_TEX];
TextureHandle textureHandle[NUM_TEX];
ExplosionData* explosion;
S32 explosionId;
SplashData();
DECLARE_CONOBJECT(SplashData);
bool onAdd();
bool preload(bool server, char errorBuffer[256]);
static void initPersistFields();
virtual void packData(BitStream* stream);
virtual void unpackData(BitStream* stream);
};
DECLARE_CONSOLETYPE(SplashData)
//--------------------------------------------------------------------------
// Splash
//--------------------------------------------------------------------------
class Splash : public GameBase
{
typedef GameBase Parent;
private:
SplashData* mDataBlock;
ParticleEmitter * mEmitterList[ SplashData::NUM_EMITTERS ];
LList <SplashRing> ringList;
U32 mCurrMS;
U32 mEndingMS;
F32 mRandAngle;
F32 mRadius;
F32 mVelocity;
F32 mHeight;
ColorF mColor;
F32 mTimeSinceLastRing;
bool mDead;
F32 mElapsedTime;
protected:
Point3F mInitialPosition;
Point3F mInitialNormal;
F32 mFade;
F32 mFog;
bool mActive;
S32 mDelayMS;
protected:
bool onAdd();
void onRemove();
void processTick(const Move *move);
void advanceTime(F32 dt);
void updateEmitters( F32 dt );
void updateWave( F32 dt );
void updateColor();
SplashRing createRing();
void updateRings( F32 dt );
void updateRing( SplashRing *ring, F32 dt );
void emitRings( F32 dt );
void render();
void renderSegment( SplashRing &top, SplashRing &bottom );
void spawnExplosion();
// Rendering
protected:
bool prepRenderImage ( SceneState *state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState=false);
void renderObject ( SceneState *state, SceneRenderImage *image);
public:
Splash();
~Splash();
void setInitialState(const Point3F& point, const Point3F& normal, const F32 fade = 1.0);
U32 packUpdate (NetConnection *conn, U32 mask, BitStream* stream);
void unpackUpdate(NetConnection *conn, BitStream* stream);
bool onNewDataBlock(GameBaseData* dptr);
DECLARE_CONOBJECT(Splash);
};
#endif // _H_SPLASH

142
engine/game/fx/underLava.cc Executable file
View File

@ -0,0 +1,142 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "game/fx/underLava.h"
#include "dgl/dgl.h"
#include "math/mRect.h"
#include "dgl/gTexManager.h"
#include "terrain/waterBlock.h"
#include "math/mConstants.h"
UnderLavaFX gLavaFX;
//**************************************************************************
// Under lava FX - "Lava - With pumice!"
//**************************************************************************
UnderLavaFX::UnderLavaFX()
{
}
//--------------------------------------------------------------------------
// Init
//--------------------------------------------------------------------------
void UnderLavaFX::init()
{
RectI viewport;
dglGetViewport( &viewport );
mViewSize = viewport.extent;
mTexFrequency.x = F32(viewport.extent.x / viewport.extent.y);
mTexFrequency.y = 1.0;
mNumPoints.x = (S32)(50 * F32(viewport.extent.x / viewport.extent.y));
mNumPoints.y = 50;
mWave[0].amplitude = 0.02;
mWave[0].frequency = 2.0;
mWave[0].velocity = Sim::getCurrentTime() / 1000.0 * 2.0;
mMoveSpeed = Sim::getCurrentTime() / 1000.0 * 0.025;
}
//--------------------------------------------------------------------------
// Render
//--------------------------------------------------------------------------
void UnderLavaFX::render()
{
init();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
// TextureHandle lavaTex = WaterBlock::getSubmergeTexture(0);
glBindTexture(GL_TEXTURE_2D, WaterBlock::getSubmergeTexture(0).getGLName());
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_FALSE);
glColor4f(1.0, 1.0, 1.0, 0.5);
if( WaterBlock::getSubmergeTexture(0) )
{
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
// render layer 1
for( U32 i=0; i<mNumPoints.y; i++ )
{
renderRow( i, mNumPoints.y-1, mNumPoints.x-1 );
}
}
// give second layer a different phase
mWave[0].velocity += 10.0;
// lavaTex = WaterBlock::getSubmergeTexture(1);
if( WaterBlock::getSubmergeTexture(1) )
{
glBindTexture(GL_TEXTURE_2D, WaterBlock::getSubmergeTexture(1).getGLName());
// render layer 2
for( U32 i=0; i<mNumPoints.y; i++ )
{
renderRow( i, mNumPoints.y-1, mNumPoints.x-1 );
}
}
glDepthMask(GL_TRUE);
glDisable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glDisable(GL_BLEND);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
//--------------------------------------------------------------------------
// Render row (triangle strip)
//--------------------------------------------------------------------------
void UnderLavaFX::renderRow( U32 row, U32 numRows, U32 numColumns )
{
F32 xMult = F32(2.0) / F32(numColumns);
F32 yMult = F32(2.0) / F32(numRows);
glBegin( GL_TRIANGLE_STRIP );
for( U32 i=0; i<numColumns+1; i++ )
{
F32 u = F32(i) / F32(numColumns) * mTexFrequency.x;
F32 v = F32(row) / F32(numRows) * mTexFrequency.y;
u += mMoveSpeed + mWave[0].amplitude * mSin( u * M_2PI * mWave[0].frequency + mWave[0].velocity );
v += mMoveSpeed + mWave[0].amplitude * mSin( v * M_2PI * mWave[0].frequency + mWave[0].velocity );
glTexCoord2f( u, v );
glVertex2f( -1.0 + i * xMult, -1.0 + row * yMult );
v = F32(row+1) / F32(numRows) * mTexFrequency.y;
v += mMoveSpeed + mWave[0].amplitude * mSin( v * M_2PI * mWave[0].frequency + mWave[0].velocity );
glTexCoord2f( u, v );
glVertex2f( -1.0 + i * xMult, -1.0 + (row+1) * yMult );
}
glEnd();
}

56
engine/game/fx/underLava.h Executable file
View File

@ -0,0 +1,56 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _UNDERLAVA_H_
#define _UNDERLAVA_H_
#ifndef _MPOINT_H_
#include "math/mPoint.h"
#endif
//**************************************************************************
// Data
//**************************************************************************
struct LavaVertex
{
Point2I pnt;
Point2F texPnt;
};
struct LavaWave
{
F32 frequency;
F32 amplitude;
F32 velocity;
};
//**************************************************************************
// Under lava FX
//**************************************************************************
class UnderLavaFX
{
private:
Point2F mTexFrequency;
Point2I mNumPoints;
Point2I mViewSize;
LavaWave mWave[2];
F32 mMoveSpeed;
void renderRow( U32 row, U32 numRows, U32 numColumns );
public:
UnderLavaFX();
void init();
void render();
};
extern UnderLavaFX gLavaFX;
#endif

View File

@ -0,0 +1,715 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "dgl/dgl.h"
#include "console/consoleTypes.h"
#include "math/mathIO.h"
#include "math/mRandom.h"
#include "sceneGraph/sceneState.h"
#include "audio/audioDataBlock.h"
#include "game/fx/weatherLightning.h"
IMPLEMENT_CO_CLIENTEVENT_V1(WeatherLightningStrikeEvent);
IMPLEMENT_CO_DATABLOCK_V1(WeatherLightningData);
IMPLEMENT_CO_NETOBJECT_V1(WeatherLightning);
MRandomLCG sgRandomGen;
S32 QSORT_CALLBACK cmpWLSounds(const void* p1, const void* p2)
{
U32 i1 = *((const S32*)p1);
U32 i2 = *((const S32*)p2);
if (i1 < i2) {
return 1;
} else if (i1 > i2) {
return -1;
} else {
return 0;
}
}
S32 QSORT_CALLBACK cmpWLTextures(const void* t1, const void* t2)
{
StringTableEntry ta = *(StringTableEntry*)t1;
StringTableEntry tb = *(StringTableEntry*)t2;
if(ta && ta[0] != '\0')
{
if(tb && tb[0] != '\0')
return dStricmp(ta, tb);
else
return -1;
}
else
{
if(tb && tb[0] != '\0')
return 1;
else
return 0;
}
}
WeatherLightningStrikeEvent::WeatherLightningStrikeEvent()
{
mLightning = NULL;
}
WeatherLightningStrikeEvent::~WeatherLightningStrikeEvent()
{
}
void WeatherLightningStrikeEvent::pack(NetConnection* con, BitStream* stream)
{
if(!mLightning)
{
stream->writeFlag(false);
return;
}
S32 ghostIndex = con->getGhostIndex(mLightning);
if(ghostIndex == -1)
{
stream->writeFlag(false);
return;
}
stream->writeFlag(true);
stream->writeRangedU32(U32(ghostIndex), 0, NetConnection::MaxGhostCount);
stream->writeFloat(mStart.x, PositionalBits);
stream->writeFloat(mStart.y, PositionalBits);
}
void WeatherLightningStrikeEvent::unpack(NetConnection* con, BitStream* stream)
{
if(!stream->readFlag())
return;
S32 ghostIndex = stream->readRangedU32(0, NetConnection::MaxGhostCount);
mLightning = NULL;
NetObject* pObject = con->resolveGhost(ghostIndex);
if(pObject)
mLightning = dynamic_cast<WeatherLightning*>(pObject);
mStart.x = stream->readFloat(PositionalBits);
mStart.y = stream->readFloat(PositionalBits);
}
void WeatherLightningStrikeEvent::process(NetConnection*)
{
if (mLightning)
mLightning->processEvent(this);
}
//--------------------------------------------------------------------------
WeatherLightningData::WeatherLightningData()
{
dMemset(strikeTextureNames, 0, sizeof(strikeTextureNames));
dMemset(flashTextureNames, 0, sizeof(flashTextureNames));
dMemset(fuzzyTextureNames, 0, sizeof(fuzzyTextureNames));
dMemset(strikeTextures, 0, sizeof(strikeTextures));
dMemset(flashTextures, 0, sizeof(flashTextures));
dMemset(fuzzyTextures, 0, sizeof(fuzzyTextures));
strikeSoundId = -1;
strikeSound = NULL_AUDIOHANDLE;
for(U32 i = 0; i < MaxSounds; i++)
{
thunderSoundIds[i] = -1;
thunderSounds[i] = NULL_AUDIOHANDLE;
}
}
WeatherLightningData::~WeatherLightningData()
{
//
};
void WeatherLightningData::initPersistFields()
{
Parent::initPersistFields();
addField("strikeTextures", TypeFilename, Offset(strikeTextureNames, WeatherLightningData), MaxStrikeTextures);
addField("flashTextures", TypeFilename, Offset(flashTextureNames, WeatherLightningData), MaxFlashTextures);
addField("fuzzyTextures", TypeFilename, Offset(fuzzyTextureNames, WeatherLightningData), MaxFuzzyTextures);
addField("strikeSound", TypeAudioProfilePtr, Offset(strikeSound, WeatherLightningData));
addField("thunderSounds", TypeAudioProfilePtr, Offset(thunderSounds, WeatherLightningData), MaxSounds);
}
bool WeatherLightningData::onAdd()
{
if(!Parent::onAdd())
return false;
if(!strikeSound && strikeSoundId != -1)
{
if(Sim::findObject(strikeSoundId, strikeSound) == false)
Con::errorf(ConsoleLogEntry::General, "WeatherLightningData::onAdd: Invalid packet, bad datablockId(sound: %d", strikeSound);
}
for(U32 i = 0; i < MaxSounds; i++)
{
if(!thunderSounds[i] && thunderSoundIds[i] != -1)
{
if(Sim::findObject(thunderSoundIds[i], thunderSounds[i]) == false)
Con::errorf(ConsoleLogEntry::General, "WeahterLightningData::onAdd: Invalid packet, bad datablockId(sound: %d", thunderSounds[i]);
}
}
return true;
}
bool WeatherLightningData::preload(bool server, char errorBuffer[256])
{
if(Parent::preload(server, errorBuffer) == false)
return false;
dQsort(strikeTextureNames, MaxStrikeTextures, sizeof(StringTableEntry), cmpWLTextures);
dQsort(flashTextureNames, MaxFlashTextures, sizeof(StringTableEntry), cmpWLTextures);
dQsort(fuzzyTextureNames, MaxFuzzyTextures, sizeof(StringTableEntry), cmpWLTextures);
if(!server)
{
for(numStrikes = 0; numStrikes < MaxStrikeTextures; numStrikes++)
{
if(strikeTextureNames[numStrikes] && strikeTextureNames[numStrikes][0] != '\0')
strikeTextures[numStrikes] = TextureHandle(strikeTextureNames[numStrikes], MeshTexture);
else
break;
}
for(numFlashes = 0; numFlashes < MaxFlashTextures && flashTextureNames[numFlashes] != NULL; numFlashes++)
{
if(flashTextureNames[numFlashes] && flashTextureNames[numFlashes][0] != '\0')
flashTextures[numFlashes] = TextureHandle(flashTextureNames[numFlashes], MeshTexture);
else
break;
}
for(numFuzzes = 0; numFuzzes < MaxFuzzyTextures && fuzzyTextureNames[numFuzzes] != NULL; numFuzzes++)
{
if(fuzzyTextureNames[numFuzzes] && fuzzyTextureNames[numFuzzes][0] != '\0')
fuzzyTextures[numFuzzes] = TextureHandle(fuzzyTextureNames[numFuzzes], MeshTexture);
else
break;
}
}
dQsort(thunderSounds, MaxSounds, sizeof(AudioProfile*), cmpWLSounds);
for(numSounds = 0; numSounds < MaxSounds && thunderSounds[numSounds] != NULL_AUDIOHANDLE; numSounds++) {
//
}
return true;
}
void WeatherLightningData::packData(BitStream* stream)
{
Parent::packData(stream);
U32 i;
for (i = 0; i < MaxStrikeTextures; i++)
stream->writeString(strikeTextureNames[i]);
for(i = 0; i < MaxFlashTextures; i++)
stream->writeString(flashTextureNames[i]);
for(i = 0; i < MaxFuzzyTextures; i++)
stream->writeString(fuzzyTextureNames[i]);
if(stream->writeFlag(strikeSound != NULL_AUDIOHANDLE))
stream->writeRangedU32(strikeSound->getId(), DataBlockObjectIdFirst,
DataBlockObjectIdLast);
for(i = 0; i < MaxSounds; i++)
{
if(stream->writeFlag(thunderSounds[i] != NULL_AUDIOHANDLE))
stream->writeRangedU32(thunderSounds[i]->getId(), DataBlockObjectIdFirst,
DataBlockObjectIdLast);
}
}
void WeatherLightningData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
U32 i;
for(i = 0; i < MaxStrikeTextures; i++)
strikeTextureNames[i] = stream->readSTString();
for(i = 0; i < MaxFlashTextures; i++)
flashTextureNames[i] = stream->readSTString();
for(i = 0; i < MaxFuzzyTextures; i++)
fuzzyTextureNames[i] = stream->readSTString();
if(stream->readFlag())
strikeSoundId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
else
strikeSoundId = -1;
for(i = 0; i < MaxSounds; i++)
{
if(stream->readFlag())
thunderSoundIds[i] = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
else
thunderSoundIds[i] = -1;
}
}
//--------------------------------------------------------------------------
WeatherLightning::WeatherLightning()
{
mNetFlags.set(Ghostable | ScopeAlways);
mTypeMask |= StaticObjectType|EnvironmentObjectType;
lastThink = 0;
strikesPerMinute = 9;
boltDeathAge = 1.5;
}
WeatherLightning::~WeatherLightning()
{
//
}
void WeatherLightning::initPersistFields()
{
Parent::initPersistFields();
addNamedField(strikesPerMinute, TypeS32, WeatherLightning);
addNamedField(boltDeathAge, TypeF32, WeatherLightning);
}
bool WeatherLightning::onAdd()
{
if(!Parent::onAdd())
return false;
mObjBox.min.set( -0.5, -0.5, -0.5 );
mObjBox.max.set( 0.5, 0.5, 0.5 );
resetWorldBox();
addToScene();
return true;
}
void WeatherLightning::onRemove()
{
while(mActiveBolts.size())
{
WeatherLightningBolt* bolt = mActiveBolts[0];
delete bolt;
mActiveBolts.erase_fast(U32(0));
}
while(mSoundEvents.size())
mSoundEvents.erase_fast(U32(0));
removeFromScene();
Parent::onRemove();
}
bool WeatherLightning::onNewDataBlock(GameBaseData* dptr)
{
mDataBlock = dynamic_cast<WeatherLightningData*>(dptr);
if(!mDataBlock || !Parent::onNewDataBlock(dptr))
return false;
scriptOnNewDataBlock();
return true;
}
bool WeatherLightning::prepRenderImage(SceneState* state, const U32 stateKey, const U32, const bool)
{
if(isLastState(state, stateKey))
return false;
setLastState(state, stateKey);
// This should be sufficient for most objects that don't manage zones, and
// don't need to return a specialized RenderImage...
if(state->isObjectRendered(this))
{
SceneRenderImage* image = new SceneRenderImage;
image->obj = this;
image->isTranslucent = true;
image->sortType = SceneRenderImage::EndSort;
state->insertRenderImage(image);
}
return false;
}
void WeatherLightningBolt::render(const Point3F &camPos)
{
Point3F perpVec;
Point3F lightUp = startPoint - endPoint;
mCross(camPos - endPoint, lightUp, &perpVec);
perpVec.normalize();
Point3F frontVec;
mCross(perpVec, lightUp, &frontVec);
frontVec.normalize();
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glDepthFunc(GL_LEQUAL);
//
// strike texture
//
// setup alpha value
F32 strikeAlpha;
if(currentAge < (strikeTime / 3.0))
{
strikeAlpha = currentAge / (strikeTime / 3.0);
strikeAlpha = mPow(strikeAlpha, F32(1.0 / 3.0));
}
else if(currentAge < (2.0 * strikeTime / 3.0))
strikeAlpha = 1.0;
else
strikeAlpha = 1.0 - ((currentAge - (2.0 * strikeTime / 3.0)) / (strikeTime / 3.0));
glColor4f(1.0f, 1.0f, 1.0f, strikeAlpha);
// generate texture coords
Point3F points[4];
F32 width = ((startPoint.z - endPoint.z) * 0.125f);
points[0] = startPoint - perpVec * width;
points[1] = startPoint + perpVec * width;
points[2] = endPoint + perpVec * width;
points[3] = endPoint - perpVec * width;
// bind and draw texture
glBindTexture(GL_TEXTURE_2D, strikeTexture->getGLName());
glBegin(GL_TRIANGLE_FAN);
glTexCoord2f(0, 0); glVertex3fv(points[0]);
glTexCoord2f(0, 1); glVertex3fv(points[1]);
glTexCoord2f(1, 1); glVertex3fv(points[2]);
glTexCoord2f(1, 0); glVertex3fv(points[3]);
//glTexCoord2f(0, 0); glVertex3fv(points[0]);
//glTexCoord2f(1, 0); glVertex3fv(points[1]);
//glTexCoord2f(1, 1); glVertex3fv(points[2]);
//glTexCoord2f(0, 1); glVertex3fv(points[3]);
glEnd();
//
// fuzzy texture
//
// setup alpha value
F32 constAlpha;
if(currentAge < strikeTime / 2.0)
constAlpha = currentAge / (strikeTime / 2.0);
else if(currentAge < (2.0 * strikeTime / 3.0))
constAlpha = 1.0 - ((currentAge - (strikeTime / 2.0)) / (strikeTime / 6.0));
else
constAlpha = 0.0;
glColor4f(1.0, 1.0, 1.0, constAlpha);
// generate texture coords
width *= 4;
points[0] = startPoint - perpVec * width;
points[1] = startPoint + perpVec * width;
points[2] = endPoint + perpVec * width;
points[3] = endPoint - perpVec * width;
if(constAlpha != 0.0)
{
// bind and draw texture
glBindTexture(GL_TEXTURE_2D, fuzzyTexture->getGLName());
glBegin(GL_TRIANGLE_FAN);
glTexCoord2f(0, 0); glVertex3fv(points[0]);
glTexCoord2f(0, 1); glVertex3fv(points[1]);
glTexCoord2f(1, 1); glVertex3fv(points[2]);
glTexCoord2f(1, 0); glVertex3fv(points[3]);
//glTexCoord2f(0, 0); glVertex3fv(points[0]);
//glTexCoord2f(1, 0); glVertex3fv(points[1]);
//glTexCoord2f(1, 1); glVertex3fv(points[2]);
//glTexCoord2f(0, 1); glVertex3fv(points[3]);
glEnd();
glDepthMask(GL_TRUE);
}
//
// flash texture
//
// setup alpha value
glColor4f(1.0f, 1.0f, 1.0f, strikeAlpha);
// generate texture coords
points[0] = startPoint - perpVec * width + frontVec * width;
points[1] = startPoint - perpVec * width - frontVec * width;
points[2] = startPoint + perpVec * width - frontVec * width;
points[3] = startPoint + perpVec * width + frontVec * width;
// bind and draw texture
glBindTexture(GL_TEXTURE_2D, flashTexture->getGLName());
glBegin(GL_TRIANGLE_FAN);
glTexCoord2f(0, 0); glVertex3fv(points[0]);
glTexCoord2f(0, 1); glVertex3fv(points[1]);
glTexCoord2f(1, 1); glVertex3fv(points[2]);
glTexCoord2f(1, 0); glVertex3fv(points[3]);
//glTexCoord2f(0, 0); glVertex3fv(points[0]);
//glTexCoord2f(1, 0); glVertex3fv(points[1]);
//glTexCoord2f(1, 1); glVertex3fv(points[2]);
//glTexCoord2f(0, 1); glVertex3fv(points[3]);
glEnd();
//
// finished
//
glDepthMask(GL_TRUE);
}
void WeatherLightning::renderObject(SceneState* state, SceneRenderImage*)
{
if(mActiveBolts.size())
{
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
RectI viewport;
F64 farPlane;
glMatrixMode(GL_PROJECTION);
glPushMatrix();
dglGetViewport(&viewport);
farPlane = state->getFarPlane();
const Point3F &camPos = state->getCameraPosition();
// adjust far clip plane
F64 distance = (getPosition() - camPos).lenSquared();
state->setFarPlane(getMax(farPlane, distance));
state->setupObjectProjection(this);
glDisable(GL_CULL_FACE);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
for(U32 i = 0; i < mActiveBolts.size(); i++)
mActiveBolts[i]->render(camPos);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
state->setFarPlane(farPlane);
dglSetViewport(viewport);
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
}
}
void WeatherLightning::processTick(const Move *move)
{
Parent::processTick(move);
if (isServerObject())
{
S32 msBetweenStrikes = (S32)(60.0 / strikesPerMinute * 1000.0);
lastThink += TickMs;
if( lastThink > msBetweenStrikes )
{
strikeRandomPoint();
lastThink -= msBetweenStrikes;
}
}
}
void WeatherLightning::advanceTime(F32 dt)
{
Parent::advanceTime(dt);
U32 i;
// loop through and erase any dead bolts
for(i = 0; i < mActiveBolts.size();)
{
WeatherLightningBolt* bolt = mActiveBolts[i];
bolt->currentAge += dt;
if(bolt->currentAge > bolt->deathAge)
{
delete bolt;
mActiveBolts.erase_fast(i);
continue;
}
i++;
}
// loop through and find any pending sound events
for(i = 0; i < mSoundEvents.size();)
{
SoundEvent *sEvent = &mSoundEvents[i];
sEvent->time -= dt;
if(sEvent->time <= 0.0)
{
// fire off the sound
if(sEvent->soundBlockId != -1)
alxPlay(mDataBlock->thunderSounds[sEvent->soundBlockId], &sEvent->position);
mSoundEvents.erase_fast(i);
continue;
}
i++;
}
}
void WeatherLightning::strikeRandomPoint()
{
// choose random strike point within object bounds
Point2F strikePoint;
strikePoint.x = sgRandomGen.randF( 0.0, 1.0 );
strikePoint.y = sgRandomGen.randF( 0.0, 1.0 );
SimGroup* pClientGroup = Sim::getClientGroup();
for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++)
{
NetConnection* nc = static_cast<NetConnection*>(*itr);
WeatherLightningStrikeEvent* wlEvent = new WeatherLightningStrikeEvent;
wlEvent->mLightning = this;
wlEvent->mStart = strikePoint;
nc->postNetEvent(wlEvent);
}
}
TextureHandle* WeatherLightning::getRandomStrike()
{
U32 strike = (U32)(mCeil(mDataBlock->numStrikes * sgRandomGen.randF()) - 1.0f);
return &mDataBlock->strikeTextures[strike];
}
TextureHandle* WeatherLightning::getRandomFlash()
{
U32 flash = (U32)(mCeil(mDataBlock->numFlashes * sgRandomGen.randF()) - 1.0f);
return &mDataBlock->flashTextures[flash];
}
TextureHandle* WeatherLightning::getRandomFuzzy()
{
U32 fuzzy = (U32)(mCeil(mDataBlock->numFuzzes * sgRandomGen.randF()) - 1.0f);
return &mDataBlock->fuzzyTextures[fuzzy];
}
S32 WeatherLightning::getRandomSound()
{
U32 sound = (U32)(mCeil(mDataBlock->numSounds * sgRandomGen.randF()) - 1.0f);
if(mDataBlock->thunderSounds[sound] != NULL_AUDIOHANDLE)
return sound;
return -1;
}
void WeatherLightning::processEvent(WeatherLightningStrikeEvent* wlEvent)
{
AssertFatal(wlEvent->mStart.x >= 0 && wlEvent->mStart.x <= 1.0, "Out of bounds coord!");
mActiveBolts.push_back(new WeatherLightningBolt);
WeatherLightningBolt* bolt = mActiveBolts.last();
Point3F strikePoint(0.0, 0.0, 0.0);
strikePoint.x = wlEvent->mStart.x;
strikePoint.y = wlEvent->mStart.y;
strikePoint *= mObjScale;
strikePoint += getPosition();
strikePoint += Point3F( -mObjScale.x * 0.5, -mObjScale.y * 0.5, 0.0 );
RayInfo rayInfo;
Point3F start = strikePoint;
start.z = mObjScale.z * 0.5 + getPosition().z;
strikePoint.z += -mObjScale.z * 0.5;
bool rayHit = gClientContainer.castRay(start, strikePoint, (STATIC_COLLISION_MASK | WaterObjectType), &rayInfo);
if(rayHit)
strikePoint.z = rayInfo.point.z;
F32 height = mObjScale.z * 0.5 + getPosition().z;
bolt->startPoint = Point3F(strikePoint.x, strikePoint.y, height);
bolt->endPoint = strikePoint;
bolt->currentAge = 0.0f;
bolt->deathAge = boltDeathAge;
bolt->strikeTime = 0.35;
bolt->strikeTexture = getRandomStrike();
bolt->flashTexture = getRandomFlash();
bolt->fuzzyTexture = getRandomFuzzy();
// setup a thunder sound event
Point3F listener;
alxGetListenerPoint3F(AL_POSITION, &listener);
mSoundEvents.increment();
SoundEvent& sEvent = mSoundEvents.last();
// find the length to the closest point on the bolt
Point3F dHat = bolt->startPoint - bolt->endPoint;
F32 boltLength = dHat.len();
dHat /= boltLength;
F32 distAlong = mDot((listener - bolt->endPoint), dHat);
Point3F contactPoint;
if(distAlong >= boltLength)
contactPoint = bolt->startPoint;
else if(distAlong <= 0.0)
contactPoint = bolt->endPoint;
else
contactPoint = bolt->endPoint + dHat * distAlong;
F32 delayDist = (listener - contactPoint).len();
U32 delayTime = U32((delayDist / 330.0f) * 100.0f);
MatrixF trans(true);
trans.setPosition(contactPoint);
sEvent.soundBlockId = getRandomSound();
sEvent.position = trans;
sEvent.time = delayTime;
// play strike sound
trans.setPosition(strikePoint);
if(mDataBlock->strikeSound)
alxPlay(mDataBlock->strikeSound, &trans);
}
U32 WeatherLightning::packUpdate(NetConnection *conn, U32 mask, BitStream *stream)
{
U32 retMask = Parent::packUpdate(conn, mask, stream);
// Only write data if this is the initial packet or we've been inspected.
if (stream->writeFlag(mask & (InitialUpdateMask | ExtendedInfoMask)))
{
// Initial update
mathWrite(*stream, getPosition());
mathWrite(*stream, mObjScale);
}
return retMask;
}
void WeatherLightning::unpackUpdate(NetConnection *conn, BitStream *stream)
{
Parent::unpackUpdate(conn, stream);
if (stream->readFlag())
{
// Initial update
Point3F pos;
mathRead(*stream, &pos);
setPosition( pos );
mathRead(*stream, &mObjScale);
}
}

172
engine/game/fx/weatherLightning.h Executable file
View File

@ -0,0 +1,172 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _WEATHER_LIGHTNING_H_
#define _WEATHER_LIGHTNING_H_
#ifndef _NETCONNECTION_H_
#include "sim/netConnection.h"
#endif
#ifndef _GAMEBASE_H_
#include "game/gameBase.h"
#endif
#ifndef _LIGHTMANAGER_H_
#include "sceneGraph/lightManager.h"
#endif
class AudioProfile;
class WeatherLightning;
class WeatherLightningStrikeEvent : public NetEvent
{
typedef NetEvent Parent;
public:
enum Constants {
PositionalBits = 10
};
Point2F mStart;
WeatherLightning* mLightning;
public:
WeatherLightningStrikeEvent();
~WeatherLightningStrikeEvent();
void pack(NetConnection*, BitStream*);
void write(NetConnection*, BitStream*){}
void unpack(NetConnection*, BitStream*);
void process(NetConnection*);
DECLARE_CONOBJECT(WeatherLightningStrikeEvent);
};
class WeatherLightningData : public GameBaseData
{
typedef GameBaseData Parent;
protected:
bool onAdd();
public:
enum {
MaxSounds = 4,
MaxStrikeTextures = 6,//8,
MaxFlashTextures = 4, //6,
MaxFuzzyTextures = 2, //4,
};
// primary strike texture
U32 numStrikes;
StringTableEntry strikeTextureNames[MaxStrikeTextures];
TextureHandle strikeTextures[MaxStrikeTextures];
// flash texture
U32 numFlashes;
StringTableEntry flashTextureNames[MaxFlashTextures];
TextureHandle flashTextures[MaxFlashTextures];
// fuzzy/stretch texture
U32 numFuzzes;
StringTableEntry fuzzyTextureNames[MaxFuzzyTextures];
TextureHandle fuzzyTextures[MaxFuzzyTextures];
//strike sound
S32 strikeSoundId;
AudioProfile* strikeSound;
// thunder sounds
U32 numSounds;
S32 thunderSoundIds[MaxSounds];
AudioProfile* thunderSounds[MaxSounds];
public:
WeatherLightningData();
~WeatherLightningData();
void packData(BitStream*);
void unpackData(BitStream*);
bool preload(bool server, char errorBuffer[256]);
DECLARE_CONOBJECT(WeatherLightningData);
static void initPersistFields();
};
struct WeatherLightningBolt
{
Point3F startPoint;
Point3F endPoint;
F32 currentAge;
F32 deathAge;
F32 strikeTime;
TextureHandle* strikeTexture;
TextureHandle* flashTexture;
TextureHandle* fuzzyTexture;
void render(const Point3F &camPos);
};
class WeatherLightning : public GameBase
{
typedef GameBase Parent;
// datablock
WeatherLightningData* mDataBlock;
U32 lastThink;
U32 strikesPerMinute;
U32 boltDeathAge;
struct SoundEvent {
S32 soundBlockId;
MatrixF position;
U32 time;
};
// only active on client
VectorPtr<WeatherLightningBolt*> mActiveBolts;
Vector<SoundEvent> mSoundEvents;
protected:
bool onAdd();
void onRemove();
bool onNewDataBlock(GameBaseData* dptr);
// rendering
bool prepRenderImage(SceneState *state, const U32 stateKey, const U32 startZone, const bool modifyBaseZoneState);
void renderObject(SceneState *state, SceneRenderImage *image);
// simulation
void processTick(const Move *move);
void advanceTime(F32 dt);
// grab random textures
TextureHandle* getRandomStrike();
TextureHandle* getRandomFlash();
TextureHandle* getRandomFuzzy();
// grab random sounds
S32 getRandomSound();
public:
WeatherLightning();
~WeatherLightning();
// strike random point within object box
void strikeRandomPoint();
// receive lightning event and create lightning bolt
void processEvent(WeatherLightningStrikeEvent*);
U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream);
void unpackUpdate(NetConnection *conn, BitStream *stream);
DECLARE_CONOBJECT(WeatherLightning);
static void initPersistFields();
};
#endif // _WEATHER_LIGHTNING_H_