Engine directory for ticket #1

This commit is contained in:
DavidWyand-GG 2012-09-19 11:15:01 -04:00
parent 352279af7a
commit 7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions

View file

@ -0,0 +1,208 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "T3D/fx/cameraFXMgr.h"
#include "platform/profiler.h"
#include "math/mRandom.h"
#include "math/mMatrix.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;
remoteControlled = false;
isAdded = false;
}
bool CameraShake::isExpired()
{
if ( remoteControlled )
return false;
return Parent::isExpired();
}
//--------------------------------------------------------------------------
// Update
//--------------------------------------------------------------------------
void CameraShake::update( F32 dt )
{
Parent::update( dt );
if ( !remoteControlled )
fadeAmplitude();
else
mAmp = mStartAmp;
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.pushFront( newFX );
}
void CameraFXManager::removeFX( CameraFX *fx )
{
CamFXList::Iterator itr = mFXList.begin();
for ( ; itr != mFXList.end(); itr++ )
{
if ( *itr == fx )
{
mFXList.erase( itr );
return;
}
}
return;
}
//--------------------------------------------------------------------------
// Clear all currently running camera effects
//--------------------------------------------------------------------------
void CameraFXManager::clear()
{
for(CamFXList::Iterator i = mFXList.begin(); i != mFXList.end(); ++i)
{
delete *i;
}
mFXList.clear();
}
//--------------------------------------------------------------------------
// Update camera effects
//--------------------------------------------------------------------------
void CameraFXManager::update( F32 dt )
{
PROFILE_SCOPE( CameraFXManager_update );
mCamFXTrans.identity();
CamFXList::Iterator cur;
for(CamFXList::Iterator i = mFXList.begin(); i != mFXList.end(); /*Trickiness*/)
{
// Store previous iterator and increment while iterator is still valid.
cur = i;
++i;
CameraFX * curFX = *cur;
curFX->update( dt );
MatrixF fxTrans = curFX->getTrans();
mCamFXTrans.mul( fxTrans );
if( curFX->isExpired() )
{
delete curFX;
mFXList.erase( cur );
}
}
}

View file

@ -0,0 +1,113 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _CAMERAFXMGR_H_
#define _CAMERAFXMGR_H_
#ifndef _TORQUE_LIST_
#include "core/util/tList.h"
#endif
#ifndef _MPOINT3_H_
#include "math/mPoint3.h"
#endif
#ifndef _MMATRIX_H_
#include "math/mMatrix.h"
#endif
//**************************************************************************
// Abstract camera effect template
//**************************************************************************
class CameraFX
{
protected:
MatrixF mCamFXTrans;
F32 mElapsedTime;
F32 mDuration;
public:
CameraFX();
MatrixF & getTrans(){ return mCamFXTrans; }
virtual 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:
/// Is controlled by someone else, ignore duration and do not delete.
bool remoteControlled;
bool isAdded;
CameraShake();
void init();
void fadeAmplitude();
void setFalloff( F32 falloff ){ mFalloff = falloff; }
void setFrequency( VectorF &freq ){ mFreq = freq; }
void setAmplitude( VectorF &amp ){ mStartAmp = amp; }
bool isExpired();
virtual void update( F32 dt );
};
//**************************************************************************
// CameraFXManager
//**************************************************************************
class CameraFXManager
{
typedef CameraFX * CameraFXPtr;
MatrixF mCamFXTrans;
typedef Torque::List<CameraFXPtr> CamFXList;
CamFXList mFXList;
public:
void addFX( CameraFX *newFX );
void removeFX( CameraFX *fx );
void clear();
MatrixF & getTrans(){ return mCamFXTrans; }
void update( F32 dt );
CameraFXManager();
~CameraFXManager();
};
extern CameraFXManager gCamFXMgr;
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,199 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _EXPLOSION_H_
#define _EXPLOSION_H_
#ifndef _GAMEBASE_H_
#include "T3D/gameBase/gameBase.h"
#endif
#ifndef _TSSHAPE_H_
#include "ts/tsShape.h"
#endif
#ifndef __RESOURCE_H__
#include "core/resource.h"
#endif
#ifndef _LIGHTINFO_H_
#include "lighting/lightInfo.h"
#endif
class ParticleEmitter;
class ParticleEmitterData;
class TSThread;
class SFXTrack;
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;
SFXTrack* soundProfile;
ParticleEmitterData* particleEmitter;
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;
F32 lightStartBrightness;
F32 lightEndBrightness;
F32 lightNormalOffset;
ExplosionData();
DECLARE_CONOBJECT(ExplosionData);
bool onAdd();
bool preload(bool server, String &errorStr);
static void initPersistFields();
virtual void packData(BitStream* stream);
virtual void unpackData(BitStream* stream);
};
//--------------------------------------------------------------------------
class Explosion : public GameBase, public ISceneLight
{
typedef GameBase Parent;
private:
ExplosionData* mDataBlock;
TSShapeInstance* mExplosionInstance;
TSThread* mExplosionThread;
SimObjectPtr<ParticleEmitter> mEmitterList[ ExplosionData::EC_NUM_EMITTERS ];
SimObjectPtr<ParticleEmitter> mMainEmitter;
U32 mCurrMS;
U32 mEndingMS;
F32 mRandAngle;
LightInfo* mLight;
protected:
Point3F mInitialNormal;
F32 mFade;
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:
void prepRenderImage( SceneRenderState *state );
void prepBatchRender(SceneRenderState *state);
void prepModelView(SceneRenderState*);
public:
Explosion();
~Explosion();
void setInitialState(const Point3F& point, const Point3F& normal, const F32 fade = 1.0);
// ISceneLight
virtual void submitLights( LightManager *lm, bool staticLighting );
virtual LightInfo* getLight() { return mLight; }
bool onNewDataBlock( GameBaseData *dptr, bool reload );
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,392 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _FOLIAGEREPLICATOR_H_
#define _FOLIAGEREPLICATOR_H_
#ifndef _SCENEOBJECT_H_
#include "scene/sceneObject.h"
#endif
#ifndef _GFXTEXTUREHANDLE_H_
#include "gfx/gfxTextureHandle.h"
#endif
#ifndef _GFXPRIMITIVEBUFFER_H_
#include "gfx/gfxPrimitiveBuffer.h"
#endif
#ifndef _GBITMAP_H_
#include "gfx/bitmap/gBitmap.h"
#endif
#ifndef _RENDERPASSMANAGER_H_
#include "renderInstance/renderPassManager.h"
#endif
#ifndef _MATHUTIL_FRUSTUM_H_
#include "math/util/frustum.h"
#endif
#pragma warning( push, 4 )
#define AREA_ANIMATION_ARC (1.0f / 360.0f)
#define FXFOLIAGEREPLICATOR_COLLISION_MASK ( TerrainObjectType | \
InteriorObjectType | \
StaticShapeObjectType | \
WaterObjectType )
#define FXFOLIAGEREPLICATOR_NOWATER_COLLISION_MASK ( TerrainObjectType | \
InteriorObjectType | \
StaticShapeObjectType )
#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;
// Used in DrawIndexPrimitive call.
U32 startIndex;
U32 primitiveCount;
};
//------------------------------------------------------------------------------
// Class: fxFoliageRenderList
//------------------------------------------------------------------------------
class fxFoliageRenderList
{
public:
Box3F mBox; // Clipping Box.
Frustum mFrustum; // View frustum.
Vector<fxFoliageItem*> mVisObjectSet; // Visible Object Set.
F32 mHeightLerp; // Height Lerp.
public:
bool IsQuadrantVisible(const Box3F VisBox, const MatrixF& RenderTransform);
void SetupClipPlanes(SceneRenderState* state, const F32 FarClipPlane);
void DrawQuadBox(const Box3F& QuadBox, const ColorF Colour);
};
// Define a vertex
GFXDeclareVertexFormat( GFXVertexFoliage )
{
Point3F point;
Point3F normal;
Point2F texCoord;
Point2F texCoord2;
};
//------------------------------------------------------------------------------
// Class: fxFoliageReplicator
//------------------------------------------------------------------------------
class fxFoliageReplicator : public SceneObject
{
private:
typedef SceneObject Parent;
protected:
void CreateFoliage(void);
void DestroyFoliage(void);
void DestroyFoliageItems();
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;
U32 mCurrentFoliageCount;
Vector<fxFoliageQuadrantNode*> mFoliageQuadTree;
Vector<fxFoliageItem*> mReplicatedFoliage;
fxFoliageRenderList mFrustumRenderSet;
GFXVertexBufferHandle<GFXVertexFoliage> mVertexBuffer;
GFXPrimitiveBufferHandle mPrimBuffer;
GFXShaderRef mShader;
ShaderData* mShaderData;
GBitmap* mAlphaLookup;
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.
// Used for alpha lookup in the pixel shader
GFXTexHandle mAlphaTexture;
GFXStateBlockRef mPlacementSB;
GFXStateBlockRef mRenderSB;
GFXStateBlockRef mDebugSB;
GFXShaderConstBufferRef mFoliageShaderConsts;
GFXShaderConstHandle* mFoliageShaderProjectionSC;
GFXShaderConstHandle* mFoliageShaderWorldSC;
GFXShaderConstHandle* mFoliageShaderGlobalSwayPhaseSC;
GFXShaderConstHandle* mFoliageShaderSwayMagnitudeSideSC;
GFXShaderConstHandle* mFoliageShaderSwayMagnitudeFrontSC;
GFXShaderConstHandle* mFoliageShaderGlobalLightPhaseSC;
GFXShaderConstHandle* mFoliageShaderLuminanceMagnitudeSC;
GFXShaderConstHandle* mFoliageShaderLuminanceMidpointSC;
GFXShaderConstHandle* mFoliageShaderDistanceRangeSC;
GFXShaderConstHandle* mFoliageShaderCameraPosSC;
GFXShaderConstHandle* mFoliageShaderTrueBillboardSC;
//pixel shader
GFXShaderConstHandle* mFoliageShaderGroundAlphaSC;
GFXShaderConstHandle* mFoliageShaderAmbientColorSC;
GFXShaderConstHandle* mDiffuseTextureSC;
GFXShaderConstHandle* mAlphaMapTextureSC;
bool mDirty;
void SetupShader();
void SetupBuffers();
void renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance*);
void renderBuffers(SceneRenderState* state);
void renderArc(const F32 fRadiusX, const F32 fRadiusY);
void renderPlacementArea(const F32 ElapsedTime);
void renderQuad(fxFoliageQuadrantNode* quadNode, const MatrixF& RenderTransform, const bool UseDebug);
void computeAlphaTex();
public:
fxFoliageReplicator();
~fxFoliageReplicator();
void StartUp(void);
void ShowReplication(void);
void HideReplication(void);
// SceneObject
virtual void prepRenderImage( SceneRenderState *state );
// SimObject
bool onAdd();
void onRemove();
void inspectPostApply();
// NetObject
U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
void unpackUpdate(NetConnection *conn, BitStream *stream);
// Editor
void onGhostAlwaysDone();
// ConObject.
static void initPersistFields();
// Field Data.
class tagFieldData
{
public:
bool mUseDebugInfo;
F32 mDebugBoxHeight;
U32 mSeed;
StringTableEntry mFoliageFile;
GFXTexHandle 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 mUseTrueBillboards;
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 = GFXTexHandle();
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;
mUseTrueBillboards = false;
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);
};
#pragma warning( pop )
#endif // _FOLIAGEREPLICATOR_H_

View file

@ -0,0 +1,764 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "T3D/fx/fxShapeReplicator.h"
#include "gfx/gfxDevice.h"
#include "gfx/primBuilder.h"
#include "console/consoleTypes.h"
#include "core/stream/bitStream.h"
#include "math/mRandom.h"
#include "math/mathIO.h"
#include "T3D/gameBase/gameConnection.h"
#include "scene/sceneManager.h"
#include "scene/sceneRenderState.h"
#include "renderInstance/renderPassManager.h"
#include "console/engineAPI.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);
ConsoleDocClass( fxShapeReplicator,
"@brief An emitter for objects to replicate across an area.\n"
"@ingroup Foliage\n"
);
ConsoleDocClass( fxShapeReplicatedStatic,
"@brief The object definition for shapes that will be replicated across an area using an fxShapeReplicator.\n"
"@ingroup Foliage\n"
);
//------------------------------------------------------------------------------
// Class: fxShapeReplicator
//------------------------------------------------------------------------------
fxShapeReplicator::fxShapeReplicator()
{
// Setup NetObject.
mTypeMask |= StaticObjectType;
mNetFlags.set(Ghostable | ScopeAlways);
// Reset Shape Count.
mCurrentShapeCount = 0;
// Reset Creation Area Angle Animation.
mCreationAreaAngle = 0;
// Reset Last Render Time.
mLastRenderTime = 0;
mPlacementSB = NULL;
}
//------------------------------------------------------------------------------
fxShapeReplicator::~fxShapeReplicator()
{
mPlacementSB = NULL;
}
//------------------------------------------------------------------------------
void fxShapeReplicator::initPersistFields()
{
// Add out own persistent fields.
addGroup( "Debugging" ); // MM: Added Group Header.
addField( "HideReplications", TypeBool, Offset( mFieldData.mHideReplications, fxShapeReplicator ), "Replicated shapes are hidden when set to true." );
addField( "ShowPlacementArea", TypeBool, Offset( mFieldData.mShowPlacementArea, fxShapeReplicator ), "Draw placement rings when set to true." );
addField( "PlacementAreaHeight", TypeS32, Offset( mFieldData.mPlacementBandHeight, fxShapeReplicator ), "Height of the placement ring in world units." );
addField( "PlacementColour", TypeColorF, Offset( mFieldData.mPlaceAreaColour, fxShapeReplicator ), "Color of the placement ring." );
endGroup( "Debugging" ); // MM: Added Group Footer.
addGroup( "Media" ); // MM: Added Group Header.
addField( "ShapeFile", TypeShapeFilename, Offset( mFieldData.mShapeFile, fxShapeReplicator ), "Filename of shape to replicate." );
endGroup( "Media" ); // MM: Added Group Footer.
addGroup( "Replications" ); // MM: Added Group Header.
addField( "Seed", TypeS32, Offset( mFieldData.mSeed, fxShapeReplicator ), "Random seed for shape placement." );
addField( "ShapeCount", TypeS32, Offset( mFieldData.mShapeCount, fxShapeReplicator ), "Maximum shape instance count." );
addField( "ShapeRetries", TypeS32, Offset( mFieldData.mShapeRetries, fxShapeReplicator ), "Number of times to try placing a shape instance before giving up." );
endGroup( "Replications" ); // MM: Added Group Footer.
addGroup( "Placement Radius" ); // MM: Added Group Header.
addField( "InnerRadiusX", TypeS32, Offset( mFieldData.mInnerRadiusX, fxShapeReplicator ), "Placement area inner radius on the X axis" );
addField( "InnerRadiusY", TypeS32, Offset( mFieldData.mInnerRadiusY, fxShapeReplicator ), "Placement area inner radius on the Y axis" );
addField( "OuterRadiusX", TypeS32, Offset( mFieldData.mOuterRadiusX, fxShapeReplicator ), "Placement area outer radius on the X axis" );
addField( "OuterRadiusY", TypeS32, Offset( mFieldData.mOuterRadiusY, fxShapeReplicator ), "Placement area outer radius on the Y axis" );
endGroup( "Placement Radius" ); // MM: Added Group Footer.
addGroup( "Restraints" ); // MM: Added Group Header.
addField( "AllowOnTerrain", TypeBool, Offset( mFieldData.mAllowOnTerrain, fxShapeReplicator ), "Shapes will be placed on terrain when set." );
addField( "AllowOnInteriors", TypeBool, Offset( mFieldData.mAllowOnInteriors, fxShapeReplicator ), "Shapes will be placed on InteriorInstances when set." );
addField( "AllowOnStatics", TypeBool, Offset( mFieldData.mAllowStatics, fxShapeReplicator ), "Shapes will be placed on Static shapes when set." );
addField( "AllowOnWater", TypeBool, Offset( mFieldData.mAllowOnWater, fxShapeReplicator ), "Shapes will be placed on/under water when set." );
addField( "AllowWaterSurface", TypeBool, Offset( mFieldData.mAllowWaterSurface, fxShapeReplicator ), "Shapes will be placed on water when set. Requires AllowOnWater." );
addField( "AlignToTerrain", TypeBool, Offset( mFieldData.mAlignToTerrain, fxShapeReplicator ), "Align shapes to surface normal when set." );
addField( "Interactions", TypeBool, Offset( mFieldData.mInteractions, fxShapeReplicator ), "Allow physics interactions with shapes." );
addField( "AllowedTerrainSlope", TypeS32, Offset( mFieldData.mAllowedTerrainSlope, fxShapeReplicator ), "Maximum surface angle allowed for shape instances." );
addField( "TerrainAlignment", TypePoint3F, Offset( mFieldData.mTerrainAlignment, fxShapeReplicator ), "Surface normals will be multiplied by these values when AlignToTerrain is enabled." );
endGroup( "Restraints" ); // MM: Added Group Footer.
addGroup( "Object Transforms" ); // MM: Added Group Header.
addField( "ShapeScaleMin", TypePoint3F, Offset( mFieldData.mShapeScaleMin, fxShapeReplicator ), "Minimum shape scale." );
addField( "ShapeScaleMax", TypePoint3F, Offset( mFieldData.mShapeScaleMax, fxShapeReplicator ), "Maximum shape scale." );
addField( "ShapeRotateMin", TypePoint3F, Offset( mFieldData.mShapeRotateMin, fxShapeReplicator ), "Minimum shape rotation angles.");
addField( "ShapeRotateMax", TypePoint3F, Offset( mFieldData.mShapeRotateMax, fxShapeReplicator ), "Maximum shape rotation angles." );
addField( "OffsetZ", TypeS32, Offset( mFieldData.mOffsetZ, fxShapeReplicator ), "Offset shapes by this amount vertically." );
endGroup( "Object Transforms" ); // MM: Added Group Footer.
// Initialise parents' persistent fields.
Parent::initPersistFields();
}
//------------------------------------------------------------------------------
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;
// Don't create shapes if we are hiding replications.
if (mFieldData.mHideReplications) return;
// Cannot continue without shapes!
if (dStrcmp(mFieldData.mShapeFile, "") == 0) 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.clear();
// 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
{
// Get the Replicator Position.
ShapePosition = getPosition();
// Calculate a random offset
HypX = RandomGen.randF(mFieldData.mInnerRadiusX, mFieldData.mOuterRadiusX);
HypY = RandomGen.randF(mFieldData.mInnerRadiusY, mFieldData.mOuterRadiusY);
Angle = RandomGen.randF(0, (F32)M_2PI);
// Calcualte the new position.
ShapePosition.x += HypX * mCos(Angle);
ShapePosition.y += HypY * mSin(Angle);
// 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 & StaticShapeObjectType) && !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.
ShapeScale.set( RandomGen.randF(mFieldData.mShapeScaleMin.x, mFieldData.mShapeScaleMax.x),
RandomGen.randF(mFieldData.mShapeScaleMin.y, mFieldData.mShapeScaleMax.y),
RandomGen.randF(mFieldData.mShapeScaleMin.z, mFieldData.mShapeScaleMax.z));
// Set Shape Scale.
fxStatic->setScale(ShapeScale);
// Lock it.
fxStatic->setLocked(true);
// Store Shape in Replicated Shapes Vector.
//mReplicatedShapes[mCurrentShapeCount++] = fxStatic;
mReplicatedShapes.push_back(fxStatic);
}
mCurrentShapeCount = mReplicatedShapes.size();
// Take first Timestamp.
mLastRenderTime = Platform::getVirtualMilliseconds();
}
//------------------------------------------------------------------------------
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.clear();
// 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)
{
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.minExtents.set( -0.5, -0.5, -0.5 );
mObjBox.maxExtents.set( 0.5, 0.5, 0.5 );
resetWorldBox();
// Add to Scene.
setRenderTransform(mObjToWorld);
addToScene();
// Register for notification when GhostAlways objects are done loading
NetConnection::smGhostAlwaysDone.notify( this, &fxShapeReplicator::onGhostAlwaysDone );
return true;
}
//------------------------------------------------------------------------------
void fxShapeReplicator::onRemove()
{
// Remove the Replicator from the Replicator Set.
dynamic_cast<SimSet*>(Sim::findObject("fxReplicatorSet"))->removeObject(this);
NetConnection::smGhostAlwaysDone.remove( this, &fxShapeReplicator::onGhostAlwaysDone );
removeFromScene();
// Destroy Shapes.
DestroyShapes();
// Do Parent.
Parent::onRemove();
}
//------------------------------------------------------------------------------
void fxShapeReplicator::onGhostAlwaysDone()
{
RenewShapes();
}
//------------------------------------------------------------------------------
void fxShapeReplicator::inspectPostApply()
{
// Set Parent.
Parent::inspectPostApply();
// Renew Shapes.
RenewShapes();
// Set Replication Mask.
setMaskBits(ReplicationMask);
}
//------------------------------------------------------------------------------
DefineEngineFunction(StartClientReplication, void, (),, "Activates the shape replicator.\n"
"@tsexample\n"
"// Call the function\n"
"StartClientReplication()\n"
"@endtsexample\n"
"@ingroup Foliage"
)
{
// 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!");
}
//------------------------------------------------------------------------------
void fxShapeReplicator::prepRenderImage( SceneRenderState* state )
{
ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
ri->renderDelegate.bind(this, &fxShapeReplicator::renderObject);
// The fxShapeReplicator isn't technically foliage but our debug
// effect seems to render best as a Foliage type (translucent,
// renders itself, no sorting)
ri->type = RenderPassManager::RIT_Foliage;
state->getRenderPass()->addInst( ri );
}
//------------------------------------------------------------------------------
// Renders a triangle stripped oval
void fxShapeReplicator::renderArc(const F32 fRadiusX, const F32 fRadiusY)
{
PrimBuild::begin(GFXTriangleStrip, 720);
for (U32 Angle = mCreationAreaAngle; Angle < (mCreationAreaAngle+360); Angle++)
{
F32 XPos, YPos;
// Calculate Position.
XPos = fRadiusX * mCos(mDegToRad(-(F32)Angle));
YPos = fRadiusY * mSin(mDegToRad(-(F32)Angle));
// Set Colour.
PrimBuild::color4f(mFieldData.mPlaceAreaColour.red,
mFieldData.mPlaceAreaColour.green,
mFieldData.mPlaceAreaColour.blue,
AREA_ANIMATION_ARC * (Angle-mCreationAreaAngle));
PrimBuild::vertex3f(XPos, YPos, -(F32)mFieldData.mPlacementBandHeight/2.0f);
PrimBuild::vertex3f(XPos, YPos, +(F32)mFieldData.mPlacementBandHeight/2.0f);
}
PrimBuild::end();
}
// This currently uses the primbuilder, could convert out, but why allocate the buffer if we
// never edit the misison?
void fxShapeReplicator::renderPlacementArea(const F32 ElapsedTime)
{
if (gEditingMission && mFieldData.mShowPlacementArea)
{
GFX->pushWorldMatrix();
GFX->multWorld(getTransform());
if (!mPlacementSB)
{
GFXStateBlockDesc transparent;
transparent.setCullMode(GFXCullNone);
transparent.alphaTestEnable = true;
transparent.setZReadWrite(true);
transparent.zWriteEnable = false;
transparent.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
mPlacementSB = GFX->createStateBlock( transparent );
}
GFX->setStateBlock(mPlacementSB);
// Do we need to draw the Outer Radius?
if (mFieldData.mOuterRadiusX || mFieldData.mOuterRadiusY)
renderArc((F32) mFieldData.mOuterRadiusX, (F32) mFieldData.mOuterRadiusY);
// Inner radius?
if (mFieldData.mInnerRadiusX || mFieldData.mInnerRadiusY)
renderArc((F32) mFieldData.mInnerRadiusX, (F32) mFieldData.mInnerRadiusY);
GFX->popWorldMatrix();
mCreationAreaAngle = (U32)(mCreationAreaAngle + (1000 * ElapsedTime));
mCreationAreaAngle = mCreationAreaAngle % 360;
}
}
//------------------------------------------------------------------------------
void fxShapeReplicator::renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat)
{
if (overrideMat)
return;
// Return if placement area not needed.
if (!mFieldData.mShowPlacementArea)
return;
// Calculate Elapsed Time and take new Timestamp.
S32 Time = Platform::getVirtualMilliseconds();
F32 ElapsedTime = (Time - mLastRenderTime) * 0.001f;
mLastRenderTime = Time;
renderPlacementArea(ElapsedTime);
}
//------------------------------------------------------------------------------
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);
RenewShapes();
}
}

View file

@ -0,0 +1,196 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _SHAPEREPLICATOR_H_
#define _SHAPEREPLICATOR_H_
#ifndef _TSSTATIC_H_
#include "T3D/tsStatic.h"
#endif
#ifndef _TSSHAPEINSTANCE_H_
#include "ts/tsShapeInstance.h"
#endif
#ifndef _RENDERPASSMANAGER_H_
#include "renderInstance/renderPassManager.h"
#endif
#define AREA_ANIMATION_ARC (1.0f / 360.0f)
#define FXREPLICATOR_COLLISION_MASK ( TerrainObjectType | \
InteriorObjectType | \
StaticShapeObjectType | \
WaterObjectType )
#define FXREPLICATOR_NOWATER_COLLISION_MASK ( TerrainObjectType | \
InteriorObjectType | \
StaticShapeObjectType )
//------------------------------------------------------------------------------
// 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;
U32 mCurrentShapeCount;
Vector<fxShapeReplicatedStatic*> mReplicatedShapes;
MRandomLCG RandomGen;
S32 mLastRenderTime;
public:
fxShapeReplicator();
~fxShapeReplicator();
void StartUp(void);
void ShowReplication(void);
void HideReplication(void);
GFXStateBlockRef mPlacementSB;
void renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance*);
void renderArc(const F32 fRadiusX, const F32 fRadiusY);
void renderPlacementArea(const F32 ElapsedTime);
// SceneObject
virtual void prepRenderImage( SceneRenderState *state );
// SimObject
bool onAdd();
void onRemove();
void inspectPostApply();
// NetObject
U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);
void unpackUpdate(NetConnection *conn, BitStream *stream);
// Editor
void onGhostAlwaysDone();
// ConObject.
static void initPersistFields();
// Field Data.
class tagFieldData
{
public:
U32 mSeed;
StringTableEntry mShapeFile;
U32 mShapeCount;
U32 mShapeRetries;
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);
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_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,391 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GROUNDCOVER_H_
#define _GROUNDCOVER_H_
#ifndef _SCENEOBJECT_H_
#include "scene/sceneObject.h"
#endif
#ifndef _MATHUTIL_FRUSTUM_H_
#include "math/util/frustum.h"
#endif
#ifndef _GFXTEXTUREHANDLE_H_
#include "gfx/gfxTextureHandle.h"
#endif
#ifndef _GFX_GFXPRIMITIVEBUFFER_H_
#include "gfx/gfxPrimitiveBuffer.h"
#endif
#ifndef _RENDERPASSMANAGER_H_
#include "renderInstance/renderPassManager.h"
#endif
#ifndef _MATTEXTURETARGET_H_
#include "materials/matTextureTarget.h"
#endif
#ifndef _SHADERFEATURE_H_
#include "shaderGen/shaderFeature.h"
#endif
class TerrainBlock;
class GroundCoverCell;
class TSShapeInstance;
class Material;
class MaterialParameters;
class MaterialParameterHandle;
///
#define MAX_COVERTYPES 8
GFXDeclareVertexFormat( GCVertex )
{
Point3F point;
Point3F normal;
// .rgb = ambient
// .a = corner index
GFXVertexColor ambient;
// .x = size x
// .y = size y
// .z = type
// .w = wind amplitude
Point4F params;
};
struct GroundCoverShaderConstData
{
Point2F fadeInfo;
Point3F gustInfo;
Point2F turbInfo;
Point3F camRight;
Point3F camUp;
};
class GroundCover;
class GroundCoverShaderConstHandles : public ShaderFeatureConstHandles
{
public:
GroundCoverShaderConstHandles();
virtual void init( GFXShader *shader );
virtual void setConsts( SceneRenderState *state,
const SceneData &sgData,
GFXShaderConstBuffer *buffer );
GroundCover *mGroundCover;
GFXShaderConstHandle *mTypeRectsSC;
GFXShaderConstHandle *mFadeSC;
GFXShaderConstHandle *mWindDirSC;
GFXShaderConstHandle *mGustInfoSC;
GFXShaderConstHandle *mTurbInfoSC;
GFXShaderConstHandle *mCamRightSC;
GFXShaderConstHandle *mCamUpSC;
};
class GroundCover : public SceneObject
{
friend class GroundCoverShaderConstHandles;
friend class GroundCoverCell;
typedef SceneObject Parent;
public:
GroundCover();
~GroundCover();
DECLARE_CONOBJECT(GroundCover);
static void consoleInit();
static void initPersistFields();
bool onAdd();
void onRemove();
void inspectPostApply();
// Network
U32 packUpdate( NetConnection *, U32 mask, BitStream *stream );
void unpackUpdate( NetConnection *, BitStream *stream );
// Rendering
void prepRenderImage( SceneRenderState *state );
// Editor
void onTerrainUpdated( U32 flags, TerrainBlock *tblock, const Point2I& min, const Point2I& max );
// Misc
const GroundCoverShaderConstData& getShaderConstData() const { return mShaderConstData; }
/// Sets the global ground cover LOD scalar which controls
/// the percentage of the maximum designed cover to put down.
/// It scales both rendering cost and placement CPU performance.
/// Returns the actual value set.
static F32 setQualityScale( F32 scale ) { return smDensityScale = mClampF( scale, 0.0f, 1.0f ); }
/// Returns the current quality scale... see above.
static F32 getQualityScale() { return smDensityScale; }
protected:
enum MaskBits
{
TerrainBlockMask = Parent::NextFreeMask << 0,
NextFreeMask = Parent::NextFreeMask << 1
};
MaterialParameters *mMatParams;
MaterialParameterHandle *mTypeRectsParam;
MaterialParameterHandle *mFadeParams;
MaterialParameterHandle *mWindDirParam;
MaterialParameterHandle *mGustInfoParam;
MaterialParameterHandle *mTurbInfoParam;
MaterialParameterHandle *mCamRightParam;
MaterialParameterHandle *mCamUpParam;
/// This RNG seed is saved and sent to clients
/// for generating the same cover.
S32 mRandomSeed;
/// This is the outer generation radius from
/// the current camera position.
F32 mRadius;
// Offset along the Z axis to render the ground cover.
F32 mZOffset;
/// This is less than or equal to mRadius and
/// defines when fading of cover elements begins.
F32 mFadeRadius;
/// This is the distance at which DTS elements are
/// completely culled out.
F32 mShapeCullRadius;
/// Whether shapes rendered by the GroundCover should cast shadows.
bool mShapesCastShadows;
/// This is used to scale the various culling radii
/// when rendering a reflection... typically for water.
F32 mReflectRadiusScale;
/// This is the number of cells per axis in the grid.
U32 mGridSize;
typedef Vector<GroundCoverCell*> CellVector;
/// This is the allocator for GridCell chunks.
CellVector mAllocCellList;
CellVector mFreeCellList;
/// This is the grid of active cells.
CellVector mCellGrid;
/// This is a scratch grid used while updating
/// the cell grid.
CellVector mScratchGrid;
/// This is the index to the first grid cell.
Point2I mGridIndex;
/// The maximum amount of cover elements to include in
/// the grid at any one time. The actual amount may be
/// less than this based on randomization.
S32 mMaxPlacement;
/// Used to detect changes in cell placement count from
/// the global quality scale so we can regen the cells.
S32 mLastPlacementCount;
/// Used for culling cells to update and render.
Frustum mCuller;
/// Debug parameter for displaying the grid cells.
bool mDebugRenderCells;
/// Debug parameter for turning off billboard rendering.
bool mDebugNoBillboards;
/// Debug parameter for turning off shape rendering.
bool mDebugNoShapes;
/// Debug parameter for locking the culling frustum which
/// will freeze the cover generation.
bool mDebugLockFrustum;
/// Stat for number of rendered cells.
static U32 smStatRenderedCells;
/// Stat for number of rendered billboards.
static U32 smStatRenderedBillboards;
/// Stat for number of rendered billboard batches.
static U32 smStatRenderedBatches;
/// Stat for number of rendered shapes.
static U32 smStatRenderedShapes;
/// The global ground cover LOD scalar which controls
/// the percentage of the maximum amount of cover to put
/// down. It scales both rendering cost and placement
/// CPU performance.
static F32 smDensityScale;
String mMaterialName;
Material *mMaterial;
BaseMatInstance *mMatInst;
GroundCoverShaderConstData mShaderConstData;
/// This is the maximum amout of degrees the billboard will
/// tilt down to match the camera.
F32 mMaxBillboardTiltAngle;
/// The probability of one cover type verses another.
F32 mProbability[MAX_COVERTYPES];
/// The minimum random size for each cover type.
F32 mSizeMin[MAX_COVERTYPES];
/// The maximum random size of this cover type.
F32 mSizeMax[MAX_COVERTYPES];
/// An exponent used to bias between the minimum
/// and maximum random sizes.
F32 mSizeExponent[MAX_COVERTYPES];
/// The wind effect scale.
F32 mWindScale[MAX_COVERTYPES];
/// The maximum slope angle in degrees for placement.
F32 mMaxSlope[MAX_COVERTYPES];
/// The minimum world space elevation for placement.
F32 mMinElevation[MAX_COVERTYPES];
/// The maximum world space elevation for placement.
F32 mMaxElevation[MAX_COVERTYPES];
/// Terrain material name to limit coverage to, or
/// left empty to cover entire terrain.
StringTableEntry mLayer[MAX_COVERTYPES];
/// Inverts the data layer test making the
/// layer an exclusion mask.
bool mInvertLayer[MAX_COVERTYPES];
/// The minimum amount of elements in a clump.
S32 mMinClumpCount[MAX_COVERTYPES];
/// The maximum amount of elements in a clump.
S32 mMaxClumpCount[MAX_COVERTYPES];
/// An exponent used to bias between the minimum
/// and maximum clump counts for a particular clump.
F32 mClumpCountExponent[MAX_COVERTYPES];
/// The maximum clump radius.
F32 mClumpRadius[MAX_COVERTYPES];
/// This is a cached array of billboard aspect scales
/// used to avoid some calculations when generating cells.
F32 mBillboardAspectScales[MAX_COVERTYPES];
RectF mBillboardRects[MAX_COVERTYPES];
/// The cover shape filenames.
StringTableEntry mShapeFilenames[MAX_COVERTYPES];
/// The cover shape instances.
TSShapeInstance* mShapeInstances[MAX_COVERTYPES];
/// This is the same as mProbability, but normalized for use
/// during the cover placement process.
F32 mNormalizedProbability[MAX_COVERTYPES];
/// A shared primitive buffer setup for drawing the maximum amount
/// of billboards you could possibly have in a single cell.
GFXPrimitiveBufferHandle mPrimBuffer;
/// The length in meters between peaks in the wind gust.
F32 mWindGustLength;
/// Controls how often the wind gust peaks per second.
F32 mWindGustFrequency;
/// The maximum distance in meters that the peak wind
/// gust will displace an element.
F32 mWindGustStrength;
/// The direction of the wind.
Point2F mWindDirection;
/// Controls the overall rapidity of the wind turbulence.
F32 mWindTurbulenceFrequency;
/// The maximum distance in meters that the turbulence can
/// displace a ground cover element.
F32 mWindTurbulenceStrength;
void _initMaterial();
bool _initShader();
void _initShapes();
void _deleteShapes();
/// Called when GroundCover parameters are changed and
/// things need to be reinitialized to continue.
void _initialize( U32 cellCount, U32 cellPlacementCount );
/// Updates the cover grid by removing cells that
/// have fallen outside of mRadius and adding new
/// ones that have come into view.
void _updateCoverGrid( const Frustum &culler );
/// Clears the cell grid, moves all the allocated cells to
/// the free list, and deletes excess free cells.
void _freeCells();
/// Clears the cell grid and deletes all the free cells.
void _deleteCells();
/// Returns a cell to the free list.
void _recycleCell( GroundCoverCell* cell );
/// Generates a new cell using the recycle list when possible.
GroundCoverCell* _generateCell( const Point2I& index,
const Box3F& bounds,
U32 placementCount,
S32 randSeed );
void _debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat );
};
#endif // _GROUNDCOVER_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,241 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _LIGHTNING_H_
#define _LIGHTNING_H_
#ifndef _GAMEBASE_H_
#include "T3D/gameBase/gameBase.h"
#endif
#ifndef _TORQUE_LIST_
#include "core/util/tList.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
#ifndef _RENDERPASSMANAGER_H_
#include "renderInstance/renderPassManager.h"
#endif
#ifndef _ENGINEAPI_H_
#include "console/engineAPI.h"
#endif
#include "gfx/gfxTextureHandle.h"
class ShapeBase;
class LightningStrikeEvent;
class SFXTrack;
// -------------------------------------------------------------------------
class LightningData : public GameBaseData
{
typedef GameBaseData Parent;
public:
enum Constants {
MaxThunders = 8,
MaxTextures = 8
};
//-------------------------------------- Console set variables
public:
SFXTrack* thunderSounds[MaxThunders];
SFXTrack* strikeSound;
StringTableEntry strikeTextureNames[MaxTextures];
//-------------------------------------- load set variables
public:
GFXTexHandle strikeTextures[MaxTextures];
U32 numThunders;
protected:
bool onAdd();
public:
LightningData();
~LightningData();
void packData(BitStream*);
void unpackData(BitStream*);
bool preload(bool server, String &errorStr);
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;
typedef Torque::List<LightningBolt> LightingBoltList;
LightingBoltList 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( const Point3F &startPoint, const Point3F &endPoint, U32 depth, F32 width );
F32 findHeight( Point3F &point, SceneManager* 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, bool reload );
DECLARE_CALLBACK( void, applyDamage, ( const Point3F& hitPosition, const Point3F& hitNormal, SceneObject* hitObject ));
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;
GFXStateBlockRef mLightningSB;
protected:
// Rendering
void prepRenderImage(SceneRenderState *state);
void renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* );
// 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 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

View file

@ -0,0 +1,633 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "particle.h"
#include "console/consoleTypes.h"
#include "core/stream/bitStream.h"
#include "math/mRandom.h"
#include "math/mathIO.h"
#include "console/engineAPI.h"
IMPLEMENT_CO_DATABLOCK_V1( ParticleData );
ConsoleDocClass( ParticleData,
"@brief Contains information for how specific particles should look and react "
"including particle colors, particle imagemap, acceleration value for individual "
"particles and spin information.\n"
"@tsexample\n"
"datablock ParticleData( GLWaterExpSmoke )\n"
"{\n"
" textureName = \"art/shapes/particles/smoke\";\n"
" dragCoefficient = 0.4;\n"
" gravityCoefficient = -0.25;\n"
" inheritedVelFactor = 0.025;\n"
" constantAcceleration = -1.1;\n"
" lifetimeMS = 1250;\n"
" lifetimeVarianceMS = 0;\n"
" useInvAlpha = false;\n"
" spinSpeed = 1;\n"
" spinRandomMin = -200.0;\n"
" spinRandomMax = 200.0;\n\n"
" colors[0] = \"0.1 0.1 1.0 1.0\";\n"
" colors[1] = \"0.4 0.4 1.0 1.0\";\n"
" colors[2] = \"0.4 0.4 1.0 0.0\";\n\n"
" sizes[0] = 2.0;\n"
" sizes[1] = 6.0;\n"
" sizes[2] = 2.0;\n\n"
" times[0] = 0.0;\n"
" times[1] = 0.5;\n"
" times[2] = 1.0;\n"
"};\n"
"@endtsexample\n"
"@ingroup FX\n"
"@see ParticleEmitter\n"
"@see ParticleEmitterData\n"
"@see ParticleEmitterNode\n"
);
static const float sgDefaultWindCoefficient = 0.0f;
static const float sgDefaultConstantAcceleration = 0.f;
static const float sgDefaultSpinSpeed = 1.f;
static const float sgDefaultSpinRandomMin = 0.f;
static const float sgDefaultSpinRandomMax = 0.f;
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
ParticleData::ParticleData()
{
dragCoefficient = 0.0f;
windCoefficient = sgDefaultWindCoefficient;
gravityCoefficient = 0.0f;
inheritedVelFactor = 0.0f;
constantAcceleration = sgDefaultConstantAcceleration;
lifetimeMS = 1000;
lifetimeVarianceMS = 0;
spinSpeed = sgDefaultSpinSpeed;
spinRandomMin = sgDefaultSpinRandomMin;
spinRandomMax = sgDefaultSpinRandomMax;
useInvAlpha = false;
animateTexture = false;
numFrames = 1;
framesPerSec = numFrames;
S32 i;
for( i=0; i<PDC_NUM_KEYS; i++ )
{
colors[i].set( 1.0, 1.0, 1.0, 1.0 );
sizes[i] = 1.0;
}
times[0] = 0.0f;
times[1] = 0.33f;
times[2] = 0.66f;
times[3] = 1.0f;
texCoords[0].set(0.0,0.0); // texture coords at 4 corners
texCoords[1].set(0.0,1.0); // of particle quad
texCoords[2].set(1.0,1.0); // (defaults to entire particle)
texCoords[3].set(1.0,0.0);
animTexTiling.set(0,0); // tiling dimensions
animTexFramesString = NULL; // string of animation frame indices
animTexUVs = NULL; // array of tile vertex UVs
textureName = NULL; // texture filename
textureHandle = NULL; // loaded texture handle
}
//-----------------------------------------------------------------------------
// Destructor
//-----------------------------------------------------------------------------
ParticleData::~ParticleData()
{
if (animTexUVs)
{
delete [] animTexUVs;
}
}
//-----------------------------------------------------------------------------
// initPersistFields
//-----------------------------------------------------------------------------
void ParticleData::initPersistFields()
{
addField( "dragCoefficient", TYPEID< F32 >(), Offset(dragCoefficient, ParticleData),
"Particle physics drag amount." );
addField( "windCoefficient", TYPEID< F32 >(), Offset(windCoefficient, ParticleData),
"Strength of wind on the particles." );
addField( "gravityCoefficient", TYPEID< F32 >(), Offset(gravityCoefficient, ParticleData),
"Strength of gravity on the particles." );
addField( "inheritedVelFactor", TYPEID< F32 >(), Offset(inheritedVelFactor, ParticleData),
"Amount of emitter velocity to add to particle initial velocity." );
addField( "constantAcceleration", TYPEID< F32 >(), Offset(constantAcceleration, ParticleData),
"Constant acceleration to apply to this particle." );
addField( "lifetimeMS", TYPEID< S32 >(), Offset(lifetimeMS, ParticleData),
"Time in milliseconds before this particle is destroyed." );
addField( "lifetimeVarianceMS", TYPEID< S32 >(), Offset(lifetimeVarianceMS, ParticleData),
"Variance in lifetime of particle, from 0 - lifetimeMS." );
addField( "spinSpeed", TYPEID< F32 >(), Offset(spinSpeed, ParticleData),
"Speed at which to spin the particle." );
addField( "spinRandomMin", TYPEID< F32 >(), Offset(spinRandomMin, ParticleData),
"Minimum allowed spin speed of this particle, between -10000 and spinRandomMax." );
addField( "spinRandomMax", TYPEID< F32 >(), Offset(spinRandomMax, ParticleData),
"Maximum allowed spin speed of this particle, between spinRandomMin and 10000." );
addField( "useInvAlpha", TYPEID< bool >(), Offset(useInvAlpha, ParticleData),
"@brief Controls how particles blend with the scene.\n\n"
"If true, particles blend like ParticleBlendStyle NORMAL, if false, "
"blend like ParticleBlendStyle ADDITIVE.\n"
"@note If ParticleEmitterData::blendStyle is set, it will override this value." );
addField( "animateTexture", TYPEID< bool >(), Offset(animateTexture, ParticleData),
"If true, allow the particle texture to be an animated sprite." );
addField( "framesPerSec", TYPEID< S32 >(), Offset(framesPerSec, ParticleData),
"If animateTexture is true, this defines the frames per second of the "
"sprite animation." );
addField( "textureCoords", TYPEID< Point2F >(), Offset(texCoords, ParticleData), 4,
"@brief 4 element array defining the UV coords into textureName to use "
"for this particle.\n\n"
"Coords should be set for the first tile only when using animTexTiling; "
"coordinates for other tiles will be calculated automatically. \"0 0\" is "
"top left and \"1 1\" is bottom right." );
addField( "animTexTiling", TYPEID< Point2I >(), Offset(animTexTiling, ParticleData),
"@brief The number of frames, in rows and columns stored in textureName "
"(when animateTexture is true).\n\n"
"A maximum of 256 frames can be stored in a single texture when using "
"animTexTiling. Value should be \"NumColumns NumRows\", for example \"4 4\"." );
addField( "animTexFrames", TYPEID< StringTableEntry >(), Offset(animTexFramesString,ParticleData),
"@brief A list of frames and/or frame ranges to use for particle "
"animation if animateTexture is true.\n\n"
"Each frame token must be separated by whitespace. A frame token must be "
"a positive integer frame number or a range of frame numbers separated "
"with a '-'. The range separator, '-', cannot have any whitspace around "
"it.\n\n"
"Ranges can be specified to move through the frames in reverse as well "
"as forward (eg. 19-14). Frame numbers exceeding the number of tiles will "
"wrap.\n"
"@tsexample\n"
"animTexFrames = \"0-16 20 19 18 17 31-21\";\n"
"@endtsexample\n" );
addField( "textureName", TYPEID< StringTableEntry >(), Offset(textureName, ParticleData),
"Texture file to use for this particle." );
addField( "animTexName", TYPEID< StringTableEntry >(), Offset(textureName, ParticleData),
"@brief Texture file to use for this particle if animateTexture is true.\n\n"
"Deprecated. Use textureName instead." );
// Interpolation variables
addField( "colors", TYPEID< ColorF >(), Offset(colors, ParticleData), PDC_NUM_KEYS,
"@brief Particle RGBA color keyframe values.\n\n"
"The particle color will linearly interpolate between the color/time keys "
"over the lifetime of the particle." );
addField( "sizes", TYPEID< F32 >(), Offset(sizes, ParticleData), PDC_NUM_KEYS,
"@brief Particle size keyframe values.\n\n"
"The particle size will linearly interpolate between the size/time keys "
"over the lifetime of the particle." );
addProtectedField( "times", TYPEID< F32 >(), Offset(times, ParticleData), &protectedSetTimes,
&defaultProtectedGetFn, PDC_NUM_KEYS,
"@brief Time keys used with the colors and sizes keyframes.\n\n"
"Values are from 0.0 (particle creation) to 1.0 (end of lifespace)." );
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
// Pack data
//-----------------------------------------------------------------------------
void ParticleData::packData(BitStream* stream)
{
Parent::packData(stream);
stream->writeFloat(dragCoefficient / 5, 10);
if( stream->writeFlag(windCoefficient != sgDefaultWindCoefficient ) )
stream->write(windCoefficient);
if (stream->writeFlag(gravityCoefficient != 0.0f))
stream->writeSignedFloat(gravityCoefficient / 10, 12);
stream->writeFloat(inheritedVelFactor, 9);
if( stream->writeFlag( constantAcceleration != sgDefaultConstantAcceleration ) )
stream->write(constantAcceleration);
stream->write( lifetimeMS );
stream->write( lifetimeVarianceMS );
if( stream->writeFlag( spinSpeed != sgDefaultSpinSpeed ) )
stream->write(spinSpeed);
if(stream->writeFlag(spinRandomMin != sgDefaultSpinRandomMin || spinRandomMax != sgDefaultSpinRandomMax))
{
stream->writeInt((S32)(spinRandomMin + 1000), 11);
stream->writeInt((S32)(spinRandomMax + 1000), 11);
}
stream->writeFlag(useInvAlpha);
S32 i, count;
// see how many frames there are:
for(count = 0; count < 3; count++)
if(times[count] >= 1)
break;
count++;
stream->writeInt(count-1, 2);
for( i=0; i<count; i++ )
{
stream->writeFloat( colors[i].red, 7);
stream->writeFloat( colors[i].green, 7);
stream->writeFloat( colors[i].blue, 7);
stream->writeFloat( colors[i].alpha, 7);
stream->writeFloat( sizes[i]/MaxParticleSize, 14);
stream->writeFloat( times[i], 8);
}
if (stream->writeFlag(textureName && textureName[0]))
stream->writeString(textureName);
for (i = 0; i < 4; i++)
mathWrite(*stream, texCoords[i]);
if (stream->writeFlag(animateTexture))
{
if (stream->writeFlag(animTexFramesString && animTexFramesString[0]))
{
stream->writeString(animTexFramesString);
}
mathWrite(*stream, animTexTiling);
stream->writeInt(framesPerSec, 8);
}
}
//-----------------------------------------------------------------------------
// Unpack data
//-----------------------------------------------------------------------------
void ParticleData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
dragCoefficient = stream->readFloat(10) * 5;
if(stream->readFlag())
stream->read(&windCoefficient);
else
windCoefficient = sgDefaultWindCoefficient;
if (stream->readFlag())
gravityCoefficient = stream->readSignedFloat(12)*10;
else
gravityCoefficient = 0.0f;
inheritedVelFactor = stream->readFloat(9);
if(stream->readFlag())
stream->read(&constantAcceleration);
else
constantAcceleration = sgDefaultConstantAcceleration;
stream->read( &lifetimeMS );
stream->read( &lifetimeVarianceMS );
if(stream->readFlag())
stream->read(&spinSpeed);
else
spinSpeed = sgDefaultSpinSpeed;
if(stream->readFlag())
{
spinRandomMin = (F32)(stream->readInt(11) - 1000);
spinRandomMax = (F32)(stream->readInt(11) - 1000);
}
else
{
spinRandomMin = sgDefaultSpinRandomMin;
spinRandomMax = sgDefaultSpinRandomMax;
}
useInvAlpha = stream->readFlag();
S32 i;
S32 count = stream->readInt(2) + 1;
for(i = 0;i < count; i++)
{
colors[i].red = stream->readFloat(7);
colors[i].green = stream->readFloat(7);
colors[i].blue = stream->readFloat(7);
colors[i].alpha = stream->readFloat(7);
sizes[i] = stream->readFloat(14) * MaxParticleSize;
times[i] = stream->readFloat(8);
}
textureName = (stream->readFlag()) ? stream->readSTString() : 0;
for (i = 0; i < 4; i++)
mathRead(*stream, &texCoords[i]);
animateTexture = stream->readFlag();
if (animateTexture)
{
animTexFramesString = (stream->readFlag()) ? stream->readSTString() : 0;
mathRead(*stream, &animTexTiling);
framesPerSec = stream->readInt(8);
}
}
bool ParticleData::protectedSetTimes( void *object, const char *index, const char *data)
{
ParticleData *pData = static_cast<ParticleData*>( object );
F32 val = dAtof(data);
U32 i;
if (!index)
i = 0;
else
i = dAtoui(index);
pData->times[i] = mClampF( val, 0.f, 1.f );
return true;
}
//-----------------------------------------------------------------------------
// onAdd
//-----------------------------------------------------------------------------
bool ParticleData::onAdd()
{
if (Parent::onAdd() == false)
return false;
if (dragCoefficient < 0.0) {
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) drag coeff less than 0", getName());
dragCoefficient = 0.0f;
}
if (lifetimeMS < 1) {
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) lifetime < 1 ms", getName());
lifetimeMS = 1;
}
if (lifetimeVarianceMS >= lifetimeMS) {
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) lifetimeVariance >= lifetime", getName());
lifetimeVarianceMS = lifetimeMS - 1;
}
if (spinSpeed > 10000.0 || spinSpeed < -10000.0) {
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinSpeed invalid", getName());
return false;
}
if (spinRandomMin > 10000.0 || spinRandomMin < -10000.0) {
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinRandomMin invalid", getName());
spinRandomMin = -360.0;
return false;
}
if (spinRandomMin > spinRandomMax) {
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinRandomMin greater than spinRandomMax", getName());
spinRandomMin = spinRandomMax - (spinRandomMin - spinRandomMax );
return false;
}
if (spinRandomMax > 10000.0 || spinRandomMax < -10000.0) {
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinRandomMax invalid", getName());
spinRandomMax = 360.0;
return false;
}
if (framesPerSec > 255)
{
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) framesPerSec > 255, too high", getName());
framesPerSec = 255;
return false;
}
times[0] = 0.0f;
for (U32 i = 1; i < 4; i++) {
if (times[i] < times[i-1]) {
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) times[%d] < times[%d]", getName(), i, i-1);
times[i] = times[i-1];
}
}
// Here we validate parameters
if (animateTexture)
{
// Tiling dimensions must be positive and non-zero
if (animTexTiling.x <= 0 || animTexTiling.y <= 0)
{
Con::warnf(ConsoleLogEntry::General,
"ParticleData(%s) bad value(s) for animTexTiling [%d or %d <= 0], invalid datablock",
animTexTiling.x, animTexTiling.y, getName());
return false;
}
// Indices must fit into a byte so these are also bad
if (animTexTiling.x * animTexTiling.y > 256)
{
Con::warnf(ConsoleLogEntry::General,
"ParticleData(%s) bad values for animTexTiling [%d*%d > %d], invalid datablock",
animTexTiling.x, animTexTiling.y, 256, getName());
return false;
}
// A list of frames is required
if (!animTexFramesString || !animTexFramesString[0])
{
Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) no animTexFrames, invalid datablock", getName());
return false;
}
// The frame list cannot be too long.
if (animTexFramesString && dStrlen(animTexFramesString) > 255)
{
Con::errorf(ConsoleLogEntry::General, "ParticleData(%s) animTexFrames string too long [> 255 chars]", getName());
return false;
}
}
return true;
}
//-----------------------------------------------------------------------------
// preload
//-----------------------------------------------------------------------------
bool ParticleData::preload(bool server, String &errorStr)
{
if (Parent::preload(server, errorStr) == false)
return false;
bool error = false;
if(!server)
{
// Here we attempt to load the particle's texture if specified. An undefined
// texture is *not* an error since the emitter may provide one.
if (textureName && textureName[0])
{
textureHandle = GFXTexHandle(textureName, &GFXDefaultStaticDiffuseProfile, avar("%s() - textureHandle (line %d)", __FUNCTION__, __LINE__));
if (!textureHandle)
{
errorStr = String::ToString("Missing particle texture: %s", textureName);
error = true;
}
}
if (animateTexture)
{
// Here we parse animTexFramesString into byte-size frame numbers in animTexFrames.
// Each frame token must be separated by whitespace.
// A frame token must be a positive integer frame number or a range of frame numbers
// separated with a '-'.
// The range separator, '-', cannot have any whitspace around it.
// Ranges can be specified to move through the frames in reverse as well as forward.
// Frame numbers exceeding the number of tiles will wrap.
// example:
// "0-16 20 19 18 17 31-21"
S32 n_tiles = animTexTiling.x * animTexTiling.y;
AssertFatal(n_tiles > 0 && n_tiles <= 256, "Error, bad animTexTiling setting." );
animTexFrames.clear();
char* tokCopy = new char[dStrlen(animTexFramesString) + 1];
dStrcpy(tokCopy, animTexFramesString);
char* currTok = dStrtok(tokCopy, " \t");
while (currTok != NULL)
{
char* minus = dStrchr(currTok, '-');
if (minus)
{
// add a range of frames
*minus = '\0';
S32 range_a = dAtoi(currTok);
S32 range_b = dAtoi(minus+1);
if (range_b < range_a)
{
// reverse frame range
for (S32 i = range_a; i >= range_b; i--)
animTexFrames.push_back((U8)(i % n_tiles));
}
else
{
// forward frame range
for (S32 i = range_a; i <= range_b; i++)
animTexFrames.push_back((U8)(i % n_tiles));
}
}
else
{
// add one frame
animTexFrames.push_back((U8)(dAtoi(currTok) % n_tiles));
}
currTok = dStrtok(NULL, " \t");
}
// Here we pre-calculate the UVs for each frame tile, which are
// tiled inside the UV region specified by texCoords. Since the
// UVs are calculated using bilinear interpolation, the texCoords
// region does *not* have to be an axis-aligned rectangle.
if (animTexUVs)
delete [] animTexUVs;
animTexUVs = new Point2F[(animTexTiling.x+1)*(animTexTiling.y+1)];
// interpolate points on the left and right edge of the uv quadrangle
Point2F lf_pt = texCoords[0];
Point2F rt_pt = texCoords[3];
// per-row delta for left and right interpolated points
Point2F lf_d = (texCoords[1] - texCoords[0])/(F32)animTexTiling.y;
Point2F rt_d = (texCoords[2] - texCoords[3])/(F32)animTexTiling.y;
S32 idx = 0;
for (S32 yy = 0; yy <= animTexTiling.y; yy++)
{
Point2F p = lf_pt;
Point2F dp = (rt_pt - lf_pt)/(F32)animTexTiling.x;
for (S32 xx = 0; xx <= animTexTiling.x; xx++)
{
animTexUVs[idx++] = p;
p += dp;
}
lf_pt += lf_d;
rt_pt += rt_d;
}
// cleanup
delete [] tokCopy;
numFrames = animTexFrames.size();
}
}
return !error;
}
//-----------------------------------------------------------------------------
// Initialize particle
//-----------------------------------------------------------------------------
void ParticleData::initializeParticle(Particle* init, const Point3F& inheritVelocity)
{
init->dataBlock = this;
// Calculate the constant accleration...
init->vel += inheritVelocity * inheritedVelFactor;
init->acc = init->vel * constantAcceleration;
// Calculate this instance's lifetime...
init->totalLifetime = lifetimeMS;
if (lifetimeVarianceMS != 0)
init->totalLifetime += S32(gRandGen.randI() % (2 * lifetimeVarianceMS + 1)) - S32(lifetimeVarianceMS);
// assign spin amount
init->spinSpeed = spinSpeed * gRandGen.randF( spinRandomMin, spinRandomMax );
}
bool ParticleData::reload(char errorBuffer[256])
{
bool error = false;
if (textureName && textureName[0])
{
textureHandle = GFXTexHandle(textureName, &GFXDefaultStaticDiffuseProfile, avar("%s() - textureHandle (line %d)", __FUNCTION__, __LINE__));
if (!textureHandle)
{
dSprintf(errorBuffer, 256, "Missing particle texture: %s", textureName);
error = true;
}
}
/*
numFrames = 0;
for( int i=0; i<PDC_MAX_TEX; i++ )
{
if( textureNameList[i] && textureNameList[i][0] )
{
textureList[i] = TextureHandle( textureNameList[i], MeshTexture );
if (!textureList[i].getName())
{
dSprintf(errorBuffer, 256, "Missing particle texture: %s", textureNameList[i]);
error = true;
}
numFrames++;
}
}
*/
return !error;
}
DefineEngineMethod(ParticleData, reload, void, (),,
"Reloads this particle.\n"
"@tsexample\n"
"// Get the editor's current particle\n"
"%particle = PE_ParticleEditor.currParticle\n\n"
"// Change a particle value\n"
"%particle.setFieldValue( %propertyField, %value );\n\n"
"// Reload it\n"
"%particle.reload();\n"
"@endtsexample\n" )
{
char errorBuffer[256];
object->reload(errorBuffer);
}

View file

@ -0,0 +1,128 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _PARTICLE_H_
#define _PARTICLE_H_
#ifndef _GAMEBASE_H_
#include "T3D/gameBase/gameBase.h"
#endif
#ifndef _GFXTEXTUREHANDLE_H_
#include "gfx/gfxTextureHandle.h"
#endif
#define MaxParticleSize 50.0
struct Particle;
//*****************************************************************************
// Particle Data
//*****************************************************************************
class ParticleData : public SimDataBlock
{
typedef SimDataBlock Parent;
public:
enum PDConst
{
PDC_NUM_KEYS = 4,
};
F32 dragCoefficient;
F32 windCoefficient;
F32 gravityCoefficient;
F32 inheritedVelFactor;
F32 constantAcceleration;
S32 lifetimeMS;
S32 lifetimeVarianceMS;
F32 spinSpeed; // degrees per second
F32 spinRandomMin;
F32 spinRandomMax;
bool useInvAlpha;
bool animateTexture;
U32 numFrames;
U32 framesPerSec;
ColorF colors[ PDC_NUM_KEYS ];
F32 sizes[ PDC_NUM_KEYS ];
F32 times[ PDC_NUM_KEYS ];
Point2F* animTexUVs;
Point2F texCoords[4]; // default: {{0.0,0.0}, {0.0,1.0}, {1.0,1.0}, {1.0,0.0}}
Point2I animTexTiling;
StringTableEntry animTexFramesString;
Vector<U8> animTexFrames;
StringTableEntry textureName;
GFXTexHandle textureHandle;
static bool protectedSetTimes( void *object, const char *index, const char *data );
public:
ParticleData();
~ParticleData();
// move this procedure to Particle
void initializeParticle(Particle*, const Point3F&);
void packData(BitStream* stream);
void unpackData(BitStream* stream);
bool onAdd();
bool preload(bool server, String &errorStr);
DECLARE_CONOBJECT(ParticleData);
static void initPersistFields();
bool reload(char errorBuffer[256]);
};
//*****************************************************************************
// Particle
//
// This structure should be as small as possible.
//*****************************************************************************
struct Particle
{
Point3F pos; // current instantaneous position
Point3F vel; // " " velocity
Point3F acc; // Constant acceleration
Point3F orientDir; // direction particle should go if using oriented particles
U32 totalLifetime; // Total ms that this instance should be "live"
ParticleData* dataBlock; // datablock that contains global parameters for
// this instance
U32 currentAge;
// are these necessary to store here? - they are interpolated in real time
ColorF color;
F32 size;
F32 spinSpeed;
Particle * next;
};
#endif // _PARTICLE_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,287 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _H_PARTICLE_EMITTER
#define _H_PARTICLE_EMITTER
#ifndef _GAMEBASE_H_
#include "T3D/gameBase/gameBase.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
#ifndef _GFXPRIMITIVEBUFFER_H_
#include "gfx/gfxPrimitiveBuffer.h"
#endif
#ifndef _GFXVERTEXBUFFER_H_
#include "gfx/gfxVertexBuffer.h"
#endif
#ifndef _PARTICLE_H_
#include "T3D/fx/particle.h"
#endif
#if defined(TORQUE_OS_XENON)
#include "gfx/D3D9/360/gfx360MemVertexBuffer.h"
#endif
class RenderPassManager;
class ParticleData;
//*****************************************************************************
// Particle Emitter Data
//*****************************************************************************
class ParticleEmitterData : public GameBaseData
{
typedef GameBaseData Parent;
static bool _setAlignDirection( void *object, const char *index, const char *data );
public:
ParticleEmitterData();
DECLARE_CONOBJECT(ParticleEmitterData);
static void initPersistFields();
void packData(BitStream* stream);
void unpackData(BitStream* stream);
bool preload(bool server, String &errorStr);
bool onAdd();
void allocPrimBuffer( S32 overrideSize = -1 );
public:
S32 ejectionPeriodMS; ///< Time, in Milliseconds, 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
F32 softnessDistance; ///< For soft particles, the distance (in meters) where particles will be faded
///< based on the difference in depth between the particle and the scene geometry.
/// A scalar value used to influence the effect
/// of the ambient color on the particle.
F32 ambientFactor;
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
bool alignParticles; ///< Particles always face along a particular axis
Point3F alignDirection; ///< The direction aligned particles should face
StringTableEntry particleString; ///< Used to load particle data directly from a string
Vector<ParticleData*> particleDataBlocks; ///< Particle Datablocks
Vector<U32> dataBlockIds; ///< Datablock IDs (parellel array to particleDataBlocks)
U32 partListInitSize; /// initial size of particle list calc'd from datablock info
GFXPrimitiveBufferHandle primBuff;
S32 blendStyle; ///< Pre-define blend factor setting
bool sortParticles; ///< Particles are sorted back-to-front
bool reverseOrder; ///< reverses draw order
StringTableEntry textureName; ///< Emitter texture file to override particle textures
GFXTexHandle textureHandle; ///< Emitter texture handle from txrName
bool highResOnly; ///< This particle system should not use the mixed-resolution particle rendering
bool renderReflection; ///< Enables this emitter to render into reflection passes.
bool reload();
};
//*****************************************************************************
// Particle Emitter
//*****************************************************************************
class ParticleEmitter : public GameBase
{
typedef GameBase Parent;
public:
#if defined(TORQUE_OS_XENON)
typedef GFXVertexPCTT ParticleVertexType;
#else
typedef GFXVertexPCT ParticleVertexType;
#endif
ParticleEmitter();
~ParticleEmitter();
DECLARE_CONOBJECT(ParticleEmitter);
static Point3F mWindVelocity;
static void setWindVelocity( const Point3F &vel ){ mWindVelocity = vel; }
ColorF getCollectiveColor();
/// 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, bool reload );
/// 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);
/// @}
bool mDead;
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);
inline void setupBillboard( Particle *part,
Point3F *basePts,
const MatrixF &camView,
const ColorF &ambientColor,
ParticleVertexType *lVerts );
inline void setupOriented( Particle *part,
const Point3F &camPos,
const ColorF &ambientColor,
ParticleVertexType *lVerts );
inline void setupAligned( const Particle *part,
const ColorF &ambientColor,
ParticleVertexType *lVerts );
/// Updates the bounding box for the particle system
void updateBBox();
/// @}
protected:
bool onAdd();
void onRemove();
void processTick(const Move *move);
void advanceTime(F32 dt);
// Rendering
protected:
void prepRenderImage( SceneRenderState *state );
void copyToVB( const Point3F &camPos, const ColorF &ambientColor );
// PEngine interface
private:
void update( U32 ms );
inline void updateKeyData( Particle *part );
private:
/// Constant used to calculate particle
/// rotation from spin and age.
static const F32 AgedSpinToRadians;
ParticleEmitterData* mDataBlock;
U32 mInternalClock;
U32 mNextParticleTime;
Point3F mLastPosition;
bool mHasLastPosition;
MatrixF mBBObjToWorld;
bool mDeleteWhenEmpty;
bool mDeleteOnTick;
S32 mLifetimeMS;
S32 mElapsedTimeMS;
F32 sizes[ ParticleData::PDC_NUM_KEYS ];
ColorF colors[ ParticleData::PDC_NUM_KEYS ];
#if defined(TORQUE_OS_XENON)
GFX360MemVertexBufferHandle<ParticleVertexType> mVertBuff;
#else
GFXVertexBufferHandle<ParticleVertexType> mVertBuff;
#endif
// These members are for implementing a link-list of the active emitter
// particles. Member part_store contains blocks of particles that can be
// chained in a link-list. Usually the first part_store block is large
// enough to contain all the particles but it can be expanded in emergency
// circumstances.
Vector <Particle*> part_store;
Particle* part_freelist;
Particle part_list_head;
S32 n_part_capacity;
S32 n_parts;
S32 mCurBuffSize;
};
#endif // _H_PARTICLE_EMITTER

View file

@ -0,0 +1,419 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "particleEmitterNode.h"
#include "console/consoleTypes.h"
#include "core/stream/bitStream.h"
#include "T3D/fx/particleEmitter.h"
#include "math/mathIO.h"
#include "sim/netConnection.h"
#include "console/engineAPI.h"
IMPLEMENT_CO_DATABLOCK_V1(ParticleEmitterNodeData);
IMPLEMENT_CO_NETOBJECT_V1(ParticleEmitterNode);
ConsoleDocClass( ParticleEmitterNodeData,
"@brief Contains additional data to be associated with a ParticleEmitterNode."
"@ingroup FX\n"
);
ConsoleDocClass( ParticleEmitterNode,
"@brief A particle emitter object that can be positioned in the world and "
"dynamically enabled or disabled.\n\n"
"@tsexample\n"
"datablock ParticleEmitterNodeData( SimpleEmitterNodeData )\n"
"{\n"
" timeMultiple = 1.0;\n"
"};\n\n"
"%emitter = new ParticleEmitterNode()\n"
"{\n"
" datablock = SimpleEmitterNodeData;\n"
" active = true;\n"
" emitter = FireEmitterData;\n"
" velocity = 3.5;\n"
"};\n\n"
"// Dynamically change emitter datablock\n"
"%emitter.setEmitterDataBlock( DustEmitterData );\n"
"@endtsexample\n"
"@note To change the emitter field dynamically (after the ParticleEmitterNode "
"object has been created) you must use the setEmitterDataBlock() method or the "
"change will not be replicated to other clients in the game.\n"
"Similarly, use the setActive() method instead of changing the active field "
"directly. When changing velocity, you need to toggle setActive() on and off "
"to force the state change to be transmitted to other clients.\n\n"
"@ingroup FX\n"
"@see ParticleEmitterNodeData\n"
"@see ParticleEmitterData\n"
);
//-----------------------------------------------------------------------------
// ParticleEmitterNodeData
//-----------------------------------------------------------------------------
ParticleEmitterNodeData::ParticleEmitterNodeData()
{
timeMultiple = 1.0;
}
ParticleEmitterNodeData::~ParticleEmitterNodeData()
{
}
//-----------------------------------------------------------------------------
// initPersistFields
//-----------------------------------------------------------------------------
void ParticleEmitterNodeData::initPersistFields()
{
addField( "timeMultiple", TYPEID< F32 >(), Offset(timeMultiple, ParticleEmitterNodeData),
"@brief Time multiplier for particle emitter nodes.\n\n"
"Increasing timeMultiple is like running the emitter at a faster rate - single-shot "
"emitters will complete in a shorter time, and continuous emitters will generate "
"particles more quickly.\n\n"
"Valid range is 0.01 - 100." );
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
// onAdd
//-----------------------------------------------------------------------------
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;
}
//-----------------------------------------------------------------------------
// preload
//-----------------------------------------------------------------------------
bool ParticleEmitterNodeData::preload(bool server, String &errorStr)
{
if( Parent::preload(server, errorStr) == false )
return false;
return true;
}
//-----------------------------------------------------------------------------
// packData
//-----------------------------------------------------------------------------
void ParticleEmitterNodeData::packData(BitStream* stream)
{
Parent::packData(stream);
stream->write(timeMultiple);
}
//-----------------------------------------------------------------------------
// unpackData
//-----------------------------------------------------------------------------
void ParticleEmitterNodeData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
stream->read(&timeMultiple);
}
//-----------------------------------------------------------------------------
// ParticleEmitterNode
//-----------------------------------------------------------------------------
ParticleEmitterNode::ParticleEmitterNode()
{
// Todo: ScopeAlways?
mNetFlags.set(Ghostable);
mTypeMask |= EnvironmentObjectType;
mActive = true;
mDataBlock = NULL;
mEmitterDatablock = NULL;
mEmitterDatablockId = 0;
mEmitter = NULL;
mVelocity = 1.0;
}
//-----------------------------------------------------------------------------
// Destructor
//-----------------------------------------------------------------------------
ParticleEmitterNode::~ParticleEmitterNode()
{
//
}
//-----------------------------------------------------------------------------
// initPersistFields
//-----------------------------------------------------------------------------
void ParticleEmitterNode::initPersistFields()
{
addField( "active", TYPEID< bool >(), Offset(mActive,ParticleEmitterNode),
"Controls whether particles are emitted from this node." );
addField( "emitter", TYPEID< ParticleEmitterData >(), Offset(mEmitterDatablock, ParticleEmitterNode),
"Datablock to use when emitting particles." );
addField( "velocity", TYPEID< F32 >(), Offset(mVelocity, ParticleEmitterNode),
"Velocity to use when emitting particles (in the direction of the "
"ParticleEmitterNode object's up (Z) axis)." );
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
// onAdd
//-----------------------------------------------------------------------------
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( isClientObject() )
{
setEmitterDataBlock( mEmitterDatablock );
}
else
{
setMaskBits( StateMask | EmitterDBMask );
}
mObjBox.minExtents.set(-0.5, -0.5, -0.5);
mObjBox.maxExtents.set( 0.5, 0.5, 0.5);
resetWorldBox();
addToScene();
return true;
}
//-----------------------------------------------------------------------------
// onRemove
//-----------------------------------------------------------------------------
void ParticleEmitterNode::onRemove()
{
removeFromScene();
if( isClientObject() )
{
if( mEmitter )
{
mEmitter->deleteWhenEmpty();
mEmitter = NULL;
}
}
Parent::onRemove();
}
//-----------------------------------------------------------------------------
// onNewDataBlock
//-----------------------------------------------------------------------------
bool ParticleEmitterNode::onNewDataBlock( GameBaseData *dptr, bool reload )
{
mDataBlock = dynamic_cast<ParticleEmitterNodeData*>( dptr );
if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
return false;
// Todo: Uncomment if this is a "leaf" class
scriptOnNewDataBlock();
return true;
}
//-----------------------------------------------------------------------------
void ParticleEmitterNode::inspectPostApply()
{
Parent::inspectPostApply();
setMaskBits(StateMask | EmitterDBMask);
}
//-----------------------------------------------------------------------------
// advanceTime
//-----------------------------------------------------------------------------
void ParticleEmitterNode::processTick(const Move* move)
{
Parent::processTick(move);
if ( isMounted() )
{
MatrixF mat;
mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat );
setTransform( mat );
}
}
void ParticleEmitterNode::advanceTime(F32 dt)
{
Parent::advanceTime(dt);
if(!mActive || mEmitter.isNull() || !mDataBlock)
return;
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));
}
//-----------------------------------------------------------------------------
// packUpdate
//-----------------------------------------------------------------------------
U32 ParticleEmitterNode::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
{
U32 retMask = Parent::packUpdate(con, mask, stream);
if ( stream->writeFlag( mask & InitialUpdateMask ) )
{
mathWrite(*stream, getTransform());
mathWrite(*stream, getScale());
}
if ( stream->writeFlag( mask & EmitterDBMask ) )
{
if( stream->writeFlag(mEmitterDatablock != NULL) )
{
stream->writeRangedU32(mEmitterDatablock->getId(), DataBlockObjectIdFirst,
DataBlockObjectIdLast);
}
}
if ( stream->writeFlag( mask & StateMask ) )
{
stream->writeFlag( mActive );
stream->write( mVelocity );
}
return retMask;
}
//-----------------------------------------------------------------------------
// unpackUpdate
//-----------------------------------------------------------------------------
void ParticleEmitterNode::unpackUpdate(NetConnection* con, BitStream* stream)
{
Parent::unpackUpdate(con, stream);
if ( stream->readFlag() )
{
MatrixF temp;
Point3F tempScale;
mathRead(*stream, &temp);
mathRead(*stream, &tempScale);
setScale(tempScale);
setTransform(temp);
}
if ( stream->readFlag() )
{
mEmitterDatablockId = stream->readFlag() ?
stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast) : 0;
ParticleEmitterData *emitterDB = NULL;
Sim::findObject( mEmitterDatablockId, emitterDB );
if ( isProperlyAdded() )
setEmitterDataBlock( emitterDB );
}
if ( stream->readFlag() )
{
mActive = stream->readFlag();
stream->read( &mVelocity );
}
}
void ParticleEmitterNode::setEmitterDataBlock(ParticleEmitterData* data)
{
if ( isServerObject() )
{
setMaskBits( EmitterDBMask );
}
else
{
ParticleEmitter* pEmitter = NULL;
if ( data )
{
// Create emitter with new datablock
pEmitter = new ParticleEmitter;
pEmitter->onNewDataBlock( data, false );
if( pEmitter->registerObject() == false )
{
Con::warnf(ConsoleLogEntry::General, "Could not register base emitter for particle of class: %s", data->getName() ? data->getName() : data->getIdString() );
delete pEmitter;
return;
}
}
// Replace emitter
if ( mEmitter )
mEmitter->deleteWhenEmpty();
mEmitter = pEmitter;
}
mEmitterDatablock = data;
}
DefineEngineMethod(ParticleEmitterNode, setEmitterDataBlock, void, (ParticleEmitterData* emitterDatablock), (0),
"Assigns the datablock for this emitter node.\n"
"@param emitterDatablock ParticleEmitterData datablock to assign\n"
"@tsexample\n"
"// Assign a new emitter datablock\n"
"%emitter.setEmitterDatablock( %emitterDatablock );\n"
"@endtsexample\n" )
{
if ( !emitterDatablock )
{
Con::errorf("ParticleEmitterData datablock could not be found when calling setEmitterDataBlock in particleEmitterNode.");
return;
}
object->setEmitterDataBlock(emitterDatablock);
}
DefineEngineMethod(ParticleEmitterNode, setActive, void, (bool active),,
"Turns the emitter on or off.\n"
"@param active New emitter state\n" )
{
object->setActive( active );
}

View file

@ -0,0 +1,118 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _PARTICLEEMITTERDUMMY_H_
#define _PARTICLEEMITTERDUMMY_H_
#ifndef _GAMEBASE_H_
#include "T3D/gameBase/gameBase.h"
#endif
class ParticleEmitterData;
class ParticleEmitter;
//*****************************************************************************
// ParticleEmitterNodeData
//*****************************************************************************
class ParticleEmitterNodeData : public GameBaseData
{
typedef GameBaseData Parent;
protected:
bool onAdd();
//-------------------------------------- Console set variables
public:
F32 timeMultiple;
//-------------------------------------- load set variables
public:
public:
ParticleEmitterNodeData();
~ParticleEmitterNodeData();
void packData(BitStream*);
void unpackData(BitStream*);
bool preload(bool server, String &errorStr);
DECLARE_CONOBJECT(ParticleEmitterNodeData);
static void initPersistFields();
};
//*****************************************************************************
// ParticleEmitterNode
//*****************************************************************************
class ParticleEmitterNode : public GameBase
{
typedef GameBase Parent;
enum MaskBits
{
StateMask = Parent::NextFreeMask << 0,
EmitterDBMask = Parent::NextFreeMask << 1,
NextFreeMask = Parent::NextFreeMask << 2,
};
private:
ParticleEmitterNodeData* mDataBlock;
protected:
bool onAdd();
void onRemove();
bool onNewDataBlock( GameBaseData *dptr, bool reload );
void inspectPostApply();
ParticleEmitterData* mEmitterDatablock;
S32 mEmitterDatablockId;
bool mActive;
SimObjectPtr<ParticleEmitter> mEmitter;
F32 mVelocity;
public:
ParticleEmitterNode();
~ParticleEmitterNode();
ParticleEmitter *getParticleEmitter() {return mEmitter;}
// Time/Move Management
public:
void processTick(const Move* move);
void advanceTime(F32 dt);
DECLARE_CONOBJECT(ParticleEmitterNode);
static void initPersistFields();
U32 packUpdate (NetConnection *conn, U32 mask, BitStream* stream);
void unpackUpdate(NetConnection *conn, BitStream* stream);
inline bool getActive( void ) { return mActive; };
inline void setActive( bool active ) { mActive = active; setMaskBits( StateMask ); };
void setEmitterDataBlock(ParticleEmitterData* data);
};
#endif // _H_PARTICLEEMISSIONDUMMY

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,287 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _PRECIPITATION_H_
#define _PRECIPITATION_H_
#include "gfx/gfxDevice.h"
#include "T3D/gameBase/gameBase.h"
#ifndef _GFXPRIMITIVEBUFFER_H_
#include "gfx/gfxPrimitiveBuffer.h"
#endif
#ifndef _RENDERPASSMANAGER_H_
#include "renderInstance/renderPassManager.h"
#endif
class SFXTrack;
class SFXSource;
//--------------------------------------------------------------------------
/// Precipitation datablock.
class PrecipitationData : public GameBaseData
{
typedef GameBaseData Parent;
public:
SFXTrack* soundProfile;
StringTableEntry mDropName; ///< Texture filename for drop particles
StringTableEntry mDropShaderName; ///< The name of the shader used for raindrops
StringTableEntry mSplashName; ///< Texture filename for splash particles
StringTableEntry mSplashShaderName; ///< The name of the shader used for raindrops
S32 mDropsPerSide; ///< How many drops are on a side of the raindrop texture.
S32 mSplashesPerSide; ///< How many splash are on a side of the splash texture.
PrecipitationData();
DECLARE_CONOBJECT(PrecipitationData);
bool preload( bool server, String& errorStr );
static void initPersistFields();
virtual void packData(BitStream* stream);
virtual void unpackData(BitStream* stream);
};
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
U32 hitType; ///< What kind of object the drop will hit
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
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;
hitType = 0;
hitPos = Point3F(0,0,0);
}
};
//--------------------------------------------------------------------------
class Precipitation : public GameBase
{
protected:
typedef GameBase Parent;
PrecipitationData* mDataBlock;
Raindrop *mDropHead; ///< Drop linked list head
Raindrop *mSplashHead; ///< Splash linked list head
Point2F* mTexCoords; ///< texture coords for rain texture
Point2F* mSplashCoords; ///< texture coordinates for splash texture
SFXSource* mAmbientSound; ///< Ambient sound
GFXShaderRef mDropShader; ///< The shader used for raindrops
GFXTexHandle mDropHandle; ///< Texture handle for raindrop
GFXShaderRef mSplashShader; ///< The shader used for splashes
GFXTexHandle mSplashHandle; ///< Texture handle for splash
U32 mLastRenderFrame; ///< Used to skip processTick when we haven't been visible.
U32 mDropHitMask; ///< Stores the current drop hit mask.
//console exposed variables
bool mFollowCam; ///< Does the system follow the camera or stay where it's placed.
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
bool mAnimateSplashes; ///< Animate the splashes using the frames in the texture.
S32 mDropAnimateMS; ///< If greater than zero, will animate the drops from
///< the frames in the texture
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 mUseLighting; ///< This enables shading of the drops and splashes
///< by the sun color.
ColorF mGlowIntensity; ///< Set it to 0 to disable the glow or use it to control
///< the intensity of each channel.
bool mReflect; ///< This enables the precipitation to be rendered
///< during reflection passes. This is expensive.
bool mUseWind; ///< This enables the wind from the sky SceneObject
///< to effect the velocitiy of the drops.
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
bool mDropHitPlayers; ///< Should drops collide with players
bool mDropHitVehicles; ///< Should drops collide with vehicles
F32 mFadeDistance; ///< The distance at which fading of the particles begins.
F32 mFadeDistanceEnd; ///< The distance at which fading of the particles ends.
U32 mMaxVBDrops; ///< The maximum drops allowed in one render batch.
GFXStateBlockRef mDefaultSB;
GFXStateBlockRef mDistantSB;
GFXShaderConstBufferRef mDropShaderConsts;
GFXShaderConstHandle* mDropShaderModelViewSC;
GFXShaderConstHandle* mDropShaderFadeStartEndSC;
GFXShaderConstHandle* mDropShaderCameraPosSC;
GFXShaderConstHandle* mDropShaderAmbientSC;
GFXShaderConstBufferRef mSplashShaderConsts;
GFXShaderConstHandle* mSplashShaderModelViewSC;
GFXShaderConstHandle* mSplashShaderFadeStartEndSC;
GFXShaderConstHandle* mSplashShaderCameraPosSC;
GFXShaderConstHandle* mSplashShaderAmbientSC;
struct
{
bool valid;
U32 startTime;
U32 totalTime;
F32 startPct;
F32 endPct;
} mStormData;
struct
{
bool valid;
U32 startTime;
U32 totalTime;
F32 startMax;
F32 startSpeed;
F32 endMax;
F32 endSpeed;
} mTurbulenceData;
//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 initRenderObjects(); ///< Re-inits the texture coord lookup tables
void initMaterials(); ///< Re-inits the textures and shaders
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, const Box3F &box, const VectorF &windVel); ///< Casts a ray to see if/when a drop will collide
void wrapDrop(Raindrop *drop, const Box3F &box, const U32 currTime, const VectorF &windVel); ///< 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
GFXPrimitiveBufferHandle mRainIB;
GFXVertexBufferHandle<GFXVertexPT> mRainVB;
bool onAdd();
void onRemove();
// Rendering
void prepRenderImage( SceneRenderState* state );
void renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* );
void setTransform(const MatrixF &mat);
public:
Precipitation();
~Precipitation();
void inspectPostApply();
enum
{
DataMask = Parent::NextFreeMask << 0,
PercentageMask = Parent::NextFreeMask << 1,
StormMask = Parent::NextFreeMask << 2,
TransformMask = Parent::NextFreeMask << 3,
TurbulenceMask = Parent::NextFreeMask << 4,
NextFreeMask = Parent::NextFreeMask << 5
};
bool onNewDataBlock( GameBaseData *dptr, bool reload );
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);
/// This is used to smoothly change the turbulence
/// over a desired time period. Setting ms to zero
/// will cause the change to be instantaneous. Setting
/// max zero will disable turbulence.
void setTurbulence(F32 max, F32 speed, U32 ms);
};
#endif // PRECIPITATION_H_

View file

@ -0,0 +1,698 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "T3D/fx/splash.h"
#include "console/consoleTypes.h"
#include "gfx/primBuilder.h"
#include "gfx/gfxDrawUtil.h"
#include "sfx/sfxSystem.h"
#include "sfx/sfxProfile.h"
#include "scene/sceneManager.h"
#include "scene/sceneRenderState.h"
#include "core/stream/bitStream.h"
#include "math/mathIO.h"
#include "T3D/fx/explosion.h"
#include "T3D/fx/particle.h"
#include "T3D/fx/particleEmitter.h"
#include "T3D/fx/particleEmitterNode.h"
#include "T3D/gameBase/gameProcess.h"
#include "sim/netConnection.h"
#include "renderInstance/renderPassManager.h"
#include "console/engineAPI.h"
namespace
{
MRandomLCG sgRandom(0xdeadbeef);
} // namespace {}
//----------------------------------------------------------------------------
IMPLEMENT_CO_DATABLOCK_V1(SplashData);
IMPLEMENT_CO_NETOBJECT_V1(Splash);
ConsoleDocClass( SplashData,
"@brief Acts as the physical point in space in white a Splash is created from.\n"
"@ingroup FX\n"
);
ConsoleDocClass( Splash,
"@brief Manages the ring used for a Splash effect.\n"
"@ingroup FX\n"
);
//--------------------------------------------------------------------------
// 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 );
}
//--------------------------------------------------------------------------
// Init fields
//--------------------------------------------------------------------------
void SplashData::initPersistFields()
{
addField("soundProfile", TYPEID< SFXProfile >(), Offset(soundProfile, SplashData), "SFXProfile effect to play.\n");
addField("scale", TypePoint3F, Offset(scale, SplashData), "The scale of this splashing effect, defined as the F32 points X, Y, Z.\n");
addField("emitter", TYPEID< ParticleEmitterData >(), Offset(emitterList, SplashData), NUM_EMITTERS, "List of particle emitters to create at the point of this Splash effect.\n");
addField("delayMS", TypeS32, Offset(delayMS, SplashData), "Time to delay, in milliseconds, before actually starting this effect.\n");
addField("delayVariance", TypeS32, Offset(delayVariance, SplashData), "Time variance for delayMS.\n");
addField("lifetimeMS", TypeS32, Offset(lifetimeMS, SplashData), "Lifetime for this effect, in milliseconds.\n");
addField("lifetimeVariance", TypeS32, Offset(lifetimeVariance, SplashData), "Time variance for lifetimeMS.\n");
addField("width", TypeF32, Offset(width, SplashData), "Width for the X and Y coordinates to create this effect within.");
addField("numSegments", TypeS32, Offset(numSegments, SplashData), "Number of ejection points in the splash ring.\n");
addField("velocity", TypeF32, Offset(velocity, SplashData), "Velocity for the splash effect to travel.\n");
addField("height", TypeF32, Offset(height, SplashData), "Height for the splash to reach.\n");
addField("acceleration", TypeF32, Offset(acceleration, SplashData), "Constant acceleration value to place upon the splash effect.\n");
addField("times", TypeF32, Offset(times, SplashData), NUM_TIME_KEYS, "Times to transition through the splash effect. Up to 4 allowed. Values are 0.0 - 1.0, and corrispond to the life of the particle where 0 is first created and 1 is end of lifespace.\n" );
addField("colors", TypeColorF, Offset(colors, SplashData), NUM_TIME_KEYS, "Color values to set the splash effect, rgba. Up to 4 allowed. Will transition through colors based on values set in the times value. Example: colors[0] = \"0.6 1.0 1.0 0.5\".\n" );
addField("texture", TypeFilename, Offset(textureName, SplashData), NUM_TEX, "Imagemap file to use as the texture for the splash effect.\n");
addField("texWrap", TypeF32, Offset(texWrap, SplashData), "Amount to wrap the texture around the splash ring, 0.0f - 1.0f.\n");
addField("texFactor", TypeF32, Offset(texFactor, SplashData), "Factor in which to apply the texture to the splash ring, 0.0f - 1.0f.\n");
addField("ejectionFreq", TypeF32, Offset(ejectionFreq, SplashData), "Frequency in which to emit splash rings.\n");
addField("ejectionAngle", TypeF32, Offset(ejectionAngle, SplashData), "Rotational angle to create a splash ring.\n");
addField("ringLifetime", TypeF32, Offset(ringLifetime, SplashData), "Lifetime, in milliseconds, for a splash ring.\n");
addField("startRadius", TypeF32, Offset(startRadius, SplashData), "Starting radius size of a splash ring.\n");
addField("explosion", TYPEID< ExplosionData >(), Offset(explosion, SplashData), "ExplosionData object to create at the creation position of this splash effect.\n");
Parent::initPersistFields();
}
//--------------------------------------------------------------------------
// 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, String &errorStr)
{
if (Parent::preload(server, errorStr) == 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] = GFXTexHandle(textureName[i], &GFXDefaultStaticDiffuseProfile, avar("%s() - textureHandle[%d] (line %d)", __FUNCTION__, i, __LINE__) );
}
}
}
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 );
// Only allocated client side.
mNetFlags.set( IsGhost );
}
//--------------------------------------------------------------------------
// 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;
for( U32 i=0; i<SplashData::NUM_EMITTERS; i++ )
{
if( mDataBlock->emitterList[i] != NULL )
{
ParticleEmitter * pEmitter = new ParticleEmitter;
pEmitter->onNewDataBlock( mDataBlock->emitterList[i], false );
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.minExtents = Point3F( -1, -1, -1 );
mObjBox.maxExtents = Point3F( 1, 1, 1 );
resetWorldBox();
gClientSceneGraph->addObjectToScene(this);
removeFromProcessList();
ClientProcessList::get()->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.clear();
getSceneManager()->removeObjectFromScene(this);
getContainer()->removeObject(this);
Parent::onRemove();
}
//--------------------------------------------------------------------------
// On New Data Block
//--------------------------------------------------------------------------
bool Splash::onNewDataBlock( GameBaseData *dptr, bool reload )
{
mDataBlock = dynamic_cast<SplashData*>(dptr);
if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
return false;
scriptOnNewDataBlock();
return true;
}
//--------------------------------------------------------------------------
// 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;
}
//----------------------------------------------------------------------------
// Update color
//----------------------------------------------------------------------------
void Splash::updateColor()
{
for(SplashRingList::Iterator ring = ringList.begin(); ring != ringList.end(); ++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.pushBack( ring );
}
}
//----------------------------------------------------------------------------
// Update rings
//----------------------------------------------------------------------------
void Splash::updateRings( F32 dt )
{
SplashRingList::Iterator ring;
for(SplashRingList::Iterator i = ringList.begin(); i != ringList.end(); /*Trickiness*/)
{
ring = i++;
ring->elapsedTime += dt;
if( !ring->isActive() )
{
ringList.erase( ring );
}
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.0f, 0.0f, -9.8f ) * 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, false);
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 );
}
}

View file

@ -0,0 +1,195 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _SPLASH_H_
#define _SPLASH_H_
#ifndef _GAMEBASE_H_
#include "T3D/gameBase/gameBase.h"
#endif
#ifndef _TORQUE_LIST_
#include "core/util/tList.h"
#endif
#include "gfx/gfxTextureHandle.h"
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];
GFXTexHandle textureHandle[NUM_TEX];
ExplosionData* explosion;
S32 explosionId;
SplashData();
DECLARE_CONOBJECT(SplashData);
bool onAdd();
bool preload(bool server, String &errorStr);
static void initPersistFields();
virtual void packData(BitStream* stream);
virtual void unpackData(BitStream* stream);
};
//--------------------------------------------------------------------------
// Splash
//--------------------------------------------------------------------------
class Splash : public GameBase
{
typedef GameBase Parent;
private:
SplashData* mDataBlock;
SimObjectPtr<ParticleEmitter> mEmitterList[ SplashData::NUM_EMITTERS ];
typedef Torque::List<SplashRing> SplashRingList;
SplashRingList 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 spawnExplosion();
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, bool reload );
DECLARE_CONOBJECT(Splash);
};
#endif // _H_SPLASH

View file

@ -0,0 +1,136 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "T3D/fx/windEmitter.h"
#include "math/mBox.h"
#include "core/tAlgorithm.h"
#include "platform/profiler.h"
Vector<WindEmitter*> WindEmitter::smAllEmitters;
WindEmitter::WindEmitter()
{
smAllEmitters.push_back( this );
mEnabled = true;
mScore = 0.0f;
mSphere.center.zero();
mSphere.radius = 0.0f;
mStrength = 0.0f;
mTurbulenceFrequency = 0.0f;
mTurbulenceStrength = 0.0f;
mVelocity.zero();
}
WindEmitter::~WindEmitter()
{
WindEmitterList::iterator iter = find( smAllEmitters.begin(), smAllEmitters.end(), this );
smAllEmitters.erase( iter );
}
void WindEmitter::setPosition( const Point3F& pos )
{
mSphere.center = pos;
}
void WindEmitter::update( const Point3F& pos, const VectorF& velocity )
{
mSphere.center = pos;
mVelocity = velocity;
}
void WindEmitter::setRadius( F32 radius )
{
mSphere.radius = radius;
}
void WindEmitter::setStrength( F32 strength )
{
mStrength = strength;
}
void WindEmitter::setTurbulency( F32 frequency, F32 strength )
{
mTurbulenceFrequency = frequency;
mTurbulenceStrength = strength;
}
S32 QSORT_CALLBACK WindEmitter::_sortByScore(const void* a, const void* b)
{
return mSign((*(WindEmitter**)b)->mScore - (*(WindEmitter**)a)->mScore);
}
bool WindEmitter::findBest( const Point3F& cameraPos,
const VectorF& cameraDir,
F32 viewDistance,
U32 maxResults,
WindEmitterList* results )
{
PROFILE_START(WindEmitter_findBest);
// Build a sphere from the camera point.
SphereF cameraSphere;
cameraSphere.center = cameraPos;
cameraSphere.radius = viewDistance;
// Collect the active spheres within the camera space and score them.
WindEmitterList best;
WindEmitterList::iterator iter = smAllEmitters.begin();
for ( ; iter != smAllEmitters.end(); iter++ )
{
const SphereF& sphere = *(*iter);
// Skip any spheres outside of our camera range or that are disabled.
if ( !(*iter)->mEnabled || !cameraSphere.isIntersecting( sphere ) )
continue;
// Simple score calculation...
//
// score = ( radius / distance to camera ) * dot( cameraDir, vector from camera to sphere )
//
Point3F vect = sphere.center - cameraSphere.center;
F32 dist = vect.len();
(*iter)->mScore = dist * sphere.radius;
vect /= getMax( dist, 0.001f );
(*iter)->mScore *= mDot( vect, cameraDir );
best.push_back( *iter );
}
// Sort the results by score!
dQsort( best.address(), best.size(), sizeof(WindEmitter*), &WindEmitter::_sortByScore );
// Clip the results to the max requested.
if ( best.size() > maxResults )
best.setSize( maxResults );
// Merge the results and return.
results->merge( best );
PROFILE_END(); // WindEmitter_findBest
return best.size() > 0;
}

View file

@ -0,0 +1,96 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _WINDEMITTER_H_
#define _WINDEMITTER_H_
#ifndef _MPOINT3_H_
#include "math/mPoint3.h"
#endif
#ifndef _MSPHERE_H_
#include "math/mSphere.h"
#endif
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
class WindEmitter;
/// A vector of WindEmitter pointers.
typedef Vector<WindEmitter*> WindEmitterList;
class WindEmitter
{
public:
WindEmitter();
~WindEmitter();
operator const SphereF&() const { return mSphere; }
void update( const Point3F& pos, const VectorF& velocity );
void setPosition( const Point3F& pos );
void setRadius( F32 radius );
void setStrength( F32 strength );
void setTurbulency( F32 frequency, F32 strength );
const Point3F& getCenter() const { return mSphere.center; }
F32 getRadius() const { return mSphere.radius; }
F32 getStrength() const { return mStrength; }
F32 getTurbulenceFrequency() const { return mTurbulenceFrequency; }
F32 getTurbulenceStrength() const { return mTurbulenceStrength; }
const VectorF& getVelocity() const { return mVelocity; }
static bool findBest( const Point3F& cameraPos,
const VectorF& cameraDir,
F32 viewDistance,
U32 maxResults,
WindEmitterList* results );
protected:
SphereF mSphere;
VectorF mVelocity;
F32 mStrength;
F32 mTurbulenceFrequency;
F32 mTurbulenceStrength;
F32 mScore;
bool mEnabled;
static WindEmitterList smAllEmitters;
static S32 QSORT_CALLBACK _sortByScore( const void* a, const void* b );
};
#endif // _WINDEMITTER_H_