Merge pull request #2056 from Bloodknight/afx_merge_main

Afx merge main
This commit is contained in:
Areloch 2017-10-11 08:47:47 -05:00 committed by GitHub
commit 35d649b57e
301 changed files with 59625 additions and 1480 deletions

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "platform/platform.h"
#include "T3D/aiPlayer.h"
@ -97,6 +102,9 @@ AIPlayer::AIPlayer()
mMoveSlowdown = true;
mMoveState = ModeStop;
// This new member saves the movement state of the AI so that
// it can be restored after a substituted animation is finished.
mMoveState_saved = -1;
mAimObject = 0;
mAimLocationSet = false;
mTargetInLOS = false;
@ -547,23 +555,27 @@ bool AIPlayer::getAIMove(Move *movePtr)
mMoveState = ModeMove;
}
if (mMoveStuckTestCountdown > 0)
--mMoveStuckTestCountdown;
else
{
// We should check to see if we are stuck...
F32 locationDelta = (location - mLastLocation).len();
// Don't check for ai stuckness if animation during
// an anim-clip effect override.
if (mDamageState == Enabled && !(anim_clip_flags & ANIM_OVERRIDDEN) && !isAnimationLocked()) {
if (mMoveStuckTestCountdown > 0)
--mMoveStuckTestCountdown;
else
{
// We should check to see if we are stuck...
F32 locationDelta = (location - mLastLocation).len();
if (locationDelta < mMoveStuckTolerance && mDamageState == Enabled)
{
// If we are slowing down, then it's likely that our location delta will be less than
// our move stuck tolerance. Because we can be both slowing and stuck
// we should TRY to check if we've moved. This could use better detection.
if ( mMoveState != ModeSlowing || locationDelta == 0 )
{
mMoveState = ModeStuck;
onStuck();
}
}
{
mMoveState = ModeStuck;
onStuck();
}
}
}
}
}
}
@ -626,6 +638,7 @@ bool AIPlayer::getAIMove(Move *movePtr)
}
#endif // TORQUE_NAVIGATION_ENABLED
if (!(anim_clip_flags & ANIM_OVERRIDDEN) && !isAnimationLocked())
mLastLocation = location;
return true;
@ -1415,6 +1428,47 @@ DefineEngineMethod( AIPlayer, clearMoveTriggers, void, ( ),,
object->clearMoveTriggers();
}
// These changes coordinate with anim-clip mods to parent class, Player.
// New method, restartMove(), restores the AIPlayer to its normal move-state
// following animation overrides from AFX. The tag argument is used to match
// the latest override and prevents interruption of overlapping animation
// overrides. See related anim-clip changes in Player.[h,cc].
void AIPlayer::restartMove(U32 tag)
{
if (tag != 0 && tag == last_anim_tag)
{
if (mMoveState_saved != -1)
{
mMoveState = (MoveState) mMoveState_saved;
mMoveState_saved = -1;
}
bool is_death_anim = ((anim_clip_flags & IS_DEATH_ANIM) != 0);
last_anim_tag = 0;
anim_clip_flags &= ~(ANIM_OVERRIDDEN | IS_DEATH_ANIM);
if (mDamageState != Enabled)
{
if (!is_death_anim)
{
// this is a bit hardwired and desperate,
// but if he's dead he needs to look like it.
setActionThread("death10", false, false, false);
}
}
}
}
// New method, saveMoveState(), stores the current movement state
// so that it can be restored when restartMove() is called.
void AIPlayer::saveMoveState()
{
if (mMoveState_saved == -1)
mMoveState_saved = (S32) mMoveState;
}
F32 AIPlayer::getTargetDistance(GameBase* target, bool _checkEnabled)
{
if (!isServerObject()) return false;

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#ifndef _AIPLAYER_H_
#define _AIPLAYER_H_
@ -225,6 +230,18 @@ public:
/// @}
#endif // TORQUE_NAVIGATION_ENABLED
// New method, restartMove(), restores the AIPlayer to its normal move-state
// following animation overrides from AFX. The tag argument is used to match
// the latest override and prevents interruption of overlapping animation
// overrides.
// New method, saveMoveState(), stores the current movement state
// so that it can be restored when restartMove() is called.
// See related anim-clip changes in Player.[h,cc].
private:
S32 mMoveState_saved;
public:
void restartMove(U32 tag);
void saveMoveState();
};
#endif

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#ifndef _CAMERA_H_
#define _CAMERA_H_
@ -246,6 +251,8 @@ class Camera: public ShapeBase
DECLARE_CONOBJECT( Camera );
DECLARE_CATEGORY( "Game" );
DECLARE_DESCRIPTION( "Represents a position, direction and field of view to render a scene from." );
static F32 getMovementSpeed() { return smMovementSpeed; }
bool isCamera() const { return true; }
};
typedef Camera::CameraMotionMode CameraMotionMode;

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "platform/platform.h"
#include "T3D/containerQuery.h"
@ -91,7 +96,9 @@ void physicalZoneFind(SceneObject* obj, void *key)
if (pz->isActive()) {
info->gravityScale *= pz->getGravityMod();
info->appliedForce += pz->getForce();
Point3F center;
info->box.getCenter(&center);
info->appliedForce += pz->getForce(&center);
}
}

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "platform/platform.h"
#include "T3D/debris.h"
@ -113,6 +118,85 @@ DebrisData::DebrisData()
ignoreWater = true;
}
//#define TRACK_DEBRIS_DATA_CLONES
#ifdef TRACK_DEBRIS_DATA_CLONES
static int debris_data_clones = 0;
#endif
DebrisData::DebrisData(const DebrisData& other, bool temp_clone) : GameBaseData(other, temp_clone)
{
#ifdef TRACK_DEBRIS_DATA_CLONES
debris_data_clones++;
if (debris_data_clones == 1)
Con::errorf("DebrisData -- Clones are on the loose!");
#endif
velocity = other.velocity;
velocityVariance = other.velocityVariance;
friction = other.friction;
elasticity = other.elasticity;
lifetime = other.lifetime;
lifetimeVariance = other.lifetimeVariance;
numBounces = other.numBounces;
bounceVariance = other.bounceVariance;
minSpinSpeed = other.minSpinSpeed;
maxSpinSpeed = other.maxSpinSpeed;
explodeOnMaxBounce = other.explodeOnMaxBounce;
staticOnMaxBounce = other.staticOnMaxBounce;
snapOnMaxBounce = other.snapOnMaxBounce;
fade = other.fade;
useRadiusMass = other.useRadiusMass;
baseRadius = other.baseRadius;
gravModifier = other.gravModifier;
terminalVelocity = other.terminalVelocity;
ignoreWater = other.ignoreWater;
shapeName = other.shapeName;
shape = other.shape; // -- TSShape loaded using shapeName
textureName = other.textureName;
explosionId = other.explosionId; // -- for pack/unpack of explosion ptr
explosion = other.explosion;
dMemcpy( emitterList, other.emitterList, sizeof( emitterList ) );
dMemcpy( emitterIDList, other.emitterIDList, sizeof( emitterIDList ) ); // -- for pack/unpack of emitterList ptrs
}
DebrisData::~DebrisData()
{
if (!isTempClone())
return;
#ifdef TRACK_DEBRIS_DATA_CLONES
if (debris_data_clones > 0)
{
debris_data_clones--;
if (debris_data_clones == 0)
Con::errorf("DebrisData -- Clones eliminated!");
}
else
Con::errorf("DebrisData -- Too many clones deleted!");
#endif
}
DebrisData* DebrisData::cloneAndPerformSubstitutions(const SimObject* owner, S32 index)
{
if (!owner || getSubstitutionCount() == 0)
return this;
DebrisData* sub_debris_db = new DebrisData(*this, true);
performSubstitutions(sub_debris_db, owner, index);
return sub_debris_db;
}
void DebrisData::onPerformSubstitutions()
{
if( shapeName && shapeName[0] != '\0')
{
shape = ResourceManager::get().load(shapeName);
if( bool(shape) == false )
Con::errorf("DebrisData::onPerformSubstitutions(): failed to load shape \"%s\"", shapeName);
}
}
bool DebrisData::onAdd()
{
if(!Parent::onAdd())
@ -269,6 +353,9 @@ void DebrisData::initPersistFields()
addField("ignoreWater", TypeBool, Offset(ignoreWater, DebrisData), "If true, this debris object will not collide with water, acting as if the water is not there.");
endGroup("Behavior");
// disallow some field substitutions
onlyKeepClearSubstitutions("emitters"); // subs resolving to "~~", or "~0" are OK
onlyKeepClearSubstitutions("explosion");
Parent::initPersistFields();
}
@ -451,6 +538,8 @@ Debris::Debris()
// Only allocated client side.
mNetFlags.set( IsGhost );
ss_object = 0;
ss_index = 0;
}
Debris::~Debris()
@ -466,6 +555,12 @@ Debris::~Debris()
delete mPart;
mPart = NULL;
}
if (mDataBlock && mDataBlock->isTempClone())
{
delete mDataBlock;
mDataBlock = 0;
}
}
void Debris::initPersistFields()
@ -495,6 +590,8 @@ bool Debris::onNewDataBlock( GameBaseData *dptr, bool reload )
if( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
return false;
if (mDataBlock->isTempClone())
return true;
scriptOnNewDataBlock();
return true;
@ -519,7 +616,7 @@ bool Debris::onAdd()
if( mDataBlock->emitterList[i] != NULL )
{
ParticleEmitter * pEmitter = new ParticleEmitter;
pEmitter->onNewDataBlock( mDataBlock->emitterList[i], false );
pEmitter->onNewDataBlock(mDataBlock->emitterList[i]->cloneAndPerformSubstitutions(ss_object, ss_index), false);
if( !pEmitter->registerObject() )
{
Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() );
@ -537,7 +634,8 @@ bool Debris::onAdd()
{
sizeList[0] = mSize * 0.5;
sizeList[1] = mSize;
sizeList[2] = mSize * 1.5;
for (U32 i = 2; i < ParticleData::PDC_NUM_KEYS; i++)
sizeList[i] = mSize * 1.5;
mEmitterList[0]->setSizes( sizeList );
}
@ -546,7 +644,8 @@ bool Debris::onAdd()
{
sizeList[0] = 0.0;
sizeList[1] = mSize * 0.5;
sizeList[2] = mSize;
for (U32 i = 2; i < ParticleData::PDC_NUM_KEYS; i++)
sizeList[i] = mSize;
mEmitterList[1]->setSizes( sizeList );
}
@ -798,7 +897,8 @@ void Debris::explode()
Point3F explosionPos = getPosition();
Explosion* pExplosion = new Explosion;
pExplosion->onNewDataBlock(mDataBlock->explosion, false);
pExplosion->setSubstitutionData(ss_object, ss_index);
pExplosion->onNewDataBlock(mDataBlock->explosion->cloneAndPerformSubstitutions(ss_object, ss_index), false);
MatrixF trans( true );
trans.setPosition( getPosition() );

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#ifndef _DEBRIS_H_
#define _DEBRIS_H_
@ -97,6 +102,12 @@ struct DebrisData : public GameBaseData
DECLARE_CONOBJECT(DebrisData);
public:
/*C*/ DebrisData(const DebrisData&, bool = false);
/*D*/ ~DebrisData();
DebrisData* cloneAndPerformSubstitutions(const SimObject*, S32 index=0);
virtual void onPerformSubstitutions();
virtual bool allowSubstitutions() const { return true; }
};
//**************************************************************************
@ -165,6 +176,11 @@ public:
DECLARE_CONOBJECT(Debris);
private:
SimObject* ss_object;
S32 ss_index;
public:
void setSubstitutionData(SimObject* obj, S32 idx=0) { ss_object = obj; ss_index = idx; }
};

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "platform/platform.h"
#include "T3D/fx/explosion.h"
@ -50,6 +55,8 @@
#include "renderInstance/renderPassManager.h"
#include "console/engineAPI.h"
#include "sfx/sfxProfile.h"
IMPLEMENT_CONOBJECT(Explosion);
ConsoleDocClass( Explosion,
@ -281,6 +288,105 @@ ExplosionData::ExplosionData()
lightNormalOffset = 0.1f;
}
//#define TRACK_EXPLOSION_DATA_CLONES
#ifdef TRACK_EXPLOSION_DATA_CLONES
static int explosion_data_clones = 0;
#endif
ExplosionData::ExplosionData(const ExplosionData& other, bool temp_clone) : GameBaseData(other, temp_clone)
{
#ifdef TRACK_EXPLOSION_DATA_CLONES
explosion_data_clones++;
if (explosion_data_clones == 1)
Con::errorf("ExplosionData -- Clones are on the loose!");
#endif
dtsFileName = other.dtsFileName;
faceViewer = other.faceViewer;
particleDensity = other.particleDensity;
particleRadius = other.particleRadius;
soundProfile = other.soundProfile;
particleEmitter = other.particleEmitter;
particleEmitterId = other.particleEmitterId; // -- for pack/unpack of particleEmitter ptr
explosionScale = other.explosionScale;
playSpeed = other.playSpeed;
explosionShape = other.explosionShape; // -- TSShape loaded using dtsFileName
explosionAnimation = other.explosionAnimation; // -- from explosionShape sequence "ambient"
dMemcpy( emitterList, other.emitterList, sizeof( emitterList ) );
dMemcpy( emitterIDList, other.emitterIDList, sizeof( emitterIDList ) ); // -- for pack/unpack of emitterList ptrs
dMemcpy( debrisList, other.debrisList, sizeof( debrisList ) );
dMemcpy( debrisIDList, other.debrisIDList, sizeof( debrisIDList ) ); // -- for pack/unpack of debrisList ptrs
debrisThetaMin = other.debrisThetaMin;
debrisThetaMax = other.debrisThetaMax;
debrisPhiMin = other.debrisPhiMin;
debrisPhiMax = other.debrisPhiMax;
debrisNum = other.debrisNum;
debrisNumVariance = other.debrisNumVariance;
debrisVelocity = other.debrisVelocity;
debrisVelocityVariance = other.debrisVelocityVariance;
dMemcpy( explosionList, other.explosionList, sizeof( explosionList ) );
dMemcpy( explosionIDList, other.explosionIDList, sizeof( explosionIDList ) ); // -- for pack/unpack of explosionList ptrs
delayMS = other.delayMS;
delayVariance = other.delayVariance;
lifetimeMS = other.lifetimeMS;
lifetimeVariance = other.lifetimeVariance;
offset = other.offset;
dMemcpy( sizes, other.times, sizeof( sizes ) );
dMemcpy( times, other.times, sizeof( times ) );
shakeCamera = other.shakeCamera;
camShakeFreq = other.camShakeFreq;
camShakeAmp = other.camShakeAmp;
camShakeDuration = other.camShakeDuration;
camShakeRadius = other.camShakeRadius;
camShakeFalloff = other.camShakeFalloff;
lightStartRadius = other.lightStartRadius;
lightEndRadius = other.lightEndRadius;
lightStartColor = other.lightStartColor;
lightEndColor = other.lightEndColor;
lightStartBrightness = other.lightStartBrightness;
lightEndBrightness = other.lightEndBrightness;
lightNormalOffset = other.lightNormalOffset;
// Note - Explosion calls mDataBlock->getName() in warning messages but
// that should be safe.
}
ExplosionData::~ExplosionData()
{
if (!isTempClone())
return;
if (soundProfile && soundProfile->isTempClone())
{
delete soundProfile;
soundProfile = 0;
}
// particleEmitter, emitterList[*], debrisList[*], explosionList[*] will delete themselves
#ifdef TRACK_EXPLOSION_DATA_CLONES
if (explosion_data_clones > 0)
{
explosion_data_clones--;
if (explosion_data_clones == 0)
Con::errorf("ExplosionData -- Clones eliminated!");
}
else
Con::errorf("ExplosionData -- Too many clones deleted!");
#endif
}
ExplosionData* ExplosionData::cloneAndPerformSubstitutions(const SimObject* owner, S32 index)
{
if (!owner || getSubstitutionCount() == 0)
return this;
ExplosionData* sub_explosion_db = new ExplosionData(*this, true);
performSubstitutions(sub_explosion_db, owner, index);
return sub_explosion_db;
}
void ExplosionData::initPersistFields()
{
addField( "explosionShape", TypeShapeFilename, Offset(dtsFileName, ExplosionData),
@ -412,6 +518,12 @@ void ExplosionData::initPersistFields()
"Distance (in the explosion normal direction) of the PointLight position "
"from the explosion center." );
// disallow some field substitutions
onlyKeepClearSubstitutions("debris"); // subs resolving to "~~", or "~0" are OK
onlyKeepClearSubstitutions("emitter");
onlyKeepClearSubstitutions("particleEmitter");
onlyKeepClearSubstitutions("soundProfile");
onlyKeepClearSubstitutions("subExplosion");
Parent::initPersistFields();
}
@ -808,6 +920,10 @@ Explosion::Explosion()
mLight = LIGHTMGR->createLightInfo();
mNetFlags.set( IsGhost );
ss_object = 0;
ss_index = 0;
mDataBlock = 0;
soundProfile_clone = 0;
}
Explosion::~Explosion()
@ -820,6 +936,18 @@ Explosion::~Explosion()
}
SAFE_DELETE(mLight);
if (soundProfile_clone)
{
delete soundProfile_clone;
soundProfile_clone = 0;
}
if (mDataBlock && mDataBlock->isTempClone())
{
delete mDataBlock;
mDataBlock = 0;
}
}
@ -978,6 +1106,8 @@ bool Explosion::onNewDataBlock( GameBaseData *dptr, bool reload )
if (!mDataBlock || !Parent::onNewDataBlock( dptr, reload ))
return false;
if (mDataBlock->isTempClone())
return true;
scriptOnNewDataBlock();
return true;
}
@ -1190,7 +1320,8 @@ void Explosion::launchDebris( Point3F &axis )
launchDir *= debrisVel;
Debris *debris = new Debris;
debris->setDataBlock( mDataBlock->debrisList[0] );
debris->setSubstitutionData(ss_object, ss_index);
debris->setDataBlock(mDataBlock->debrisList[0]->cloneAndPerformSubstitutions(ss_object, ss_index));
debris->setTransform( getTransform() );
debris->init( pos, launchDir );
@ -1218,7 +1349,8 @@ void Explosion::spawnSubExplosions()
{
MatrixF trans = getTransform();
Explosion* pExplosion = new Explosion;
pExplosion->setDataBlock( mDataBlock->explosionList[i] );
pExplosion->setSubstitutionData(ss_object, ss_index);
pExplosion->setDataBlock(mDataBlock->explosionList[i]->cloneAndPerformSubstitutions(ss_object, ss_index));
pExplosion->setTransform( trans );
pExplosion->setInitialState( trans.getPosition(), mInitialNormal, 1);
if (!pExplosion->registerObject())
@ -1256,12 +1388,18 @@ bool Explosion::explode()
resetWorldBox();
}
if (mDataBlock->soundProfile)
SFX->playOnce( mDataBlock->soundProfile, &getTransform() );
SFXProfile* sound_prof = dynamic_cast<SFXProfile*>(mDataBlock->soundProfile);
if (sound_prof)
{
soundProfile_clone = sound_prof->cloneAndPerformSubstitutions(ss_object, ss_index);
SFX->playOnce( soundProfile_clone, &getTransform() );
if (!soundProfile_clone->isTempClone())
soundProfile_clone = 0;
}
if (mDataBlock->particleEmitter) {
mMainEmitter = new ParticleEmitter;
mMainEmitter->setDataBlock(mDataBlock->particleEmitter);
mMainEmitter->setDataBlock(mDataBlock->particleEmitter->cloneAndPerformSubstitutions(ss_object, ss_index));
mMainEmitter->registerObject();
mMainEmitter->emitParticles(getPosition(), mInitialNormal, mDataBlock->particleRadius,
@ -1273,7 +1411,7 @@ bool Explosion::explode()
if( mDataBlock->emitterList[i] != NULL )
{
ParticleEmitter * pEmitter = new ParticleEmitter;
pEmitter->setDataBlock( mDataBlock->emitterList[i] );
pEmitter->setDataBlock(mDataBlock->emitterList[i]->cloneAndPerformSubstitutions(ss_object, ss_index));
if( !pEmitter->registerObject() )
{
Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() );

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#ifndef _EXPLOSION_H_
#define _EXPLOSION_H_
@ -42,6 +47,7 @@ class TSThread;
class SFXTrack;
struct DebrisData;
class SFXProfile;
//--------------------------------------------------------------------------
class ExplosionData : public GameBaseData {
public:
@ -126,6 +132,11 @@ class ExplosionData : public GameBaseData {
static void initPersistFields();
virtual void packData(BitStream* stream);
virtual void unpackData(BitStream* stream);
public:
/*C*/ ExplosionData(const ExplosionData&, bool = false);
/*D*/ ~ExplosionData();
ExplosionData* cloneAndPerformSubstitutions(const SimObject*, S32 index=0);
virtual bool allowSubstitutions() const { return true; }
};
@ -188,6 +199,12 @@ class Explosion : public GameBase, public ISceneLight
DECLARE_CONOBJECT(Explosion);
static void initPersistFields();
private:
SimObject* ss_object;
S32 ss_index;
SFXProfile* soundProfile_clone;
public:
void setSubstitutionData(SimObject* obj, S32 idx=0) { ss_object = obj; ss_index = idx; }
};
#endif // _H_EXPLOSION

View file

@ -43,6 +43,11 @@
// POTENTIAL TODO LIST:
// TODO: Clamp item alpha to fog alpha
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "platform/platform.h"
#include "T3D/fx/fxFoliageReplicator.h"
@ -402,6 +407,9 @@ void fxFoliageReplicator::initPersistFields()
addField( "AllowedTerrainSlope", TypeS32, Offset( mFieldData.mAllowedTerrainSlope, fxFoliageReplicator ), "Maximum surface angle allowed for foliage instances." );
endGroup( "Restrictions" ); // MM: Added Group Footer.
addGroup( "AFX" );
addField( "AmbientModulationBias", TypeF32, Offset( mFieldData.mAmbientModulationBias,fxFoliageReplicator ), "Multiplier controling amount foliage is modulated by sun's ambient." );
endGroup( "AFX" );
// Initialise parents' persistent fields.
Parent::initPersistFields();
}
@ -1564,7 +1572,12 @@ void fxFoliageReplicator::renderObject(ObjectRenderInst *ri, SceneRenderState *s
mFoliageShaderConsts->setSafe(mFoliageShaderGroundAlphaSC, Point4F(mFieldData.mGroundAlpha, mFieldData.mGroundAlpha, mFieldData.mGroundAlpha, mFieldData.mGroundAlpha));
if (mFoliageShaderAmbientColorSC->isValid())
mFoliageShaderConsts->set(mFoliageShaderAmbientColorSC, state->getAmbientLightColor());
{
LinearColorF ambient = state->getAmbientLightColor();
LinearColorF ambient_inv(1.0f-ambient.red, 1.0f-ambient.green, 1.0f-ambient.blue, 0.0f);
ambient += ambient_inv*(1.0f - mFieldData.mAmbientModulationBias);
mFoliageShaderConsts->set(mFoliageShaderAmbientColorSC, ambient);
}
GFX->setShaderConstBuffer(mFoliageShaderConsts);
@ -1705,6 +1718,7 @@ U32 fxFoliageReplicator::packUpdate(NetConnection * con, U32 mask, BitStream * s
stream->writeFlag(mFieldData.mShowPlacementArea); // Show Placement Area Flag.
stream->write(mFieldData.mPlacementBandHeight); // Placement Area Height.
stream->write(mFieldData.mPlaceAreaColour); // Placement Area Colour.
stream->write(mFieldData.mAmbientModulationBias);
}
// Were done ...
@ -1782,6 +1796,7 @@ void fxFoliageReplicator::unpackUpdate(NetConnection * con, BitStream * stream)
stream->read(&mFieldData.mPlacementBandHeight); // Placement Area Height.
stream->read(&mFieldData.mPlaceAreaColour);
stream->read(&mFieldData.mAmbientModulationBias);
// Calculate Fade-In/Out Gradients.
mFadeInGradient = 1.0f / mFieldData.mFadeInRegion;
mFadeOutGradient = 1.0f / mFieldData.mFadeOutRegion;

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#ifndef _FOLIAGEREPLICATOR_H_
#define _FOLIAGEREPLICATOR_H_
@ -319,6 +324,7 @@ public:
U32 mPlacementBandHeight;
LinearColorF mPlaceAreaColour;
F32 mAmbientModulationBias;
tagFieldData()
{
// Set Defaults.
@ -377,6 +383,7 @@ public:
mShowPlacementArea = true;
mPlacementBandHeight = 25;
mPlaceAreaColour .set(0.4f, 0, 0.8f);
mAmbientModulationBias = 1.0f;
}
} mFieldData;

View file

@ -19,6 +19,12 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "particle.h"
#include "console/consoleTypes.h"
#include "console/typeValidators.h"
@ -72,6 +78,8 @@ static const F32 sgDefaultSpinSpeed = 1.f;
static const F32 sgDefaultSpinRandomMin = 0.f;
static const F32 sgDefaultSpinRandomMax = 0.f;
static const F32 sgDefaultSpinBias = 1.0f;
static const F32 sgDefaultSizeBias = 1.0f;
//-----------------------------------------------------------------------------
// Constructor
@ -102,9 +110,9 @@ ParticleData::ParticleData()
}
times[0] = 0.0f;
times[1] = 0.33f;
times[2] = 0.66f;
times[3] = 1.0f;
times[1] = 1.0f;
for (i = 2; i < PDC_NUM_KEYS; i++)
times[i] = -1.0f;
texCoords[0].set(0.0,0.0); // texture coords at 4 corners
texCoords[1].set(0.0,1.0); // of particle quad
@ -115,18 +123,20 @@ ParticleData::ParticleData()
animTexUVs = NULL; // array of tile vertex UVs
textureName = NULL; // texture filename
textureHandle = NULL; // loaded texture handle
textureExtName = NULL;
textureExtHandle = NULL;
constrain_pos = false;
start_angle = 0.0f;
angle_variance = 0.0f;
sizeBias = sgDefaultSizeBias;
spinBias = sgDefaultSpinBias;
randomizeSpinDir = false;
}
//-----------------------------------------------------------------------------
// Destructor
//-----------------------------------------------------------------------------
ParticleData::~ParticleData()
{
if (animTexUVs)
{
delete [] animTexUVs;
}
}
FRangeValidator dragCoefFValidator(0.f, 5.f);
FRangeValidator gravCoefFValidator(-10.f, 10.f);
@ -214,6 +224,15 @@ void ParticleData::initPersistFields()
"@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)." );
addGroup("AFX");
addField("textureExtName", TypeFilename, Offset(textureExtName, ParticleData));
addField("constrainPos", TypeBool, Offset(constrain_pos, ParticleData));
addField("angle", TypeF32, Offset(start_angle, ParticleData));
addField("angleVariance", TypeF32, Offset(angle_variance, ParticleData));
addField("sizeBias", TypeF32, Offset(sizeBias, ParticleData));
addField("spinBias", TypeF32, Offset(spinBias, ParticleData));
addField("randomizeSpinDir", TypeBool, Offset(randomizeSpinDir, ParticleData));
endGroup("AFX");
Parent::initPersistFields();
}
@ -243,18 +262,22 @@ void ParticleData::packData(BitStream* stream)
stream->writeInt((S32)(spinRandomMin + 1000), 11);
stream->writeInt((S32)(spinRandomMax + 1000), 11);
}
if(stream->writeFlag(spinBias != sgDefaultSpinBias))
stream->write(spinBias);
stream->writeFlag(randomizeSpinDir);
stream->writeFlag(useInvAlpha);
S32 i, count;
// see how many frames there are:
for(count = 0; count < 3; count++)
for(count = 0; count < ParticleData::PDC_NUM_KEYS-1; count++)
if(times[count] >= 1)
break;
count++;
stream->writeInt(count-1, 2);
// An extra bit is needed for 8 keys.
stream->writeInt(count-1, 3);
for( i=0; i<count; i++ )
{
@ -262,7 +285,8 @@ void ParticleData::packData(BitStream* stream)
stream->writeFloat( colors[i].green, 7);
stream->writeFloat( colors[i].blue, 7);
stream->writeFloat( colors[i].alpha, 7);
stream->writeFloat( sizes[i]/MaxParticleSize, 14);
// AFX bits raised from 14 to 16 to allow larger sizes
stream->writeFloat( sizes[i]/MaxParticleSize, 16);
stream->writeFloat( times[i], 8);
}
@ -279,6 +303,13 @@ void ParticleData::packData(BitStream* stream)
mathWrite(*stream, animTexTiling);
stream->writeInt(framesPerSec, 8);
}
if (stream->writeFlag(textureExtName && textureExtName[0]))
stream->writeString(textureExtName);
stream->writeFlag(constrain_pos);
stream->writeFloat(start_angle/360.0f, 11);
stream->writeFloat(angle_variance/180.0f, 10);
if(stream->writeFlag(sizeBias != sgDefaultSizeBias))
stream->write(sizeBias);
}
//-----------------------------------------------------------------------------
@ -322,17 +353,24 @@ void ParticleData::unpackData(BitStream* stream)
spinRandomMax = sgDefaultSpinRandomMax;
}
if(stream->readFlag())
stream->read(&spinBias);
else
spinBias = sgDefaultSpinBias;
randomizeSpinDir = stream->readFlag();
useInvAlpha = stream->readFlag();
S32 i;
S32 count = stream->readInt(2) + 1;
// An extra bit is needed for 8 keys.
S32 count = stream->readInt(3) + 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;
// AFX bits raised from 14 to 16 to allow larger sizes
sizes[i] = stream->readFloat(16) * MaxParticleSize;
times[i] = stream->readFloat(8);
}
textureName = (stream->readFlag()) ? stream->readSTString() : 0;
@ -346,6 +384,14 @@ void ParticleData::unpackData(BitStream* stream)
mathRead(*stream, &animTexTiling);
framesPerSec = stream->readInt(8);
}
textureExtName = (stream->readFlag()) ? stream->readSTString() : 0;
constrain_pos = stream->readFlag();
start_angle = 360.0f*stream->readFloat(11);
angle_variance = 180.0f*stream->readFloat(10);
if(stream->readFlag())
stream->read(&sizeBias);
else
sizeBias = sgDefaultSizeBias;
}
bool ParticleData::protectedSetSizes( void *object, const char *index, const char *data)
@ -427,11 +473,33 @@ bool ParticleData::onAdd()
}
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];
}
for (U32 i = 1; i < PDC_NUM_KEYS; i++)
{
if (times[i] < 0.0f)
break;
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];
}
}
times[0] = 0.0f;
U32 last_idx = 0;
for (U32 i = 1; i < PDC_NUM_KEYS; i++)
{
if (times[i] < 0.0f)
break;
else
last_idx = i;
}
for (U32 i = last_idx+1; i < PDC_NUM_KEYS; i++)
{
times[i] = times[last_idx];
colors[i] = colors[last_idx];
sizes[i] = sizes[last_idx];
}
// Here we validate parameters
@ -470,6 +538,10 @@ bool ParticleData::onAdd()
}
}
start_angle = mFmod(start_angle, 360.0f);
if (start_angle < 0.0f)
start_angle += 360.0f;
angle_variance = mClampF(angle_variance, -180.0f, 180.0f);
return true;
}
@ -495,6 +567,15 @@ bool ParticleData::preload(bool server, String &errorStr)
error = true;
}
}
if (textureExtName && textureExtName[0])
{
textureExtHandle = GFXTexHandle(textureExtName, &GFXStaticTextureSRGBProfile, avar("%s() - textureExtHandle (line %d)", __FUNCTION__, __LINE__));
if (!textureExtHandle)
{
errorStr = String::ToString("Missing particle texture: %s", textureName);
error = true;
}
}
if (animateTexture)
{
@ -606,6 +687,11 @@ void ParticleData::initializeParticle(Particle* init, const Point3F& inheritVelo
// assign spin amount
init->spinSpeed = spinSpeed * gRandGen.randF( spinRandomMin, spinRandomMax );
// apply spin bias
init->spinSpeed *= spinBias;
// randomize spin direction
if (randomizeSpinDir && (gRandGen.randI( 0, 1 ) == 1))
init->spinSpeed = -init->spinSpeed;
}
bool ParticleData::reload(char errorBuffer[256])
@ -653,3 +739,78 @@ DefineEngineMethod(ParticleData, reload, void, (),,
char errorBuffer[256];
object->reload(errorBuffer);
}
//#define TRACK_PARTICLE_DATA_CLONES
#ifdef TRACK_PARTICLE_DATA_CLONES
static int particle_data_clones = 0;
#endif
ParticleData::ParticleData(const ParticleData& other, bool temp_clone) : SimDataBlock(other, temp_clone)
{
#ifdef TRACK_PARTICLE_DATA_CLONES
particle_data_clones++;
if (particle_data_clones == 1)
Con::errorf("ParticleData -- Clones are on the loose!");
#endif
dragCoefficient = other.dragCoefficient;
windCoefficient = other.windCoefficient;
gravityCoefficient = other.gravityCoefficient;
inheritedVelFactor = other.inheritedVelFactor;
constantAcceleration = other.constantAcceleration;
lifetimeMS = other.lifetimeMS;
lifetimeVarianceMS = other.lifetimeVarianceMS;
spinSpeed = other.spinSpeed;
spinRandomMin = other.spinRandomMin;
spinRandomMax = other.spinRandomMax;
useInvAlpha = other.useInvAlpha;
animateTexture = other.animateTexture;
numFrames = other.numFrames; // -- calc from other fields
framesPerSec = other.framesPerSec;
dMemcpy( colors, other.colors, sizeof( colors ) );
dMemcpy( sizes, other.sizes, sizeof( sizes ) );
dMemcpy( times, other.times, sizeof( times ) );
animTexUVs = other.animTexUVs; // -- calc from other fields
dMemcpy( texCoords, other.texCoords, sizeof( texCoords ) );
animTexTiling = other.animTexTiling;
animTexFramesString = other.animTexFramesString;
animTexFrames = other.animTexFrames; // -- parsed from animTexFramesString
textureName = other.textureName;
textureHandle = other.textureHandle;
spinBias = other.spinBias;
randomizeSpinDir = other.randomizeSpinDir;
textureExtName = other.textureExtName;
textureExtHandle = other.textureExtHandle;
constrain_pos = other.constrain_pos;
start_angle = other.start_angle;
angle_variance = other.angle_variance;
sizeBias = other.sizeBias;
}
ParticleData::~ParticleData()
{
if (animTexUVs)
{
delete [] animTexUVs;
}
if (!isTempClone())
return;
#ifdef TRACK_PARTICLE_DATA_CLONES
if (particle_data_clones > 0)
{
particle_data_clones--;
if (particle_data_clones == 0)
Con::errorf("ParticleData -- Clones eliminated!");
}
else
Con::errorf("ParticleData -- Too many clones deleted!");
#endif
}
void ParticleData::onPerformSubstitutions()
{
char errorBuffer[256];
reload(errorBuffer);
}

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#ifndef _PARTICLE_H_
#define _PARTICLE_H_
@ -44,7 +49,9 @@ class ParticleData : public SimDataBlock
public:
enum PDConst
{
PDC_NUM_KEYS = 4,
// This increase the keyframes from 4 to 8. Especially useful for premult-alpha blended particles
// for which 4 keyframes is often not enough.
PDC_NUM_KEYS = 8,
};
F32 dragCoefficient;
@ -97,6 +104,23 @@ class ParticleData : public SimDataBlock
static void initPersistFields();
bool reload(char errorBuffer[256]);
public:
/*C*/ ParticleData(const ParticleData&, bool = false);
virtual void onPerformSubstitutions();
virtual bool allowSubstitutions() const { return true; }
protected:
F32 spinBias;
bool randomizeSpinDir;
StringTableEntry textureExtName;
public:
GFXTexHandle textureExtHandle;
bool constrain_pos;
F32 start_angle;
F32 angle_variance;
F32 sizeBias;
public:
bool loadParameters();
bool reload(String &errorStr);
};
//*****************************************************************************
@ -123,6 +147,10 @@ struct Particle
F32 spinSpeed;
Particle * next;
Point3F pos_local;
F32 t_last;
Point3F radial_v; // radial vector for concentric effects
// note -- for non-oriented particles, we use orientDir.x to store the billboard start angle.
};

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "platform/platform.h"
#include "T3D/fx/particleEmitter.h"
@ -38,6 +43,10 @@
#include "lighting/lightInfo.h"
#include "console/engineAPI.h"
#if defined(AFX_CAP_PARTICLE_POOLS)
#include "afx/util/afxParticlePool.h"
#endif
Point3F ParticleEmitter::mWindVelocity( 0.0, 0.0, 0.0 );
const F32 ParticleEmitter::AgedSpinToRadians = (1.0f/1000.0f) * (1.0f/360.0f) * M_PI_F * 2.0f;
@ -149,6 +158,21 @@ ParticleEmitterData::ParticleEmitterData()
alignParticles = false;
alignDirection = Point3F(0.0f, 1.0f, 0.0f);
ejectionInvert = false;
fade_color = false;
fade_alpha = false;
fade_size = false;
parts_per_eject = 1;
use_emitter_xfm = false;
#if defined(AFX_CAP_PARTICLE_POOLS)
pool_datablock = 0;
pool_index = 0;
pool_depth_fade = false;
pool_radial_fade = false;
do_pool_id_convert = false;
#endif
}
@ -293,6 +317,26 @@ void ParticleEmitterData::initPersistFields()
endGroup( "ParticleEmitterData" );
addGroup("AFX");
addField("ejectionInvert", TypeBool, Offset(ejectionInvert, ParticleEmitterData));
addField("fadeColor", TypeBool, Offset(fade_color, ParticleEmitterData));
addField("fadeAlpha", TypeBool, Offset(fade_alpha, ParticleEmitterData));
addField("fadeSize", TypeBool, Offset(fade_size, ParticleEmitterData));
// useEmitterTransform currently does not work in TGEA or T3D
addField("useEmitterTransform", TypeBool, Offset(use_emitter_xfm, ParticleEmitterData));
endGroup("AFX");
#if defined(AFX_CAP_PARTICLE_POOLS)
addGroup("AFX Pooled Particles");
addField("poolData", TYPEID<afxParticlePoolData>(), Offset(pool_datablock, ParticleEmitterData));
addField("poolIndex", TypeS32, Offset(pool_index, ParticleEmitterData));
addField("poolDepthFade", TypeBool, Offset(pool_depth_fade, ParticleEmitterData));
addField("poolRadialFade", TypeBool, Offset(pool_radial_fade, ParticleEmitterData));
endGroup("AFX Pooled Particles");
#endif
// disallow some field substitutions
disableFieldSubstitutions("particles");
onlyKeepClearSubstitutions("poolData"); // subs resolving to "~~", or "~0" are OK
Parent::initPersistFields();
}
@ -358,6 +402,22 @@ void ParticleEmitterData::packData(BitStream* stream)
stream->writeFlag(renderReflection);
stream->writeFlag(glow);
stream->writeInt( blendStyle, 4 );
stream->writeFlag(ejectionInvert);
stream->writeFlag(fade_color);
stream->writeFlag(fade_alpha);
stream->writeFlag(fade_size);
stream->writeFlag(use_emitter_xfm);
#if defined(AFX_CAP_PARTICLE_POOLS)
if (stream->writeFlag(pool_datablock))
{
stream->writeRangedU32(packed ? SimObjectId((uintptr_t)pool_datablock) : pool_datablock->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast);
stream->write(pool_index);
stream->writeFlag(pool_depth_fade);
stream->writeFlag(pool_radial_fade);
}
#endif
}
//-----------------------------------------------------------------------------
@ -421,6 +481,22 @@ void ParticleEmitterData::unpackData(BitStream* stream)
renderReflection = stream->readFlag();
glow = stream->readFlag();
blendStyle = stream->readInt( 4 );
ejectionInvert = stream->readFlag();
fade_color = stream->readFlag();
fade_alpha = stream->readFlag();
fade_size = stream->readFlag();
use_emitter_xfm = stream->readFlag();
#if defined(AFX_CAP_PARTICLE_POOLS)
if (stream->readFlag())
{
pool_datablock = (afxParticlePoolData*)stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
stream->read(&pool_index);
pool_depth_fade = stream->readFlag();
pool_radial_fade = stream->readFlag();
do_pool_id_convert = true;
}
#endif
}
//-----------------------------------------------------------------------------
@ -600,6 +676,22 @@ bool ParticleEmitterData::preload(bool server, String &errorStr)
if (!server)
{
#if defined(AFX_CAP_PARTICLE_POOLS)
if (do_pool_id_convert)
{
SimObjectId db_id = (SimObjectId)(uintptr_t)pool_datablock;
if (db_id != 0)
{
// try to convert id to pointer
if (!Sim::findObject(db_id, pool_datablock))
{
Con::errorf("ParticleEmitterData::reload() -- bad datablockId: 0x%x (poolData)", db_id);
}
}
do_pool_id_convert = false;
}
#endif
// load emitter texture if specified
if (textureName && textureName[0])
{
@ -669,6 +761,8 @@ void ParticleEmitterData::allocPrimBuffer( S32 overrideSize )
partListInitSize = maxPartLife / (ejectionPeriodMS - periodVarianceMS);
partListInitSize += 8; // add 8 as "fudge factor" to make sure it doesn't realloc if it goes over by 1
if (parts_per_eject > 1)
partListInitSize *= parts_per_eject;
// if override size is specified, then the emitter overran its buffer and needs a larger allocation
if( overrideSize != -1 )
@ -705,6 +799,134 @@ void ParticleEmitterData::allocPrimBuffer( S32 overrideSize )
delete [] indices;
}
//#define TRACK_PARTICLE_EMITTER_DATA_CLONES
#ifdef TRACK_PARTICLE_EMITTER_DATA_CLONES
static int emitter_data_clones = 0;
#endif
ParticleEmitterData::ParticleEmitterData(const ParticleEmitterData& other, bool temp_clone) : GameBaseData(other, temp_clone)
{
#ifdef TRACK_PARTICLE_EMITTER_DATA_CLONES
emitter_data_clones++;
if (emitter_data_clones == 1)
Con::errorf("ParticleEmitterData -- Clones are on the loose!");
#endif
ejectionPeriodMS = other.ejectionPeriodMS;
periodVarianceMS = other.periodVarianceMS;
ejectionVelocity = other.ejectionVelocity;
velocityVariance = other.velocityVariance;
ejectionOffset = other.ejectionOffset;
ejectionOffsetVariance = other.ejectionOffsetVariance;
thetaMin = other.thetaMin;
thetaMax = other.thetaMax;
phiReferenceVel = other.phiReferenceVel;
phiVariance = other.phiVariance;
softnessDistance = other.softnessDistance;
ambientFactor = other.ambientFactor;
lifetimeMS = other.lifetimeMS;
lifetimeVarianceMS = other.lifetimeVarianceMS;
overrideAdvance = other.overrideAdvance;
orientParticles = other.orientParticles;
orientOnVelocity = other.orientOnVelocity;
useEmitterSizes = other.useEmitterSizes;
useEmitterColors = other.useEmitterColors;
alignParticles = other.alignParticles;
alignDirection = other.alignDirection;
particleString = other.particleString;
particleDataBlocks = other.particleDataBlocks; // -- derived from particleString
dataBlockIds = other.dataBlockIds; // -- derived from particleString
partListInitSize = other.partListInitSize; // -- approx calc from other fields
primBuff = other.primBuff;
blendStyle = other.blendStyle;
sortParticles = other.sortParticles;
reverseOrder = other.reverseOrder;
textureName = other.textureName;
textureHandle = other.textureHandle; // -- TextureHandle loads using textureName
highResOnly = other.highResOnly;
renderReflection = other.renderReflection;
fade_color = other.fade_color;
fade_size = other.fade_size;
fade_alpha = other.fade_alpha;
ejectionInvert = other.ejectionInvert;
parts_per_eject = other.parts_per_eject; // -- set to 1 (used by subclasses)
use_emitter_xfm = other.use_emitter_xfm;
#if defined(AFX_CAP_PARTICLE_POOLS)
pool_datablock = other.pool_datablock;
pool_index = other.pool_index;
pool_depth_fade = other.pool_depth_fade;
pool_radial_fade = other.pool_radial_fade;
do_pool_id_convert = other.do_pool_id_convert; // -- flags pool id conversion need
#endif
}
ParticleEmitterData::~ParticleEmitterData()
{
if (!isTempClone())
return;
for (S32 i = 0; i < particleDataBlocks.size(); i++)
{
if (particleDataBlocks[i] && particleDataBlocks[i]->isTempClone())
{
delete particleDataBlocks[i];
particleDataBlocks[i] = 0;
}
}
#ifdef TRACK_PARTICLE_EMITTER_DATA_CLONES
if (emitter_data_clones > 0)
{
emitter_data_clones--;
if (emitter_data_clones == 0)
Con::errorf("ParticleEmitterData -- Clones eliminated!");
}
else
Con::errorf("ParticleEmitterData -- Too many clones deleted!");
#endif
}
ParticleEmitterData* ParticleEmitterData::cloneAndPerformSubstitutions(const SimObject* owner, S32 index)
{
if (!owner)
return this;
bool clone_parts_db = false;
// note -- this could be checked when the particle blocks are evaluated
for (S32 i = 0; i < this->particleDataBlocks.size(); i++)
{
if (this->particleDataBlocks[i] && (this->particleDataBlocks[i]->getSubstitutionCount() > 0))
{
clone_parts_db = true;
break;
}
}
ParticleEmitterData* sub_emitter_db = this;
if (this->getSubstitutionCount() > 0 || clone_parts_db)
{
sub_emitter_db = new ParticleEmitterData(*this, true);
performSubstitutions(sub_emitter_db, owner, index);
if (clone_parts_db)
{
for (S32 i = 0; i < sub_emitter_db->particleDataBlocks.size(); i++)
{
if (sub_emitter_db->particleDataBlocks[i] && (sub_emitter_db->particleDataBlocks[i]->getSubstitutionCount() > 0))
{
ParticleData* orig_db = sub_emitter_db->particleDataBlocks[i];
sub_emitter_db->particleDataBlocks[i] = new ParticleData(*orig_db, true);
orig_db->performSubstitutions(sub_emitter_db->particleDataBlocks[i], owner, index);
}
}
}
}
return sub_emitter_db;
}
//-----------------------------------------------------------------------------
// ParticleEmitter
@ -736,6 +958,16 @@ ParticleEmitter::ParticleEmitter()
// ParticleEmitter should be allocated on the client only.
mNetFlags.set( IsGhost );
fade_amt = 1.0f;
forced_bbox = false;
db_temp_clone = false;
pos_pe.set(0,0,0);
sort_priority = 0;
mDataBlock = 0;
#if defined(AFX_CAP_PARTICLE_POOLS)
pool = 0;
#endif
}
//-----------------------------------------------------------------------------
@ -747,6 +979,19 @@ ParticleEmitter::~ParticleEmitter()
{
delete [] part_store[i];
}
if (db_temp_clone && mDataBlock && mDataBlock->isTempClone())
{
for (S32 i = 0; i < mDataBlock->particleDataBlocks.size(); i++)
{
if (mDataBlock->particleDataBlocks[i] && mDataBlock->particleDataBlocks[i]->isTempClone())
{
delete mDataBlock->particleDataBlocks[i];
mDataBlock->particleDataBlocks[i] = 0;
}
}
delete mDataBlock;
mDataBlock = 0;
}
}
//-----------------------------------------------------------------------------
@ -771,6 +1016,11 @@ bool ParticleEmitter::onAdd()
mObjBox.maxExtents = Point3F(radius, radius, radius);
resetWorldBox();
#if defined(AFX_CAP_PARTICLE_POOLS)
if (pool)
pool->addParticleEmitter(this);
#endif
return true;
}
@ -780,6 +1030,14 @@ bool ParticleEmitter::onAdd()
//-----------------------------------------------------------------------------
void ParticleEmitter::onRemove()
{
#if defined(AFX_CAP_PARTICLE_POOLS)
if (pool)
{
pool->removeParticleEmitter(this);
pool = 0;
}
#endif
removeFromScene();
Parent::onRemove();
}
@ -825,6 +1083,11 @@ bool ParticleEmitter::onNewDataBlock( GameBaseData *dptr, bool reload )
part_list_head.next = NULL;
n_parts = 0;
}
if (mDataBlock->isTempClone())
{
db_temp_clone = true;
return true;
}
scriptOnNewDataBlock();
return true;
@ -861,6 +1124,11 @@ LinearColorF ParticleEmitter::getCollectiveColor()
//-----------------------------------------------------------------------------
void ParticleEmitter::prepRenderImage(SceneRenderState* state)
{
#if defined(AFX_CAP_PARTICLE_POOLS)
if (pool)
return;
#endif
if( state->isReflectPass() && !getDataBlock()->renderReflection )
return;
@ -889,6 +1157,7 @@ void ParticleEmitter::prepRenderImage(SceneRenderState* state)
ri->translucentSort = true;
ri->type = RenderPassManager::RIT_Particle;
ri->sortDistSq = getRenderWorldBox().getSqDistanceToPoint( camPos );
ri->defaultKey = (-sort_priority*100);
// Draw the system offscreen unless the highResOnly flag is set on the datablock
ri->systemState = ( getDataBlock()->highResOnly ? PSS_AwaitingHighResDraw : PSS_AwaitingOffscreenDraw );
@ -992,6 +1261,7 @@ void ParticleEmitter::emitParticles(const Point3F& point,
return;
}
pos_pe = point;
Point3F realStart;
if( useLastPosition && mHasLastPosition )
realStart = mLastPosition;
@ -1059,7 +1329,8 @@ void ParticleEmitter::emitParticles(const Point3F& start,
// Create particle at the correct position
Point3F pos;
pos.interpolate(start, end, F32(currTime) / F32(numMilliseconds));
addParticle(pos, axis, velocity, axisx);
addParticle(pos, axis, velocity, axisx, numMilliseconds-currTime);
particlesAdded = true;
mNextParticleTime = 0;
}
@ -1089,7 +1360,7 @@ void ParticleEmitter::emitParticles(const Point3F& start,
// Create particle at the correct position
Point3F pos;
pos.interpolate(start, end, F32(currTime) / F32(numMilliseconds));
addParticle(pos, axis, velocity, axisx);
addParticle(pos, axis, velocity, axisx, numMilliseconds-currTime);
particlesAdded = true;
// This override-advance code is restored in order to correctly adjust
@ -1114,17 +1385,27 @@ void ParticleEmitter::emitParticles(const Point3F& start,
{
if (advanceMS != 0)
{
F32 t = F32(advanceMS) / 1000.0;
F32 t = F32(advanceMS) / 1000.0;
Point3F a = last_part->acc;
a -= last_part->vel * last_part->dataBlock->dragCoefficient;
a -= mWindVelocity * last_part->dataBlock->windCoefficient;
a += Point3F(0.0f, 0.0f, -9.81f) * last_part->dataBlock->gravityCoefficient;
Point3F a = last_part->acc;
a -= last_part->vel * last_part->dataBlock->dragCoefficient;
a -= mWindVelocity * last_part->dataBlock->windCoefficient;
//a += Point3F(0.0f, 0.0f, -9.81f) * last_part->dataBlock->gravityCoefficient;
a.z += -9.81f*last_part->dataBlock->gravityCoefficient; // as long as gravity is a constant, this is faster
last_part->vel += a * t;
last_part->pos += last_part->vel * t;
last_part->vel += a * t;
//last_part->pos += last_part->vel * t;
last_part->pos_local += last_part->vel * t;
updateKeyData( last_part );
// AFX -- allow subclasses to adjust the particle params here
sub_particleUpdate(last_part);
if (last_part->dataBlock->constrain_pos)
last_part->pos = last_part->pos_local + this->pos_pe;
else
last_part->pos = last_part->pos_local;
updateKeyData( last_part );
}
}
}
@ -1196,7 +1477,7 @@ void ParticleEmitter::emitParticles(const Point3F& rCenter,
axis.normalize();
pos += rCenter;
addParticle(pos, axis, velocity, axisz);
addParticle(pos, axis, velocity, axisz, 0);
}
// Set world bounding box
@ -1219,6 +1500,8 @@ void ParticleEmitter::emitParticles(const Point3F& rCenter,
//-----------------------------------------------------------------------------
void ParticleEmitter::updateBBox()
{
if (forced_bbox)
return;
Point3F minPt(1e10, 1e10, 1e10);
Point3F maxPt(-1e10, -1e10, -1e10);
@ -1239,15 +1522,18 @@ void ParticleEmitter::updateBBox()
boxScale.y = getMax(boxScale.y, 1.0f);
boxScale.z = getMax(boxScale.z, 1.0f);
mBBObjToWorld.scale(boxScale);
#if defined(AFX_CAP_PARTICLE_POOLS)
if (pool)
pool->updatePoolBBox(this);
#endif
}
//-----------------------------------------------------------------------------
// addParticle
//-----------------------------------------------------------------------------
void ParticleEmitter::addParticle(const Point3F& pos,
const Point3F& axis,
const Point3F& vel,
const Point3F& axisx)
void ParticleEmitter::addParticle(const Point3F& pos, const Point3F& axis, const Point3F& vel,
const Point3F& axisx, const U32 age_offset)
{
n_parts++;
if (n_parts > n_part_capacity || n_parts > mDataBlock->partListInitSize)
@ -1269,6 +1555,16 @@ void ParticleEmitter::addParticle(const Point3F& pos,
pNew->next = part_list_head.next;
part_list_head.next = pNew;
// for earlier access to constrain_pos, the ParticleData datablock is chosen here instead
// of later in the method.
U32 dBlockIndex = gRandGen.randI() % mDataBlock->particleDataBlocks.size();
ParticleData* part_db = mDataBlock->particleDataBlocks[dBlockIndex];
// set start position to world or local space
Point3F pos_start;
if (part_db->constrain_pos)
pos_start.set(0,0,0);
else
pos_start = pos;
Point3F ejectionAxis = axis;
F32 theta = (mDataBlock->thetaMax - mDataBlock->thetaMin) * gRandGen.randF() +
mDataBlock->thetaMin;
@ -1290,14 +1586,17 @@ void ParticleEmitter::addParticle(const Point3F& pos,
F32 initialVel = mDataBlock->ejectionVelocity;
initialVel += (mDataBlock->velocityVariance * 2.0f * gRandGen.randF()) - mDataBlock->velocityVariance;
pNew->pos = pos + (ejectionAxis * (mDataBlock->ejectionOffset + mDataBlock->ejectionOffsetVariance* gRandGen.randF()) );
pNew->vel = ejectionAxis * initialVel;
pNew->orientDir = ejectionAxis;
pNew->pos = pos_start + (ejectionAxis * (mDataBlock->ejectionOffset + mDataBlock->ejectionOffsetVariance* gRandGen.randF()) );
pNew->pos_local = pNew->pos;
pNew->vel = mDataBlock->ejectionInvert ? ejectionAxis * -initialVel : ejectionAxis * initialVel;
if (mDataBlock->orientParticles)
pNew->orientDir = ejectionAxis;
else
// note -- for non-oriented particles, we use orientDir.x to store the billboard start angle.
pNew->orientDir.x = mDegToRad(part_db->start_angle + part_db->angle_variance*2.0f*gRandGen.randF() - part_db->angle_variance);
pNew->acc.set(0, 0, 0);
pNew->currentAge = 0;
// Choose a new particle datablack randomly from the list
U32 dBlockIndex = gRandGen.randI() % mDataBlock->particleDataBlocks.size();
pNew->currentAge = age_offset;
pNew->t_last = 0.0f;
mDataBlock->particleDataBlocks[dBlockIndex]->initializeParticle(pNew, vel);
updateKeyData( pNew );
@ -1379,8 +1678,10 @@ void ParticleEmitter::updateKeyData( Particle *part )
if( part->totalLifetime < 1 )
part->totalLifetime = 1;
F32 t = F32(part->currentAge) / F32(part->totalLifetime);
AssertFatal(t <= 1.0f, "Out out bounds filter function for particle.");
if (part->currentAge > part->totalLifetime)
part->currentAge = part->totalLifetime;
F32 t = (F32)part->currentAge / (F32)part->totalLifetime;
for( U32 i = 1; i < ParticleData::PDC_NUM_KEYS; i++ )
{
@ -1412,7 +1713,25 @@ void ParticleEmitter::updateKeyData( Particle *part )
{
part->size = (part->dataBlock->sizes[i-1] * (1.0 - firstPart)) +
(part->dataBlock->sizes[i] * firstPart);
part->size *= part->dataBlock->sizeBias;
}
if (mDataBlock->fade_color)
{
if (mDataBlock->fade_alpha)
part->color *= fade_amt;
else
{
part->color.red *= fade_amt;
part->color.green *= fade_amt;
part->color.blue *= fade_amt;
}
}
else if (mDataBlock->fade_alpha)
part->color.alpha *= fade_amt;
if (mDataBlock->fade_size)
part->size *= fade_amt;
break;
}
@ -1424,19 +1743,25 @@ void ParticleEmitter::updateKeyData( Particle *part )
//-----------------------------------------------------------------------------
void ParticleEmitter::update( U32 ms )
{
// TODO: Prefetch
F32 t = F32(ms)/1000.0f; // AFX -- moved outside loop, no need to recalculate this for every particle
for (Particle* part = part_list_head.next; part != NULL; part = part->next)
{
F32 t = F32(ms) / 1000.0;
Point3F a = part->acc;
a -= part->vel * part->dataBlock->dragCoefficient;
a -= part->vel * part->dataBlock->dragCoefficient;
a -= mWindVelocity * part->dataBlock->windCoefficient;
a += Point3F(0.0f, 0.0f, -9.81f) * part->dataBlock->gravityCoefficient;
a.z += -9.81f*part->dataBlock->gravityCoefficient; // AFX -- as long as gravity is a constant, this is faster
part->vel += a * t;
part->pos += part->vel * t;
part->pos_local += part->vel * t;
// AFX -- allow subclasses to adjust the particle params here
sub_particleUpdate(part);
if (part->dataBlock->constrain_pos)
part->pos = part->pos_local + this->pos_pe;
else
part->pos = part->pos_local;
updateKeyData( part );
}
@ -1999,3 +2324,43 @@ DefineEngineMethod(ParticleEmitterData, reload, void,(),,
{
object->reload();
}
void ParticleEmitter::emitParticlesExt(const MatrixF& xfm, const Point3F& point,
const Point3F& velocity, const U32 numMilliseconds)
{
if (mDataBlock->use_emitter_xfm)
{
Point3F zero_point(0.0f, 0.0f, 0.0f);
this->pos_pe = zero_point;
this->setTransform(xfm);
Point3F axis(0.0,0.0,1.0);
xfm.mulV(axis);
emitParticles(zero_point, true, axis, velocity, numMilliseconds);
}
else
{
this->pos_pe = point;
Point3F axis(0.0,0.0,1.0);
xfm.mulV(axis);
emitParticles(point, true, axis, velocity, numMilliseconds);
}
}
void ParticleEmitter::setForcedObjBox(Box3F& box)
{
mObjBox = box;
forced_bbox = true;
#if defined(AFX_CAP_PARTICLE_POOLS)
if (pool)
pool->updatePoolBBox(this);
#endif
}
void ParticleEmitter::setSortPriority(S8 priority)
{
sort_priority = (priority == 0) ? 1 : priority;
#if defined(AFX_CAP_PARTICLE_POOLS)
if (pool)
pool->setSortPriority(sort_priority);
#endif
}

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#ifndef _H_PARTICLE_EMITTER
#define _H_PARTICLE_EMITTER
@ -42,6 +47,12 @@
class RenderPassManager;
class ParticleData;
#define AFX_CAP_PARTICLE_POOLS
#if defined(AFX_CAP_PARTICLE_POOLS)
class afxParticlePoolData;
class afxParticlePool;
#endif
//*****************************************************************************
// Particle Emitter Data
//*****************************************************************************
@ -113,6 +124,26 @@ class ParticleEmitterData : public GameBaseData
bool glow; ///< Renders this emitter into the glow buffer.
bool reload();
public:
bool fade_color;
bool fade_size;
bool fade_alpha;
bool ejectionInvert;
U8 parts_per_eject;
bool use_emitter_xfm;
#if defined(AFX_CAP_PARTICLE_POOLS)
public:
afxParticlePoolData* pool_datablock;
U32 pool_index;
bool pool_depth_fade;
bool pool_radial_fade;
bool do_pool_id_convert;
#endif
public:
/*C*/ ParticleEmitterData(const ParticleEmitterData&, bool = false);
/*D*/ ~ParticleEmitterData();
virtual ParticleEmitterData* cloneAndPerformSubstitutions(const SimObject*, S32 index=0);
virtual bool allowSubstitutions() const { return true; }
};
//*****************************************************************************
@ -121,6 +152,9 @@ class ParticleEmitterData : public GameBaseData
class ParticleEmitter : public GameBase
{
typedef GameBase Parent;
#if defined(AFX_CAP_PARTICLE_POOLS)
friend class afxParticlePool;
#endif
public:
@ -190,7 +224,7 @@ class ParticleEmitter : public GameBase
/// @param axis
/// @param vel Initial velocity
/// @param axisx
void addParticle(const Point3F &pos, const Point3F &axis, const Point3F &vel, const Point3F &axisx);
void addParticle(const Point3F &pos, const Point3F &axis, const Point3F &vel, const Point3F &axisx, const U32 age_offset);
inline void setupBillboard( Particle *part,
@ -227,7 +261,12 @@ class ParticleEmitter : public GameBase
// PEngine interface
private:
// AFX subclasses to ParticleEmitter require access to some members and methods of
// ParticleEmitter which are normally declared with private scope. In this section,
// protected and private scope statements have been inserted inline with the original
// code to expose the necessary members and methods.
void update( U32 ms );
protected:
inline void updateKeyData( Particle *part );
@ -239,25 +278,30 @@ class ParticleEmitter : public GameBase
ParticleEmitterData* mDataBlock;
protected:
U32 mInternalClock;
U32 mNextParticleTime;
Point3F mLastPosition;
bool mHasLastPosition;
private:
MatrixF mBBObjToWorld;
bool mDeleteWhenEmpty;
bool mDeleteOnTick;
protected:
S32 mLifetimeMS;
S32 mElapsedTimeMS;
private:
F32 sizes[ ParticleData::PDC_NUM_KEYS ];
LinearColorF colors[ ParticleData::PDC_NUM_KEYS ];
GFXVertexBufferHandle<ParticleVertexType> mVertBuff;
protected:
// 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
@ -268,8 +312,28 @@ class ParticleEmitter : public GameBase
Particle part_list_head;
S32 n_part_capacity;
S32 n_parts;
private:
S32 mCurBuffSize;
protected:
F32 fade_amt;
bool forced_bbox;
bool db_temp_clone;
Point3F pos_pe;
S8 sort_priority;
virtual void sub_particleUpdate(Particle*) { }
public:
virtual void emitParticlesExt(const MatrixF& xfm, const Point3F& point, const Point3F& velocity, const U32 numMilliseconds);
void setFadeAmount(F32 amt) { fade_amt = amt; }
void setForcedObjBox(Box3F& box);
void setSortPriority(S8 priority);
#if defined(AFX_CAP_PARTICLE_POOLS)
protected:
afxParticlePool* pool;
public:
void clearPool() { pool = 0; }
void setPool(afxParticlePool* p) { pool = p; }
#endif
};
#endif // _H_PARTICLE_EMITTER

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "platform/platform.h"
#include "T3D/gameBase/gameBase.h"
#include "console/consoleTypes.h"
@ -36,6 +41,7 @@
#include "T3D/aiConnection.h"
#endif
#include "afx/arcaneFX.h"
//----------------------------------------------------------------------------
// Ghost update relative priority values
@ -122,6 +128,12 @@ GameBaseData::GameBaseData()
category = "";
packed = false;
}
GameBaseData::GameBaseData(const GameBaseData& other, bool temp_clone) : SimDataBlock(other, temp_clone)
{
packed = other.packed;
category = other.category;
//mReloadSignal = other.mReloadSignal; // DO NOT copy the mReloadSignal member.
}
void GameBaseData::inspectPostApply()
{
@ -244,6 +256,8 @@ GameBase::GameBase()
GameBase::~GameBase()
{
if (scope_registered)
arcaneFX::unregisterScopedObject(this);
}
@ -256,8 +270,16 @@ bool GameBase::onAdd()
// Datablock must be initialized on the server.
// Client datablock are initialized by the initial update.
if ( isServerObject() && mDataBlock && !onNewDataBlock( mDataBlock, false ) )
return false;
if (isClientObject())
{
if (scope_id > 0 && !scope_registered)
arcaneFX::registerScopedObject(this);
}
else
{
if ( mDataBlock && !onNewDataBlock( mDataBlock, false ) )
return false;
}
setProcessTick( true );
@ -266,6 +288,8 @@ bool GameBase::onAdd()
void GameBase::onRemove()
{
if (scope_registered)
arcaneFX::unregisterScopedObject(this);
// EDITOR FEATURE: Remove us from the reload signal of our datablock.
if ( mDataBlock )
mDataBlock->mReloadSignal.remove( this, &GameBase::_onDatablockModified );
@ -290,6 +314,9 @@ bool GameBase::onNewDataBlock( GameBaseData *dptr, bool reload )
if ( !mDataBlock )
return false;
// Don't set mask when new datablock is a temp-clone.
if (mDataBlock->isTempClone())
return true;
setMaskBits(DataBlockMask);
return true;
@ -543,6 +570,11 @@ U32 GameBase::packUpdate( NetConnection *connection, U32 mask, BitStream *stream
stream->writeFlag(mIsAiControlled);
#endif
if (stream->writeFlag(mask & ScopeIdMask))
{
if (stream->writeFlag(scope_refs > 0))
stream->writeInt(scope_id, SCOPE_ID_BITS);
}
return retMask;
}
@ -581,6 +613,11 @@ void GameBase::unpackUpdate(NetConnection *con, BitStream *stream)
mTicksSinceLastMove = 0;
mIsAiControlled = stream->readFlag();
#endif
if (stream->readFlag())
{
scope_id = (stream->readFlag()) ? (U16) stream->readInt(SCOPE_ID_BITS) : 0;
scope_refs = 0;
}
}
void GameBase::onMount( SceneObject *obj, S32 node )

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#ifndef _GAMEBASE_H_
#define _GAMEBASE_H_
@ -113,6 +118,8 @@ public:
DECLARE_CALLBACK( void, onMount, ( SceneObject* obj, SceneObject* mountObj, S32 node ) );
DECLARE_CALLBACK( void, onUnmount, ( SceneObject* obj, SceneObject* mountObj, S32 node ) );
/// @}
public:
GameBaseData(const GameBaseData&, bool = false);
};
//----------------------------------------------------------------------------
@ -229,7 +236,8 @@ public:
enum GameBaseMasks {
DataBlockMask = Parent::NextFreeMask << 0,
ExtendedInfoMask = Parent::NextFreeMask << 1,
NextFreeMask = Parent::NextFreeMask << 2
ScopeIdMask = Parent::NextFreeMask << 2,
NextFreeMask = Parent::NextFreeMask << 3,
};
// net flags added by game base
@ -453,6 +461,8 @@ private:
/// within this callback.
///
void _onDatablockModified();
protected:
void onScopeIdChange() { setMaskBits(ScopeIdMask); }
};

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "platform/platform.h"
#include "T3D/gameBase/gameConnection.h"
@ -52,6 +57,11 @@
#include "T3D/gameBase/std/stdMoveList.h"
#endif
#ifdef AFX_CAP_DATABLOCK_CACHE
#include "core/stream/fileStream.h"
#endif
#include "afx/arcaneFX.h"
//----------------------------------------------------------------------------
#define MAX_MOVE_PACKET_SENDS 4
@ -173,9 +183,26 @@ IMPLEMENT_CALLBACK( GameConnection, onFlash, void, (bool state), (state),
"either is on or both are off. Typically this is used to enable the flash postFx.\n\n"
"@param state Set to true if either the damage flash or white out conditions are active.\n\n");
#ifdef AFX_CAP_DATABLOCK_CACHE
StringTableEntry GameConnection::server_cache_filename = "";
StringTableEntry GameConnection::client_cache_filename = "";
bool GameConnection::server_cache_on = false;
bool GameConnection::client_cache_on = false;
#endif
//----------------------------------------------------------------------------
GameConnection::GameConnection()
{
mRolloverObj = NULL;
mPreSelectedObj = NULL;
mSelectedObj = NULL;
mChangedSelectedObj = false;
mPreSelectTimestamp = 0;
zoned_in = false;
#ifdef AFX_CAP_DATABLOCK_CACHE
client_db_stream = new InfiniteBitStream;
server_cache_CRC = 0xffffffff;
#endif
mLagging = false;
mControlObject = NULL;
mCameraObject = NULL;
@ -246,6 +273,10 @@ GameConnection::~GameConnection()
dFree(mConnectArgv[i]);
dFree(mJoinPassword);
delete mMoveList;
#ifdef AFX_CAP_DATABLOCK_CACHE
delete client_db_stream;
#endif
}
//----------------------------------------------------------------------------
@ -1144,6 +1175,17 @@ void GameConnection::readPacket(BitStream *bstream)
{
mMoveList->clientReadMovePacket(bstream);
// selected object - do we have a change in status?
if (bstream->readFlag())
{
if (bstream->readFlag())
{
S32 gIndex = bstream->readInt(NetConnection::GhostIdBitSize);
setSelectedObj(static_cast<SceneObject*>(resolveGhost(gIndex)));
}
else
setSelectedObj(NULL);
}
bool hadFlash = mDamageFlash > 0 || mWhiteOut > 0;
mDamageFlash = 0;
mWhiteOut = 0;
@ -1387,6 +1429,35 @@ void GameConnection::writePacket(BitStream *bstream, PacketNotify *note)
// all the damage flash & white out
S32 gIndex = -1;
if (mChangedSelectedObj)
{
S32 gidx;
// send NULL player
if ((mSelectedObj == NULL) || mSelectedObj.isNull())
{
bstream->writeFlag(true);
bstream->writeFlag(false);
mChangedSelectedObj = false;
}
// send ghost-idx
else if ((gidx = getGhostIndex(mSelectedObj)) != -1)
{
Con::printf("SEND OBJECT SELECTION");
bstream->writeFlag(true);
bstream->writeFlag(true);
bstream->writeInt(gidx, NetConnection::GhostIdBitSize);
mChangedSelectedObj = false;
}
// not fully changed yet
else
{
bstream->writeFlag(false);
mChangedSelectedObj = true;
}
}
else
bstream->writeFlag(false);
if (!mControlObject.isNull())
{
gIndex = getGhostIndex(mControlObject);
@ -1608,6 +1679,14 @@ void GameConnection::preloadNextDataBlock(bool hadNewFiles)
sendConnectionMessage(DataBlocksDownloadDone, mDataBlockSequence);
// gResourceManager->setMissingFileLogging(false);
#ifdef AFX_CAP_DATABLOCK_CACHE
// This should be the last of the datablocks. An argument of false
// indicates that this is a client save.
if (clientCacheEnabled())
saveDatablockCache(false);
#endif
return;
}
mFilesWereDownloaded = hadNewFiles;
@ -1771,7 +1850,11 @@ DefineEngineMethod( GameConnection, transmitDataBlocks, void, (S32 sequence),,
const U32 iCount = pGroup->size();
// If this is the local client...
#ifdef AFX_CAP_DATABLOCK_CACHE
if (GameConnection::getLocalClientConnection() == object && !GameConnection::serverCacheEnabled())
#else
if (GameConnection::getLocalClientConnection() == object)
#endif
{
// Set up a pointer to the datablock.
SimDataBlock* pDataBlock = 0;
@ -2166,6 +2249,13 @@ void GameConnection::consoleInit()
"@ingroup Networking\n");
// Con::addVariable("specialFog", TypeBool, &SceneGraph::useSpecial);
#ifdef AFX_CAP_DATABLOCK_CACHE
Con::addVariable("$Pref::Server::DatablockCacheFilename", TypeString, &server_cache_filename);
Con::addVariable("$pref::Client::DatablockCacheFilename", TypeString, &client_cache_filename);
Con::addVariable("$Pref::Server::EnableDatablockCache", TypeBool, &server_cache_on);
Con::addVariable("$pref::Client::EnableDatablockCache", TypeBool, &client_cache_on);
#endif
}
DefineEngineMethod( GameConnection, startRecording, void, (const char* fileName),,
@ -2360,4 +2450,522 @@ DefineEngineMethod( GameConnection, getVisibleGhostDistance, F32, (),,
)
{
return object->getVisibleGhostDistance();
}
}
// The object selection code here is, in part, based, on functionality described
// in the following resource:
// Object Selection in Torque by Dave Myers
// http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=7335
ConsoleMethod(GameConnection, setSelectedObj, bool, 3, 4, "(object, [propagate_to_client])")
{
SceneObject* pending_selection;
if (!Sim::findObject(argv[2], pending_selection))
return false;
bool propagate_to_client = (argc > 3) ? dAtob(argv[3]) : false;
object->setSelectedObj(pending_selection, propagate_to_client);
return true;
}
ConsoleMethod(GameConnection, getSelectedObj, S32, 2, 2, "()")
{
SimObject* selected = object->getSelectedObj();
return (selected) ? selected->getId(): -1;
}
ConsoleMethod(GameConnection, clearSelectedObj, void, 2, 3, "([propagate_to_client])")
{
bool propagate_to_client = (argc > 2) ? dAtob(argv[2]) : false;
object->setSelectedObj(NULL, propagate_to_client);
}
ConsoleMethod(GameConnection, setPreSelectedObjFromRollover, void, 2, 2, "()")
{
object->setPreSelectedObjFromRollover();
}
ConsoleMethod(GameConnection, clearPreSelectedObj, void, 2, 2, "()")
{
object->clearPreSelectedObj();
}
ConsoleMethod(GameConnection, setSelectedObjFromPreSelected, void, 2, 2, "()")
{
object->setSelectedObjFromPreSelected();
}
void GameConnection::setSelectedObj(SceneObject* so, bool propagate_to_client)
{
if (!isConnectionToServer())
{
// clear previously selected object
if (mSelectedObj)
clearNotify(mSelectedObj);
// save new selection
mSelectedObj = so;
// mark selected object
if (mSelectedObj)
deleteNotify(mSelectedObj);
// mark selection dirty
if (propagate_to_client)
mChangedSelectedObj = true;
return;
}
// clear previously selected object
if (mSelectedObj)
{
mSelectedObj->setSelectionFlags(mSelectedObj->getSelectionFlags() & ~SceneObject::SELECTED);
clearNotify(mSelectedObj);
Con::executef(this, "onObjectDeselected", mSelectedObj->getIdString());
}
// save new selection
mSelectedObj = so;
// mark selected object
if (mSelectedObj)
{
mSelectedObj->setSelectionFlags(mSelectedObj->getSelectionFlags() | SceneObject::SELECTED);
deleteNotify(mSelectedObj);
}
// mark selection dirty
//mChangedSelectedObj = true;
// notify appropriate script of the change
if (mSelectedObj)
Con::executef(this, "onObjectSelected", mSelectedObj->getIdString());
}
void GameConnection::setRolloverObj(SceneObject* so)
{
// save new selection
mRolloverObj = so;
// notify appropriate script of the change
Con::executef(this, "onObjectRollover", (mRolloverObj) ? mRolloverObj->getIdString() : "");
}
void GameConnection::setPreSelectedObjFromRollover()
{
mPreSelectedObj = mRolloverObj;
mPreSelectTimestamp = Platform::getRealMilliseconds();
}
void GameConnection::clearPreSelectedObj()
{
mPreSelectedObj = 0;
mPreSelectTimestamp = 0;
}
void GameConnection::setSelectedObjFromPreSelected()
{
U32 now = Platform::getRealMilliseconds();
if (now - mPreSelectTimestamp < arcaneFX::sTargetSelectionTimeoutMS)
setSelectedObj(mPreSelectedObj);
mPreSelectedObj = 0;
}
void GameConnection::onDeleteNotify(SimObject* obj)
{
if (obj == mSelectedObj)
setSelectedObj(NULL);
Parent::onDeleteNotify(obj);
}
#ifdef AFX_CAP_DATABLOCK_CACHE
void GameConnection::tempDisableStringBuffering(BitStream* bs) const
{
bs->setStringBuffer(0);
}
void GameConnection::restoreStringBuffering(BitStream* bs) const
{
bs->clearStringBuffer();
}
// rewind to stream postion and then move raw bytes into client_db_stream
// for caching purposes.
void GameConnection::repackClientDatablock(BitStream* bstream, S32 start_pos)
{
static U8 bit_buffer[Net::MaxPacketDataSize];
if (!clientCacheEnabled() || !client_db_stream)
return;
S32 cur_pos = bstream->getCurPos();
S32 n_bits = cur_pos - start_pos;
if (n_bits <= 0)
return;
bstream->setCurPos(start_pos);
bstream->readBits(n_bits, bit_buffer);
bstream->setCurPos(cur_pos);
//S32 start_pos2 = client_db_stream->getCurPos();
client_db_stream->writeBits(n_bits, bit_buffer);
}
#define CLIENT_CACHE_VERSION_CODE 47241113
void GameConnection::saveDatablockCache(bool on_server)
{
InfiniteBitStream bit_stream;
BitStream* bstream = 0;
if (on_server)
{
SimDataBlockGroup *g = Sim::getDataBlockGroup();
// find the first one we haven't sent:
U32 i, groupCount = g->size();
S32 key = this->getDataBlockModifiedKey();
for (i = 0; i < groupCount; i++)
if (((SimDataBlock*)(*g)[i])->getModifiedKey() > key)
break;
// nothing to save
if (i == groupCount)
return;
bstream = &bit_stream;
for (;i < groupCount; i++)
{
SimDataBlock* obj = (SimDataBlock*)(*g)[i];
GameConnection* gc = this;
NetConnection* conn = this;
SimObjectId id = obj->getId();
if (bstream->writeFlag(gc->getDataBlockModifiedKey() < obj->getModifiedKey())) // A - flag
{
if (obj->getModifiedKey() > gc->getMaxDataBlockModifiedKey())
gc->setMaxDataBlockModifiedKey(obj->getModifiedKey());
bstream->writeInt(id - DataBlockObjectIdFirst,DataBlockObjectIdBitSize); // B - int
S32 classId = obj->getClassId(conn->getNetClassGroup());
bstream->writeClassId(classId, NetClassTypeDataBlock, conn->getNetClassGroup()); // C - id
bstream->writeInt(i, DataBlockObjectIdBitSize); // D - int
bstream->writeInt(groupCount, DataBlockObjectIdBitSize + 1); // E - int
obj->packData(bstream);
}
}
}
else
{
bstream = client_db_stream;
}
if (bstream->getPosition() <= 0)
return;
// zero out any leftover bits short of an even byte count
U32 n_leftover_bits = (bstream->getPosition()*8) - bstream->getCurPos();
if (n_leftover_bits >= 0 && n_leftover_bits <= 8)
{
// note - an unusual problem regarding setCurPos() results when there
// are no leftover bytes. Adding a buffer byte in this case avoids the problem.
if (n_leftover_bits == 0)
n_leftover_bits = 8;
U8 bzero = 0;
bstream->writeBits(n_leftover_bits, &bzero);
}
// this is where we actually save the file
const char* filename = (on_server) ? server_cache_filename : client_cache_filename;
if (filename && filename[0] != '\0')
{
FileStream* f_stream;
if((f_stream = FileStream::createAndOpen(filename, Torque::FS::File::Write )) == NULL)
{
Con::printf("Failed to open file '%s'.", filename);
return;
}
U32 save_sz = bstream->getPosition();
if (!on_server)
{
f_stream->write((U32)CLIENT_CACHE_VERSION_CODE);
f_stream->write(save_sz);
f_stream->write(server_cache_CRC);
f_stream->write((U32)CLIENT_CACHE_VERSION_CODE);
}
f_stream->write(save_sz, bstream->getBuffer());
// zero out any leftover bytes short of a 4-byte multiple
while ((save_sz % 4) != 0)
{
f_stream->write((U8)0);
save_sz++;
}
delete f_stream;
}
if (!on_server)
client_db_stream->clear();
}
static bool afx_saved_db_cache = false;
static U32 afx_saved_db_cache_CRC = 0xffffffff;
void GameConnection::resetDatablockCache()
{
afx_saved_db_cache = false;
afx_saved_db_cache_CRC = 0xffffffff;
}
ConsoleFunction(resetDatablockCache, void, 1, 1, "resetDatablockCache()")
{
GameConnection::resetDatablockCache();
}
ConsoleFunction(isDatablockCacheSaved, bool, 1, 1, "resetDatablockCache()")
{
return afx_saved_db_cache;
}
ConsoleFunction(getDatablockCacheCRC, S32, 1, 1, "getDatablockCacheCRC()")
{
return (S32)afx_saved_db_cache_CRC;
}
ConsoleFunction(extractDatablockCacheCRC, S32, 2, 2, "extractDatablockCacheCRC(filename)")
{
FileStream f_stream;
const char* fileName = argv[1];
if(!f_stream.open(fileName, Torque::FS::File::Read))
{
Con::errorf("Failed to open file '%s'.", fileName);
return -1;
}
U32 stream_sz = f_stream.getStreamSize();
if (stream_sz < 4*32)
{
Con::errorf("File '%s' is not a valid datablock cache.", fileName);
f_stream.close();
return -1;
}
U32 pre_code; f_stream.read(&pre_code);
U32 save_sz; f_stream.read(&save_sz);
U32 crc_code; f_stream.read(&crc_code);
U32 post_code; f_stream.read(&post_code);
f_stream.close();
if (pre_code != post_code)
{
Con::errorf("File '%s' is not a valid datablock cache.", fileName);
return -1;
}
if (pre_code != (U32)CLIENT_CACHE_VERSION_CODE)
{
Con::errorf("Version of datablock cache file '%s' does not match version of running software.", fileName);
return -1;
}
return (S32)crc_code;
}
ConsoleFunction(setDatablockCacheCRC, void, 2, 2, "setDatablockCacheCRC(crc)")
{
GameConnection *conn = GameConnection::getConnectionToServer();
if(!conn)
return;
U32 crc_u = (U32)dAtoi(argv[1]);
conn->setServerCacheCRC(crc_u);
}
ConsoleMethod( GameConnection, saveDatablockCache, void, 2, 2, "saveDatablockCache()")
{
if (GameConnection::serverCacheEnabled() && !afx_saved_db_cache)
{
// Save the datablocks to a cache file. An argument
// of true indicates that this is a server save.
object->saveDatablockCache(true);
afx_saved_db_cache = true;
afx_saved_db_cache_CRC = 0xffffffff;
static char filename_buffer[1024];
String filename(Torque::Path::CleanSeparators(object->serverCacheFilename()));
Con::expandScriptFilename(filename_buffer, sizeof(filename_buffer), filename.c_str());
Torque::Path givenPath(Torque::Path::CompressPath(filename_buffer));
Torque::FS::FileNodeRef fileRef = Torque::FS::GetFileNode(givenPath);
if ( fileRef == NULL )
Con::errorf("saveDatablockCache() failed to get CRC for file '%s'.", filename.c_str());
else
afx_saved_db_cache_CRC = (S32)fileRef->getChecksum();
}
}
ConsoleMethod( GameConnection, loadDatablockCache, void, 2, 2, "loadDatablockCache()")
{
if (GameConnection::clientCacheEnabled())
{
object->loadDatablockCache();
}
}
ConsoleMethod( GameConnection, loadDatablockCache_Begin, bool, 2, 2, "loadDatablockCache_Begin()")
{
if (GameConnection::clientCacheEnabled())
{
return object->loadDatablockCache_Begin();
}
return false;
}
ConsoleMethod( GameConnection, loadDatablockCache_Continue, bool, 2, 2, "loadDatablockCache_Continue()")
{
if (GameConnection::clientCacheEnabled())
{
return object->loadDatablockCache_Continue();
}
return false;
}
static char* afx_db_load_buf = 0;
static U32 afx_db_load_buf_sz = 0;
static BitStream* afx_db_load_bstream = 0;
void GameConnection::loadDatablockCache()
{
if (!loadDatablockCache_Begin())
return;
while (loadDatablockCache_Continue())
;
}
bool GameConnection::loadDatablockCache_Begin()
{
if (!client_cache_filename || client_cache_filename[0] == '\0')
{
Con::errorf("No filename was specified for the client datablock cache.");
return false;
}
// open cache file
FileStream f_stream;
if(!f_stream.open(client_cache_filename, Torque::FS::File::Read))
{
Con::errorf("Failed to open file '%s'.", client_cache_filename);
return false;
}
// get file size
U32 stream_sz = f_stream.getStreamSize();
if (stream_sz <= 4*4)
{
Con::errorf("File '%s' is too small to be a valid datablock cache.", client_cache_filename);
f_stream.close();
return false;
}
// load header data
U32 pre_code; f_stream.read(&pre_code);
U32 save_sz; f_stream.read(&save_sz);
U32 crc_code; f_stream.read(&crc_code);
U32 post_code; f_stream.read(&post_code);
// validate header info
if (pre_code != post_code)
{
Con::errorf("File '%s' is not a valid datablock cache.", client_cache_filename);
f_stream.close();
return false;
}
if (pre_code != (U32)CLIENT_CACHE_VERSION_CODE)
{
Con::errorf("Version of datablock cache file '%s' does not match version of running software.", client_cache_filename);
f_stream.close();
return false;
}
// allocated the in-memory buffer
afx_db_load_buf_sz = stream_sz - (4*4);
afx_db_load_buf = new char[afx_db_load_buf_sz];
// load data from file into memory
if (!f_stream.read(stream_sz, afx_db_load_buf))
{
Con::errorf("Failed to read data from file '%s'.", client_cache_filename);
f_stream.close();
delete [] afx_db_load_buf;
afx_db_load_buf = 0;
afx_db_load_buf_sz = 0;
return false;
}
// close file
f_stream.close();
// At this point we have the whole cache in memory
// create a bitstream from the in-memory buffer
afx_db_load_bstream = new BitStream(afx_db_load_buf, afx_db_load_buf_sz);
return true;
}
bool GameConnection::loadDatablockCache_Continue()
{
if (!afx_db_load_bstream)
return false;
// prevent repacking of datablocks during load
BitStream* save_client_db_stream = client_db_stream;
client_db_stream = 0;
bool all_finished = false;
// loop through at most 16 datablocks
BitStream *bstream = afx_db_load_bstream;
for (S32 i = 0; i < 16; i++)
{
S32 save_pos = bstream->getCurPos();
if (!bstream->readFlag())
{
all_finished = true;
break;
}
bstream->setCurPos(save_pos);
SimDataBlockEvent evt;
evt.unpack(this, bstream);
evt.process(this);
}
client_db_stream = save_client_db_stream;
if (all_finished)
{
delete afx_db_load_bstream;
afx_db_load_bstream = 0;
delete [] afx_db_load_buf;
afx_db_load_buf = 0;
afx_db_load_buf_sz = 0;
return false;
}
return true;
}
#endif

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#ifndef _GAMECONNECTION_H_
#define _GAMECONNECTION_H_
@ -55,6 +60,14 @@ class MoveList;
struct Move;
struct AuthInfo;
// To disable datablock caching, remove or comment out the AFX_CAP_DATABLOCK_CACHE define below.
// Also, at a minimum, the following script preferences should be set to false:
// $pref::Client::EnableDatablockCache = false; (in arcane.fx/client/defaults.cs)
// $Pref::Server::EnableDatablockCache = false; (in arcane.fx/server/defaults.cs)
// Alternatively, all script code marked with "DATABLOCK CACHE CODE" can be removed or
// commented out.
//
#define AFX_CAP_DATABLOCK_CACHE
const F32 MinCameraFov = 1.f; ///< min camera FOV
const F32 MaxCameraFov = 179.f; ///< max camera FOV
@ -372,6 +385,61 @@ protected:
DECLARE_CALLBACK( void, setLagIcon, (bool state) );
DECLARE_CALLBACK( void, onDataBlocksDone, (U32 sequence) );
DECLARE_CALLBACK( void, onFlash, (bool state) );
// GameConnection is modified to keep track of object selections which are used in
// spell targeting. This code stores the current object selection as well as the
// current rollover object beneath the cursor. The rollover object is treated as a
// pending object selection and actual object selection is usually made by promoting
// the rollover object to the current object selection.
private:
SimObjectPtr<SceneObject> mRolloverObj;
SimObjectPtr<SceneObject> mPreSelectedObj;
SimObjectPtr<SceneObject> mSelectedObj;
bool mChangedSelectedObj;
U32 mPreSelectTimestamp;
protected:
virtual void onDeleteNotify(SimObject*);
public:
void setRolloverObj(SceneObject*);
SceneObject* getRolloverObj() { return mRolloverObj; }
void setSelectedObj(SceneObject*, bool propagate_to_client=false);
SceneObject* getSelectedObj() { return mSelectedObj; }
void setPreSelectedObjFromRollover();
void clearPreSelectedObj();
void setSelectedObjFromPreSelected();
// Flag is added to indicate when a client is fully connected or "zoned-in".
// This information determines when AFX will startup active effects on a newly
// added client.
private:
bool zoned_in;
public:
bool isZonedIn() const { return zoned_in; }
void setZonedIn() { zoned_in = true; }
#ifdef AFX_CAP_DATABLOCK_CACHE
private:
static StringTableEntry server_cache_filename;
static StringTableEntry client_cache_filename;
static bool server_cache_on;
static bool client_cache_on;
BitStream* client_db_stream;
U32 server_cache_CRC;
public:
void repackClientDatablock(BitStream*, S32 start_pos);
void saveDatablockCache(bool on_server);
void loadDatablockCache();
bool loadDatablockCache_Begin();
bool loadDatablockCache_Continue();
void tempDisableStringBuffering(BitStream* bs) const;
void restoreStringBuffering(BitStream* bs) const;
void setServerCacheCRC(U32 crc) { server_cache_CRC = crc; }
static void resetDatablockCache();
static bool serverCacheEnabled() { return server_cache_on; }
static bool clientCacheEnabled() { return client_cache_on; }
static const char* serverCacheFilename() { return server_cache_filename; }
static const char* clientCacheFilename() { return client_cache_filename; }
#endif
};
#endif

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "platform/platform.h"
#include "core/dnet.h"
#include "core/stream/bitStream.h"
@ -136,6 +141,9 @@ void SimDataBlockEvent::notifyDelivered(NetConnection *conn, bool )
void SimDataBlockEvent::pack(NetConnection *conn, BitStream *bstream)
{
#ifdef AFX_CAP_DATABLOCK_CACHE
((GameConnection *)conn)->tempDisableStringBuffering(bstream);
#endif
SimDataBlock* obj;
Sim::findObject(id,obj);
GameConnection *gc = (GameConnection *) conn;
@ -157,10 +165,18 @@ void SimDataBlockEvent::pack(NetConnection *conn, BitStream *bstream)
bstream->writeInt(classId ^ DebugChecksum, 32);
#endif
}
#ifdef AFX_CAP_DATABLOCK_CACHE
((GameConnection *)conn)->restoreStringBuffering(bstream);
#endif
}
void SimDataBlockEvent::unpack(NetConnection *cptr, BitStream *bstream)
{
#ifdef AFX_CAP_DATABLOCK_CACHE
// stash the stream position prior to unpacking
S32 start_pos = bstream->getCurPos();
((GameConnection *)cptr)->tempDisableStringBuffering(bstream);
#endif
if(bstream->readFlag())
{
mProcess = true;
@ -215,6 +231,11 @@ void SimDataBlockEvent::unpack(NetConnection *cptr, BitStream *bstream)
#endif
}
#ifdef AFX_CAP_DATABLOCK_CACHE
// rewind to stream position and then process raw bytes for caching
((GameConnection *)cptr)->repackClientDatablock(bstream, start_pos);
((GameConnection *)cptr)->restoreStringBuffering(bstream);
#endif
}
void SimDataBlockEvent::write(NetConnection *cptr, BitStream *bstream)

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "platform/platform.h"
#include "T3D/gameBase/processList.h"
@ -284,5 +289,20 @@ void ProcessList::advanceObjects()
PROFILE_END();
}
ProcessObject* ProcessList::findNearestToEnd(Vector<ProcessObject*>& objs) const
{
if (objs.empty())
return 0;
for (ProcessObject* obj = mHead.mProcessLink.prev; obj != &mHead; obj = obj->mProcessLink.prev)
{
for (S32 i = 0; i < objs.size(); i++)
{
if (obj == objs[i])
return obj;
}
}
return 0;
}

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#ifndef _PROCESSLIST_H_
#define _PROCESSLIST_H_
@ -188,6 +193,9 @@ protected:
PreTickSignal mPreTick;
PostTickSignal mPostTick;
// JTF: still needed?
public:
ProcessObject* findNearestToEnd(Vector<ProcessObject*>& objs) const;
};
#endif // _PROCESSLIST_H_

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "platform/platform.h"
#include "T3D/groundPlane.h"
@ -40,6 +45,7 @@
#include "T3D/physics/physicsBody.h"
#include "T3D/physics/physicsCollision.h"
#include "afx/ce/afxZodiacMgr.h"
/// Minimum square size allowed. This is a cheap way to limit the amount
/// of geometry possibly generated by the GroundPlane (vertex buffers have a
@ -77,6 +83,7 @@ GroundPlane::GroundPlane()
mNetFlags.set( Ghostable | ScopeAlways );
mConvexList = new Convex;
mTypeMask |= TerrainLikeObjectType;
}
GroundPlane::~GroundPlane()
@ -356,6 +363,7 @@ void GroundPlane::prepRenderImage( SceneRenderState* state )
if( mVertexBuffer.isNull() )
return;
afxZodiacMgr::renderGroundPlaneZodiacs(state, this);
// Add a render instance.
RenderPassManager* pass = state->getRenderPass();

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "platform/platform.h"
#include "T3D/lightBase.h"
@ -73,6 +78,7 @@ LightBase::LightBase()
mLight = LightManager::createLightInfo();
mFlareState.clear();
mLocalRenderViz = false;
}
LightBase::~LightBase()
@ -206,7 +212,7 @@ void LightBase::prepRenderImage( SceneRenderState *state )
// If the light is selected or light visualization
// is enabled then register the callback.
if ( smRenderViz || isSelectedInEditor )
if ( mLocalRenderViz || smRenderViz || isSelectedInEditor )
{
ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
ri->renderDelegate.bind( this, &LightBase::_onRenderViz );

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#ifndef _LIGHTBASE_H_
#define _LIGHTBASE_H_
@ -132,6 +137,8 @@ public:
virtual void pauseAnimation( void );
virtual void playAnimation( void );
virtual void playAnimation( LightAnimData *animData );
protected:
bool mLocalRenderViz;
};
#endif // _LIGHTBASE_H_

View file

@ -20,11 +20,19 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#ifndef _OBJECTTYPES_H_
#define _OBJECTTYPES_H_
#include "platform/types.h"
// Uncomment the AFX_CAP_AFXMODEL_TYPE define below to enable a type flag
// for afxModel objects.
//#define AFX_CAP_AFXMODEL_TYPE
/// Types used for SceneObject type masks (SceneObject::mTypeMask)
///
/// @note If a new object type is added, don't forget to add it to
@ -149,6 +157,11 @@ enum SceneObjectTypes
EntityObjectType = BIT(23),
/// @}
InteriorLikeObjectType = BIT(24),
TerrainLikeObjectType = BIT(25),
#if defined(AFX_CAP_AFXMODEL_TYPE)
afxModelObjectType = BIT(26)
#endif
};
enum SceneObjectTypeMasks : U32

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "T3D/physicalZone.h"
#include "core/stream/bitStream.h"
#include "collision/boxConvex.h"
@ -33,6 +38,8 @@
#include "gfx/gfxDrawUtil.h"
#include "console/engineAPI.h"
//#include "console/engineTypes.h"
#include "sim/netConnection.h"
IMPLEMENT_CO_NETOBJECT_V1(PhysicalZone);
ConsoleDocClass( PhysicalZone,
@ -103,6 +110,10 @@ PhysicalZone::PhysicalZone()
mConvexList = new Convex;
mActive = true;
force_type = VECTOR;
force_mag = 0.0f;
orient_force = false;
fade_amt = 1.0f;
}
PhysicalZone::~PhysicalZone()
@ -111,6 +122,16 @@ PhysicalZone::~PhysicalZone()
mConvexList = NULL;
}
ImplementEnumType( PhysicalZone_ForceType, "Possible physical zone force types.\n" "@ingroup PhysicalZone\n\n" )
{ PhysicalZone::VECTOR, "vector", "..." },
{ PhysicalZone::SPHERICAL, "spherical", "..." },
{ PhysicalZone::CYLINDRICAL, "cylindrical", "..." },
// aliases
{ PhysicalZone::SPHERICAL, "sphere", "..." },
{ PhysicalZone::CYLINDRICAL, "cylinder", "..." },
EndImplementEnumType;
//--------------------------------------------------------------------------
void PhysicalZone::consoleInit()
{
@ -129,6 +150,10 @@ void PhysicalZone::initPersistFields()
"point followed by three vectors representing the edges extending from the corner." );
endGroup("Misc");
addGroup("AFX");
addField("forceType", TYPEID<PhysicalZone::ForceType>(), Offset(force_type, PhysicalZone));
addField("orientForce", TypeBool, Offset(orient_force, PhysicalZone));
endGroup("AFX");
Parent::initPersistFields();
}
@ -158,6 +183,19 @@ bool PhysicalZone::onAdd()
Polyhedron temp = mPolyhedron;
setPolyhedron(temp);
switch (force_type)
{
case SPHERICAL:
force_mag = mAppliedForce.magnitudeSafe();
break;
case CYLINDRICAL:
{
Point3F force_vec = mAppliedForce;
force_vec.z = 0.0;
force_mag = force_vec.magnitudeSafe();
}
break;
}
addToScene();
return true;
@ -191,7 +229,7 @@ void PhysicalZone::setTransform(const MatrixF & mat)
mClippedList.setBaseTransform(base);
if (isServerObject())
setMaskBits(InitialUpdateMask);
setMaskBits(MoveMask);
}
@ -242,12 +280,8 @@ U32 PhysicalZone::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
U32 i;
U32 retMask = Parent::packUpdate(con, mask, stream);
if (stream->writeFlag((mask & InitialUpdateMask) != 0)) {
// Note that we don't really care about efficiency here, since this is an
// edit-only ghost...
mathWrite(*stream, mObjToWorld);
mathWrite(*stream, mObjScale);
if (stream->writeFlag(mask & PolyhedronMask))
{
// Write the polyhedron
stream->write(mPolyhedron.pointList.size());
for (i = 0; i < mPolyhedron.pointList.size(); i++)
@ -266,32 +300,44 @@ U32 PhysicalZone::packUpdate(NetConnection* con, U32 mask, BitStream* stream)
stream->write(rEdge.vertex[0]);
stream->write(rEdge.vertex[1]);
}
}
if (stream->writeFlag(mask & MoveMask))
{
stream->writeAffineTransform(mObjToWorld);
mathWrite(*stream, mObjScale);
}
if (stream->writeFlag(mask & SettingsMask))
{
stream->write(mVelocityMod);
stream->write(mGravityMod);
mathWrite(*stream, mAppliedForce);
stream->writeFlag(mActive);
} else {
stream->writeFlag(mActive);
stream->writeInt(force_type, FORCE_TYPE_BITS);
stream->writeFlag(orient_force);
}
return retMask;
if (stream->writeFlag(mask & FadeMask))
{
U8 fade_byte = (U8)(fade_amt*255.0f);
stream->write(fade_byte);
}
stream->writeFlag(mActive);
return retMask;
}
void PhysicalZone::unpackUpdate(NetConnection* con, BitStream* stream)
{
Parent::unpackUpdate(con, stream);
if (stream->readFlag()) {
bool new_ph = false;
if (stream->readFlag()) // PolyhedronMask
{
U32 i, size;
MatrixF temp;
Point3F tempScale;
Polyhedron tempPH;
// Transform
mathRead(*stream, &temp);
mathRead(*stream, &tempScale);
// Read the polyhedron
stream->read(&size);
tempPH.pointList.setSize(size);
@ -314,17 +360,46 @@ void PhysicalZone::unpackUpdate(NetConnection* con, BitStream* stream)
stream->read(&rEdge.vertex[1]);
}
setPolyhedron(tempPH);
new_ph = true;
}
if (stream->readFlag()) // MoveMask
{
MatrixF temp;
stream->readAffineTransform(&temp);
Point3F tempScale;
mathRead(*stream, &tempScale);
//if (!new_ph)
//{
// Polyhedron rPolyhedron = mPolyhedron;
// setPolyhedron(rPolyhedron);
//}
setScale(tempScale);
setTransform(temp);
}
if (stream->readFlag()) //SettingsMask
{
stream->read(&mVelocityMod);
stream->read(&mGravityMod);
mathRead(*stream, &mAppliedForce);
setPolyhedron(tempPH);
setScale(tempScale);
setTransform(temp);
mActive = stream->readFlag();
} else {
mActive = stream->readFlag();
force_type = stream->readInt(FORCE_TYPE_BITS); // AFX
orient_force = stream->readFlag(); // AFX
}
if (stream->readFlag()) //FadeMask
{
U8 fade_byte;
stream->read(&fade_byte);
fade_amt = ((F32)fade_byte)/255.0f;
}
else
fade_amt = 1.0f;
mActive = stream->readFlag();
}
@ -443,3 +518,104 @@ void PhysicalZone::deactivate()
mActive = false;
}
void PhysicalZone::onStaticModified(const char* slotName, const char*newValue)
{
if (dStricmp(slotName, "appliedForce") == 0 || dStricmp(slotName, "forceType") == 0)
{
switch (force_type)
{
case SPHERICAL:
force_mag = mAppliedForce.magnitudeSafe();
break;
case CYLINDRICAL:
{
Point3F force_vec = mAppliedForce;
force_vec.z = 0.0;
force_mag = force_vec.magnitudeSafe();
}
break;
}
}
}
const Point3F& PhysicalZone::getForce(const Point3F* center) const
{
static Point3F force_vec;
if (force_type == VECTOR)
{
if (orient_force)
{
getTransform().mulV(mAppliedForce, &force_vec);
force_vec *= fade_amt;
return force_vec;
}
force_vec = mAppliedForce;
force_vec *= fade_amt;
return force_vec;
}
if (!center)
{
force_vec.zero();
return force_vec;
}
if (force_type == SPHERICAL)
{
force_vec = *center - getPosition();
force_vec.normalizeSafe();
force_vec *= force_mag*fade_amt;
return force_vec;
}
if (orient_force)
{
force_vec = *center - getPosition();
getWorldTransform().mulV(force_vec);
force_vec.z = 0.0f;
force_vec.normalizeSafe();
force_vec *= force_mag;
force_vec.z = mAppliedForce.z;
getTransform().mulV(force_vec);
force_vec *= fade_amt;
return force_vec;
}
force_vec = *center - getPosition();
force_vec.z = 0.0f;
force_vec.normalizeSafe();
force_vec *= force_mag;
force_vec *= fade_amt;
return force_vec;
}
bool PhysicalZone::isExcludedObject(SceneObject* obj) const
{
for (S32 i = 0; i < excluded_objects.size(); i++)
if (excluded_objects[i] == obj)
return true;
return false;
}
void PhysicalZone::registerExcludedObject(SceneObject* obj)
{
if (isExcludedObject(obj))
return;
excluded_objects.push_back(obj);
setMaskBits(FadeMask);
}
void PhysicalZone::unregisterExcludedObject(SceneObject* obj)
{
for (S32 i = 0; i < excluded_objects.size(); i++)
if (excluded_objects[i] == obj)
{
excluded_objects.erase(i);
setMaskBits(FadeMask);
return;
}
}

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#ifndef _H_PHYSICALZONE
#define _H_PHYSICALZONE
@ -40,9 +45,14 @@ class PhysicalZone : public SceneObject
{
typedef SceneObject Parent;
enum UpdateMasks {
enum UpdateMasks {
ActiveMask = Parent::NextFreeMask << 0,
NextFreeMask = Parent::NextFreeMask << 1
SettingsMask = Parent::NextFreeMask << 1,
FadeMask = Parent::NextFreeMask << 2,
PolyhedronMask = Parent::NextFreeMask << 3,
MoveMask = Parent::NextFreeMask << 4,
ExclusionMask = Parent::NextFreeMask << 5,
NextFreeMask = Parent::NextFreeMask << 6
};
protected:
@ -83,7 +93,10 @@ class PhysicalZone : public SceneObject
inline F32 getVelocityMod() const { return mVelocityMod; }
inline F32 getGravityMod() const { return mGravityMod; }
inline const Point3F& getForce() const { return mAppliedForce; }
// the scene object is now passed in to getForce() where
// it is needed to calculate the applied force when the
// force is radial.
const Point3F& getForce(const Point3F* center=0) const;
void setPolyhedron(const Polyhedron&);
bool testObject(SceneObject*);
@ -96,7 +109,25 @@ class PhysicalZone : public SceneObject
void deactivate();
inline bool isActive() const { return mActive; }
protected:
friend class afxPhysicalZoneData;
friend class afxEA_PhysicalZone;
Vector<SceneObject*> excluded_objects;
S32 force_type;
F32 force_mag;
bool orient_force;
F32 fade_amt;
void setFadeAmount(F32 amt) { fade_amt = amt; if (fade_amt < 1.0f) setMaskBits(FadeMask); }
public:
enum ForceType { VECTOR, SPHERICAL, CYLINDRICAL };
enum { FORCE_TYPE_BITS = 2 };
virtual void onStaticModified(const char* slotName, const char*newValue = NULL);
bool isExcludedObject(SceneObject*) const;
void registerExcludedObject(SceneObject*);
void unregisterExcludedObject(SceneObject*);
};
typedef PhysicalZone::ForceType PhysicalZone_ForceType;
DefineEnumType( PhysicalZone_ForceType );
#endif // _H_PHYSICALZONE

View file

@ -20,6 +20,10 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "platform/platform.h"
#include "T3D/player.h"
@ -1656,6 +1660,8 @@ Player::Player()
mLastAbsoluteYaw = 0.0f;
mLastAbsolutePitch = 0.0f;
mLastAbsoluteRoll = 0.0f;
afx_init();
}
Player::~Player()
@ -2070,6 +2076,32 @@ void Player::processTick(const Move* move)
}
Parent::processTick(move);
// Check for state changes in the standard move triggers and
// set bits for any triggers that switched on this tick in
// the fx_s_triggers mask. Flag any changes to be packed to
// clients.
if (isServerObject())
{
fx_s_triggers = 0;
if (move)
{
U8 on_bits = 0;
for (S32 i = 0; i < MaxTriggerKeys; i++)
if (move->trigger[i])
on_bits |= BIT(i);
if (on_bits != move_trigger_states)
{
U8 switched_on_bits = (on_bits & ~move_trigger_states);
if (switched_on_bits)
{
fx_s_triggers |= (U32)switched_on_bits;
setMaskBits(TriggerMask);
}
move_trigger_states = on_bits;
}
}
}
// Warp to catch up to server
if (delta.warpTicks > 0) {
delta.warpTicks--;
@ -2085,7 +2117,10 @@ void Player::processTick(const Move* move)
else if (delta.rot.z > M_PI_F)
delta.rot.z -= M_2PI_F;
setPosition(delta.pos,delta.rot);
if (!ignore_updates)
{
setPosition(delta.pos,delta.rot);
}
updateDeathOffsets();
updateLookAnimation();
@ -2183,7 +2218,8 @@ void Player::interpolateTick(F32 dt)
Point3F pos = delta.pos + delta.posVec * dt;
Point3F rot = delta.rot + delta.rotVec * dt;
setRenderPosition(pos,rot,dt);
if (!ignore_updates)
setRenderPosition(pos,rot,dt);
/*
// apply camera effects - is this the best place? - bramage
@ -2208,6 +2244,9 @@ void Player::advanceTime(F32 dt)
{
// Client side animations
Parent::advanceTime(dt);
// Increment timer for triggering idle events.
if (idle_timer >= 0.0f)
idle_timer += dt;
updateActionThread();
updateAnimation(dt);
updateSplash();
@ -2498,6 +2537,30 @@ AngAxisF gPlayerMoveRot;
void Player::updateMove(const Move* move)
{
struct Move my_move;
if (override_movement && movement_op < 3)
{
my_move = *move;
switch (movement_op)
{
case 0: // add
my_move.x += movement_data.x;
my_move.y += movement_data.y;
my_move.z += movement_data.z;
break;
case 1: // mult
my_move.x *= movement_data.x;
my_move.y *= movement_data.y;
my_move.z *= movement_data.z;
break;
case 2: // replace
my_move.x = movement_data.x;
my_move.y = movement_data.y;
my_move.z = movement_data.z;
break;
}
move = &my_move;
}
delta.move = *move;
#ifdef TORQUE_OPENVR
@ -2740,7 +2803,12 @@ void Player::updateMove(const Move* move)
// Desired move direction & speed
VectorF moveVec;
F32 moveSpeed;
if ((mState == MoveState || (mState == RecoverState && mDataBlock->recoverRunForceScale > 0.0f)) && mDamageState == Enabled)
// If BLOCK_USER_CONTROL is set in anim_clip_flags, the user won't be able to
// resume control over the player character. This generally happens for
// short periods of time synchronized with script driven animation at places
// where it makes sense that user motion is prohibited, such as when the
// player is lifted off the ground or knocked down.
if ((mState == MoveState || (mState == RecoverState && mDataBlock->recoverRunForceScale > 0.0f)) && mDamageState == Enabled && !isAnimationLocked())
{
zRot.getColumn(0,&moveVec);
moveVec *= (move->x * (mPose == SprintPose ? mDataBlock->sprintStrafeScale : 1.0f));
@ -2799,6 +2867,9 @@ void Player::updateMove(const Move* move)
moveSpeed = 0.0f;
}
// apply speed bias here.
speed_bias = speed_bias + (speed_bias_goal - speed_bias)*0.1f;
moveSpeed *= speed_bias;
// Acceleration due to gravity
VectorF acc(0.0f, 0.0f, mGravity * mGravityMod * TickSec);
@ -3025,7 +3096,9 @@ void Player::updateMove(const Move* move)
mContactTimer++;
// Acceleration from Jumping
if (move->trigger[sJumpTrigger] && canJump())// !isMounted() &&
// While BLOCK_USER_CONTROL is set in anim_clip_flags, the user won't be able to
// make the player character jump.
if (move->trigger[sJumpTrigger] && canJump() && !isAnimationLocked())
{
// Scale the jump impulse base on maxJumpSpeed
F32 zSpeedScale = mVelocity.z;
@ -3076,6 +3149,9 @@ void Player::updateMove(const Move* move)
setActionThread( seq, true, false, true );
mJumpSurfaceLastContact = JumpSkipContactsMax;
// Flag the jump event trigger.
fx_s_triggers |= PLAYER_JUMP_S_TRIGGER;
setMaskBits(TriggerMask);
}
}
else
@ -3493,6 +3569,19 @@ void Player::updateDamageState()
void Player::updateLookAnimation(F32 dt)
{
// If the preference setting overrideLookAnimation is true, the player's
// arm and head no longer animate according to the view direction. They
// are instead given fixed positions.
if (overrideLookAnimation)
{
if (mArmAnimation.thread)
mShapeInstance->setPos(mArmAnimation.thread, armLookOverridePos);
if (mHeadVThread)
mShapeInstance->setPos(mHeadVThread, headVLookOverridePos);
if (mHeadHThread)
mShapeInstance->setPos(mHeadHThread, headHLookOverridePos);
return;
}
// Calculate our interpolated head position.
Point3F renderHead = delta.head + delta.headVec * dt;
@ -3533,6 +3622,8 @@ void Player::updateLookAnimation(F32 dt)
bool Player::inDeathAnim()
{
if ((anim_clip_flags & ANIM_OVERRIDDEN) != 0 && (anim_clip_flags & IS_DEATH_ANIM) == 0)
return false;
if (mActionAnimation.thread && mActionAnimation.action >= 0)
if (mActionAnimation.action < mDataBlock->actionCount)
return mDataBlock->actionList[mActionAnimation.action].death;
@ -3742,6 +3833,8 @@ bool Player::setArmThread(U32 action)
bool Player::setActionThread(const char* sequence,bool hold,bool wait,bool fsp)
{
if (anim_clip_flags & ANIM_OVERRIDDEN)
return false;
for (U32 i = 1; i < mDataBlock->actionCount; i++)
{
PlayerData::ActionAnimation &anim = mDataBlock->actionList[i];
@ -3766,6 +3859,11 @@ void Player::setActionThread(U32 action,bool forward,bool hold,bool wait,bool fs
return;
}
if (isClientObject())
{
mark_idle = (action == PlayerData::RootAnim);
idle_timer = (mark_idle) ? 0.0f : -1.0f;
}
PlayerData::ActionAnimation &anim = mDataBlock->actionList[action];
if (anim.sequence != -1)
{
@ -3858,7 +3956,8 @@ void Player::updateActionThread()
offset = mDataBlock->decalOffset * getScale().x;
}
if( triggeredLeft || triggeredRight )
process_client_triggers(triggeredLeft, triggeredRight);
if ((triggeredLeft || triggeredRight) && !noFootfallFX)
{
Point3F rot, pos;
RayInfo rInfo;
@ -3875,7 +3974,7 @@ void Player::updateActionThread()
// Put footprints on surface, if appropriate for material.
if( material && material->mShowFootprints
&& mDataBlock->decalData )
&& mDataBlock->decalData && !footfallDecalOverride )
{
Point3F normal;
Point3F tangent;
@ -3886,8 +3985,8 @@ void Player::updateActionThread()
// Emit footpuffs.
if( rInfo.t <= 0.5 && mWaterCoverage == 0.0
&& material && material->mShowDust )
if (!footfallDustOverride && rInfo.t <= 0.5f && mWaterCoverage == 0.0f
&& material && material->mShowDust )
{
// New emitter every time for visibility reasons
ParticleEmitter * emitter = new ParticleEmitter;
@ -3920,6 +4019,7 @@ void Player::updateActionThread()
// Play footstep sound.
if (footfallSoundOverride <= 0)
playFootstepSound( triggeredLeft, material, rInfo.object );
}
}
@ -3941,8 +4041,10 @@ void Player::updateActionThread()
pickActionAnimation();
}
// prevent scaling of AFX picked actions
if ( (mActionAnimation.action != PlayerData::LandAnim) &&
(mActionAnimation.action != PlayerData::NullAnimation) )
(mActionAnimation.action != PlayerData::NullAnimation) &&
!(anim_clip_flags & ANIM_OVERRIDDEN))
{
// Update action animation time scale to match ground velocity
PlayerData::ActionAnimation &anim =
@ -4560,6 +4662,10 @@ void Player::updateAnimation(F32 dt)
if (mImageStateThread)
mShapeInstance->advanceTime(dt,mImageStateThread);
// update any active blend clips
if (isGhost())
for (S32 i = 0; i < blend_clips.size(); i++)
mShapeInstance->advanceTime(dt, blend_clips[i].thread);
// If we are the client's player on this machine, then we need
// to make sure the transforms are up to date as they are used
// to setup the camera.
@ -4573,6 +4679,11 @@ void Player::updateAnimation(F32 dt)
else
{
updateAnimationTree(false);
// This addition forces recently visible players to animate their
// skeleton now rather than in pre-render so that constrained effects
// get up-to-date node transforms.
if (didRenderLastRender())
mShapeInstance->animate();
}
}
}
@ -4878,6 +4989,11 @@ Point3F Player::_move( const F32 travelTime, Collision *outCol )
// we can use it to do impacts
// and query collision.
*outCol = *collision;
if (isServerObject() && bd > 6.8f && collision->normal.z > 0.7f)
{
fx_s_triggers |= PLAYER_LANDING_S_TRIGGER;
setMaskBits(TriggerMask);
}
// Subtract out velocity
VectorF dv = collision->normal * (bd + sNormalElasticity);
@ -5879,7 +5995,12 @@ void Player::applyImpulse(const Point3F&,const VectorF& vec)
bool Player::castRay(const Point3F &start, const Point3F &end, RayInfo* info)
{
if (getDamageState() != Enabled)
// In standard Torque there's a rather brute force culling of all
// non-enabled players (corpses) from the ray cast. But, to
// demonstrate a resurrection spell, we need corpses to be
// selectable, so this code change allows consideration of corpses
// in the ray cast if corpsesHiddenFromRayCast is set to false.
if (sCorpsesHiddenFromRayCast && getDamageState() != Enabled)
return false;
// Collide against bounding box. Need at least this for the editor.
@ -6146,7 +6267,8 @@ void Player::readPacketData(GameConnection *connection, BitStream *stream)
stream->read(&mHead.z);
stream->read(&rot.z);
rot.x = rot.y = 0;
setPosition(pos,rot);
if (!ignore_updates)
setPosition(pos,rot);
delta.head = mHead;
delta.rot = rot;
@ -6188,6 +6310,7 @@ U32 Player::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
mArmAnimation.action != mDataBlock->lookAction))) {
stream->writeInt(mArmAnimation.action,PlayerData::ActionAnimBits);
}
retMask = afx_packUpdate(con, mask, stream, retMask);
// The rest of the data is part of the control object packet update.
// If we're controlled by this client, we don't need to send it.
@ -6292,6 +6415,7 @@ void Player::unpackUpdate(NetConnection *con, BitStream *stream)
mArmAnimation.action = action;
}
afx_unpackUpdate(con, stream);
// Done if controlled by client ( and not initial update )
if(stream->readFlag())
return;
@ -6391,7 +6515,8 @@ void Player::unpackUpdate(NetConnection *con, BitStream *stream)
}
delta.pos = pos;
delta.rot = rot;
setPosition(pos,rot);
if (!ignore_updates)
setPosition(pos,rot);
}
}
else
@ -6403,7 +6528,8 @@ void Player::unpackUpdate(NetConnection *con, BitStream *stream)
delta.rotVec.set(0.0f, 0.0f, 0.0f);
delta.warpTicks = 0;
delta.dt = 0.0f;
setPosition(pos,rot);
if (!ignore_updates)
setPosition(pos,rot);
}
}
F32 energy = stream->readFloat(EnergyLevelBits) * mDataBlock->maxEnergy;
@ -6819,6 +6945,7 @@ void Player::consoleInit()
Con::addVariable("$player::extendedMoveHeadPosRotIndex", TypeS32, &smExtendedMoveHeadPosRotIndex,
"@brief The ExtendedMove position/rotation index used for head movements.\n\n"
"@ingroup GameObjects\n");
afx_consoleInit();
}
//--------------------------------------------------------------------------
@ -6863,6 +6990,8 @@ void Player::calcClassRenderData()
void Player::playFootstepSound( bool triggeredLeft, Material* contactMaterial, SceneObject* contactObject )
{
if (footfallSoundOverride > 0)
return;
MatrixF footMat = getTransform();
if( mWaterCoverage > 0.0 )
{
@ -7172,6 +7301,340 @@ void Player::renderConvex( ObjectRenderInst *ri, SceneRenderState *state, BaseMa
GFX->leaveDebugEvent();
}
// static
bool Player::sCorpsesHiddenFromRayCast = true; // this default matches stock Torque behavior.
// static
void Player::afx_consoleInit()
{
Con::addVariable("pref::Player::corpsesHiddenFromRayCast", TypeBool, &sCorpsesHiddenFromRayCast);
}
void Player::afx_init()
{
overrideLookAnimation = false;
armLookOverridePos = 0.5f;
headVLookOverridePos = 0.5f;
headHLookOverridePos = 0.5f;
ignore_updates = false;
fx_c_triggers = 0;
mark_fx_c_triggers = 0;
fx_s_triggers = 0;
move_trigger_states = 0;
z_velocity = 0.0f;
mark_idle = false;
idle_timer = 0.0f;
mark_s_landing = false;
speed_bias = 1.0f;
speed_bias_goal = 1.0f;
override_movement = 0;
movement_data.zero();
movement_op = 1;
last_movement_tag = 0;
footfallDecalOverride = 0;
footfallSoundOverride = 0;
footfallDustOverride = 0;
noFootfallFX = false;
}
U32 Player::afx_packUpdate(NetConnection* con, U32 mask, BitStream* stream, U32 retMask)
{
#if 0
if (stream->writeFlag(mask & LookOverrideMask))
#else
if (stream->writeFlag(mask & ActionMask))
#endif
stream->writeFlag(overrideLookAnimation);
if (stream->writeFlag(mask & TriggerMask))
stream->write(fx_s_triggers);
return retMask;
}
void Player::afx_unpackUpdate(NetConnection* con, BitStream* stream)
{
if (stream->readFlag()) // LookOverrideMask
overrideLookAnimation = stream->readFlag();
if (stream->readFlag()) // TriggerMask
{
U32 mask;
stream->read(&mask);
mark_fx_c_triggers = mask;
}
}
// Code for overriding player's animation with sequences selected by the
// anim-clip component effect.
void Player::restoreAnimation(U32 tag)
{
// check if this is a blended clip
if ((tag & BLENDED_CLIP) != 0)
{
restoreBlendAnimation(tag);
return;
}
if (tag != 0 && tag == last_anim_tag)
{
bool is_death_anim = ((anim_clip_flags & IS_DEATH_ANIM) != 0);
anim_clip_flags &= ~(ANIM_OVERRIDDEN | IS_DEATH_ANIM);
if (isClientObject())
{
if (mDamageState != Enabled)
{
if (!is_death_anim)
{
// this is a bit hardwired and desperate,
// but if he's dead he needs to look like it.
setActionThread("death10", false, false, false);
}
}
else if (mState != MoveState)
{
// not sure what happens here
}
else
{
pickActionAnimation();
}
}
last_anim_tag = 0;
last_anim_id = -1;
}
}
U32 Player::getAnimationID(const char* name)
{
for (U32 i = 0; i < mDataBlock->actionCount; i++)
{
PlayerData::ActionAnimation &anim = mDataBlock->actionList[i];
if (dStricmp(anim.name, name) == 0)
return i;
}
Con::errorf("Player::getAnimationID() -- Player does not contain a sequence that matches the name, %s.", name);
return BAD_ANIM_ID;
}
U32 Player::playAnimationByID(U32 anim_id, F32 pos, F32 rate, F32 trans, bool hold, bool wait, bool is_death_anim)
{
if (anim_id == BAD_ANIM_ID)
return 0;
S32 seq_id = mDataBlock->actionList[anim_id].sequence;
if (seq_id == -1)
{
Con::errorf("Player::playAnimation() problem. BAD_SEQ_ID");
return 0;
}
if (mShapeInstance->getShape()->sequences[seq_id].isBlend())
return playBlendAnimation(seq_id, pos, rate);
if (isClientObject())
{
PlayerData::ActionAnimation &anim = mDataBlock->actionList[anim_id];
if (anim.sequence != -1)
{
mActionAnimation.action = anim_id;
mActionAnimation.forward = (rate >= 0);
mActionAnimation.firstPerson = false;
mActionAnimation.holdAtEnd = hold;
mActionAnimation.waitForEnd = hold? true: wait;
mActionAnimation.animateOnServer = false;
mActionAnimation.atEnd = false;
mActionAnimation.delayTicks = (S32)sNewAnimationTickTime;
F32 transTime = (trans < 0) ? sAnimationTransitionTime : trans;
mShapeInstance->setTimeScale(mActionAnimation.thread, rate);
mShapeInstance->transitionToSequence(mActionAnimation.thread,anim.sequence,
pos, transTime, true);
}
}
if (is_death_anim)
anim_clip_flags |= IS_DEATH_ANIM;
else
anim_clip_flags &= ~IS_DEATH_ANIM;
anim_clip_flags |= ANIM_OVERRIDDEN;
last_anim_tag = unique_anim_tag_counter++;
last_anim_id = anim_id;
return last_anim_tag;
}
F32 Player::getAnimationDurationByID(U32 anim_id)
{
if (anim_id == BAD_ANIM_ID)
return 0.0f;
S32 seq_id = mDataBlock->actionList[anim_id].sequence;
if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size())
return mDataBlock->mShape->sequences[seq_id].duration;
return 0.0f;
}
bool Player::isBlendAnimation(const char* name)
{
U32 anim_id = getAnimationID(name);
if (anim_id == BAD_ANIM_ID)
return false;
S32 seq_id = mDataBlock->actionList[anim_id].sequence;
if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size())
return mDataBlock->mShape->sequences[seq_id].isBlend();
return false;
}
const char* Player::getLastClipName(U32 clip_tag)
{
if (clip_tag != last_anim_tag || last_anim_id >= PlayerData::NumActionAnims)
return "";
return mDataBlock->actionList[last_anim_id].name;
}
void Player::unlockAnimation(U32 tag, bool force)
{
if ((tag != 0 && tag == last_anim_lock_tag) || force)
anim_clip_flags &= ~BLOCK_USER_CONTROL;
}
U32 Player::lockAnimation()
{
anim_clip_flags |= BLOCK_USER_CONTROL;
last_anim_lock_tag = unique_anim_tag_counter++;
return last_anim_lock_tag;
}
ConsoleMethod(Player, isAnimationLocked, bool, 2, 2, "isAnimationLocked()")
{
return object->isAnimationLocked();
}
void Player::setLookAnimationOverride(bool flag)
{
overrideLookAnimation = flag;
#if 0
setMaskBits(LookOverrideMask);
#else
setMaskBits(ActionMask);
#endif
}
ConsoleMethod(Player, setLookAnimationOverride, void, 3, 3, "setLookAnimationOverride(flag)")
{
object->setLookAnimationOverride(dAtob(argv[2]));
}
ConsoleMethod(Player, copyHeadRotation, void, 3, 3, "copyHeadRotation(other_player)")
{
Player* other_player = dynamic_cast<Player*>(Sim::findObject(argv[2]));
if (other_player)
object->copyHeadRotation(other_player);
}
void Player::process_client_triggers(bool triggeredLeft, bool triggeredRight)
{
bool mark_landing = false;
Point3F my_vel = getVelocity();
if (my_vel.z > 5.0f)
z_velocity = 1;
else if (my_vel.z < -5.0f)
z_velocity = -1;
else
{
if (z_velocity < 0)
mark_landing = true;
z_velocity = 0.0f;
}
fx_c_triggers = mark_fx_c_triggers;
if (triggeredLeft)
fx_c_triggers |= PLAYER_LF_FOOT_C_TRIGGER;
if (triggeredRight)
fx_c_triggers |= PLAYER_RT_FOOT_C_TRIGGER;
if (mark_landing)
fx_c_triggers |= PLAYER_LANDING_C_TRIGGER;
if (idle_timer > 10.0f)
{
fx_c_triggers |= PLAYER_IDLE_C_TRIGGER;
idle_timer = 0.0f;
}
if (fx_c_triggers & PLAYER_LANDING_S_TRIGGER)
{
fx_c_triggers &= ~(PLAYER_LANDING_S_TRIGGER);
}
}
U32 Player::unique_movement_tag_counter = 1;
void Player::setMovementSpeedBias(F32 bias)
{
speed_bias_goal = bias;
}
U32 Player::setMovementOverride(F32 bias, const Point3F* mov, U32 op)
{
if (mov)
{
movement_data = *mov;
override_movement = true;
movement_op = (U8)op;
}
else
override_movement = false;
speed_bias_goal = bias;
last_movement_tag = unique_movement_tag_counter++;
return last_movement_tag;
}
void Player::restoreMovement(U32 tag)
{
if (tag != 0 && tag == last_movement_tag)
{
speed_bias_goal = 1.0;
override_movement = false;
}
}
ConsoleMethod(Player, setMovementSpeedBias, void, 3, 3, "setMovementSpeedBias(F32 bias)")
{
object->setMovementSpeedBias(dAtof(argv[2]));
}
void Player::overrideFootfallFX(bool decals, bool sounds, bool dust)
{
if (decals)
footfallDecalOverride++;
if (sounds)
footfallSoundOverride++;
if (dust)
footfallDustOverride++;
noFootfallFX = (footfallDecalOverride > 0 && footfallSoundOverride > 0 && footfallDustOverride > 0);
}
void Player::restoreFootfallFX(bool decals, bool sounds, bool dust)
{
if (decals && footfallDecalOverride)
footfallDecalOverride--;
if (sounds && footfallSoundOverride)
footfallSoundOverride--;
if (dust && footfallDustOverride)
footfallDustOverride--;
noFootfallFX = (footfallDecalOverride > 0 && footfallSoundOverride > 0 && footfallDustOverride > 0);
}
#ifdef TORQUE_OPENVR
void Player::setControllers(Vector<OpenVRTrackedObject*> controllerList)
{

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#ifndef _PLAYER_H_
#define _PLAYER_H_
@ -401,7 +406,8 @@ protected:
ActionMask = Parent::NextFreeMask << 0,
MoveMask = Parent::NextFreeMask << 1,
ImpactMask = Parent::NextFreeMask << 2,
NextFreeMask = Parent::NextFreeMask << 3
TriggerMask = Parent::NextFreeMask << 3,
NextFreeMask = Parent::NextFreeMask << 4
};
SimObjectPtr<ParticleEmitter> mSplashEmitter[PlayerData::NUM_SPLASH_EMITTERS];
@ -780,6 +786,89 @@ public:
virtual void prepRenderImage( SceneRenderState* state );
virtual void renderConvex( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat );
virtual void renderMountedImage( U32 imageSlot, TSRenderState &rstate, SceneRenderState *state );
private:
static void afx_consoleInit();
void afx_init();
U32 afx_packUpdate(NetConnection*, U32 mask, BitStream*, U32 retMask);
void afx_unpackUpdate(NetConnection*, BitStream*);
private:
static bool sCorpsesHiddenFromRayCast;
public:
virtual void restoreAnimation(U32 tag);
virtual U32 getAnimationID(const char* name);
virtual U32 playAnimationByID(U32 anim_id, F32 pos, F32 rate, F32 trans, bool hold, bool wait, bool is_death_anim);
virtual F32 getAnimationDurationByID(U32 anim_id);
virtual bool isBlendAnimation(const char* name);
virtual const char* getLastClipName(U32 clip_tag);
virtual void unlockAnimation(U32 tag, bool force=false);
virtual U32 lockAnimation();
virtual bool isAnimationLocked() const { return ((anim_clip_flags & BLOCK_USER_CONTROL) != 0); }
protected:
bool overrideLookAnimation;
F32 armLookOverridePos;
F32 headVLookOverridePos;
F32 headHLookOverridePos;
public:
void setLookAnimationOverride(bool flag);
void copyHeadRotation(const Player* p) { mHead = p->mHead; }
public:
bool ignore_updates;
void resetContactTimer() { mContactTimer = 0; }
private:
U8 move_trigger_states;
U32 fx_s_triggers;
U32 mark_fx_c_triggers;
U32 fx_c_triggers;
F32 z_velocity;
bool mark_idle;
F32 idle_timer;
bool mark_s_landing;
void process_client_triggers(bool triggeredLeft, bool triggeredRight);
public:
enum {
// server events
PLAYER_MOVE_TRIGGER_0 = BIT(0),
PLAYER_MOVE_TRIGGER_1 = BIT(1),
PLAYER_MOVE_TRIGGER_2 = BIT(2),
PLAYER_MOVE_TRIGGER_3 = BIT(3),
PLAYER_MOVE_TRIGGER_4 = BIT(4),
PLAYER_MOVE_TRIGGER_5 = BIT(5),
PLAYER_LANDING_S_TRIGGER = BIT(6),
PLAYER_FIRE_S_TRIGGER = PLAYER_MOVE_TRIGGER_0,
PLAYER_FIRE_ALT_S_TRIGGER = PLAYER_MOVE_TRIGGER_1,
PLAYER_JUMP_S_TRIGGER = BIT(7),
// client events
PLAYER_LF_FOOT_C_TRIGGER = BIT(16),
PLAYER_RT_FOOT_C_TRIGGER = BIT(17),
PLAYER_LANDING_C_TRIGGER = BIT(18),
PLAYER_IDLE_C_TRIGGER = BIT(19),
};
U32 getClientEventTriggers() const { return fx_c_triggers; }
U32 getServerEventTriggers() const { return fx_s_triggers; }
private:
F32 speed_bias;
F32 speed_bias_goal;
bool override_movement;
Point3F movement_data;
U8 movement_op;
U32 last_movement_tag;
static U32 unique_movement_tag_counter;
public:
void setMovementSpeedBias(F32 bias);
U32 setMovementOverride(F32 bias, const Point3F* mov=0, U32 op=1);
void restoreMovement(U32 tag);
private:
S32 footfallDecalOverride;
S32 footfallSoundOverride;
S32 footfallDustOverride;
bool noFootfallFX;
public:
void overrideFootfallFX(bool decals=true, bool sounds=true, bool dust=true);
void restoreFootfallFX(bool decals=true, bool sounds=true, bool dust=true);
};
typedef Player::Pose PlayerPose;

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "platform/platform.h"
#include "T3D/projectile.h"
@ -190,6 +195,40 @@ ProjectileData::ProjectileData()
lightDescId = 0;
}
ProjectileData::ProjectileData(const ProjectileData& other, bool temp_clone) : GameBaseData(other, temp_clone)
{
projectileShapeName = other.projectileShapeName;
faceViewer = other.faceViewer; // -- always set to false
scale = other.scale;
velInheritFactor = other.velInheritFactor;
muzzleVelocity = other.muzzleVelocity;
impactForce = other.impactForce;
isBallistic = other.isBallistic;
bounceElasticity = other.bounceElasticity;
bounceFriction = other.bounceFriction;
gravityMod = other.gravityMod;
lifetime = other.lifetime;
armingDelay = other.armingDelay;
fadeDelay = other.fadeDelay;
explosion = other.explosion;
explosionId = other.explosionId; // -- for pack/unpack of explosion ptr
waterExplosion = other.waterExplosion;
waterExplosionId = other.waterExplosionId; // -- for pack/unpack of waterExplosion ptr
splash = other.splash;
splashId = other.splashId; // -- for pack/unpack of splash ptr
decal = other.decal;
decalId = other.decalId; // -- for pack/unpack of decal ptr
sound = other.sound;
lightDesc = other.lightDesc;
lightDescId = other.lightDescId; // -- for pack/unpack of lightDesc ptr
projectileShape = other.projectileShape; // -- TSShape loads using projectileShapeName
activateSeq = other.activateSeq; // -- from projectileShape sequence "activate"
maintainSeq = other.maintainSeq; // -- from projectileShape sequence "maintain"
particleEmitter = other.particleEmitter;
particleEmitterId = other.particleEmitterId; // -- for pack/unpack of particleEmitter ptr
particleWaterEmitter = other.particleWaterEmitter;
particleWaterEmitterId = other.particleWaterEmitterId; // -- for pack/unpack of particleWaterEmitter ptr
}
//--------------------------------------------------------------------------
void ProjectileData::initPersistFields()
@ -274,6 +313,13 @@ void ProjectileData::initPersistFields()
"A value of 1.0 will assume \"normal\" influence upon it.\n"
"The magnitude of gravity is assumed to be 9.81 m/s/s\n\n"
"@note ProjectileData::isBallistic must be true for this to have any affect.");
// disallow some field substitutions
onlyKeepClearSubstitutions("explosion");
onlyKeepClearSubstitutions("particleEmitter");
onlyKeepClearSubstitutions("particleWaterEmitter");
onlyKeepClearSubstitutions("sound");
onlyKeepClearSubstitutions("splash");
onlyKeepClearSubstitutions("waterExplosion");
Parent::initPersistFields();
}
@ -574,6 +620,11 @@ Projectile::Projectile()
mLightState.clear();
mLightState.setLightInfo( mLight );
mDataBlock = 0;
ignoreSourceTimeout = false;
dynamicCollisionMask = csmDynamicCollisionMask;
staticCollisionMask = csmStaticCollisionMask;
}
Projectile::~Projectile()
@ -582,6 +633,11 @@ Projectile::~Projectile()
delete mProjectileShape;
mProjectileShape = NULL;
if (mDataBlock && mDataBlock->isTempClone())
{
delete mDataBlock;
mDataBlock = 0;
}
}
//--------------------------------------------------------------------------
@ -609,6 +665,7 @@ void Projectile::initPersistFields()
addField("sourceSlot", TypeS32, Offset(mSourceObjectSlot, Projectile),
"@brief The sourceObject's weapon slot that the projectile originates from.\n\n");
addField("ignoreSourceTimeout", TypeBool, Offset(ignoreSourceTimeout, Projectile));
endGroup("Source");
@ -1088,7 +1145,7 @@ void Projectile::simulate( F32 dt )
// disable the source objects collision reponse for a short time while we
// determine if the projectile is capable of moving from the old position
// to the new position, otherwise we'll hit ourself
bool disableSourceObjCollision = (mSourceObject.isValid() && mCurrTick <= SourceIdTimeoutTicks);
bool disableSourceObjCollision = (mSourceObject.isValid() && (ignoreSourceTimeout || mCurrTick <= SourceIdTimeoutTicks));
if ( disableSourceObjCollision )
mSourceObject->disableCollision();
disableCollision();
@ -1105,12 +1162,12 @@ void Projectile::simulate( F32 dt )
if ( mPhysicsWorld )
hit = mPhysicsWorld->castRay( oldPosition, newPosition, &rInfo, Point3F( newPosition - oldPosition) * mDataBlock->impactForce );
else
hit = getContainer()->castRay(oldPosition, newPosition, csmDynamicCollisionMask | csmStaticCollisionMask, &rInfo);
hit = getContainer()->castRay(oldPosition, newPosition, dynamicCollisionMask | staticCollisionMask, &rInfo);
if ( hit )
{
// make sure the client knows to bounce
if ( isServerObject() && ( rInfo.object->getTypeMask() & csmStaticCollisionMask ) == 0 )
if(isServerObject() && (rInfo.object->getTypeMask() & staticCollisionMask) == 0)
setMaskBits( BounceMask );
MatrixF xform( true );
@ -1301,6 +1358,7 @@ U32 Projectile::packUpdate( NetConnection *con, U32 mask, BitStream *stream )
stream->writeRangedU32( U32(mSourceObjectSlot),
0,
ShapeBase::MaxMountedImages - 1 );
stream->writeFlag(ignoreSourceTimeout);
}
else
// have not recieved the ghost for the source object yet, try again later
@ -1344,6 +1402,7 @@ void Projectile::unpackUpdate(NetConnection* con, BitStream* stream)
mSourceObjectId = stream->readRangedU32( 0, NetConnection::MaxGhostCount );
mSourceObjectSlot = stream->readRangedU32( 0, ShapeBase::MaxMountedImages - 1 );
ignoreSourceTimeout = stream->readFlag();
NetObject* pObject = con->resolveGhost( mSourceObjectId );
if ( pObject != NULL )
mSourceObject = dynamic_cast<ShapeBase*>( pObject );

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#ifndef _PROJECTILE_H_
#define _PROJECTILE_H_
@ -144,6 +149,9 @@ public:
DECLARE_CALLBACK( void, onExplode, ( Projectile* proj, Point3F pos, F32 fade ) );
DECLARE_CALLBACK( void, onCollision, ( Projectile* proj, SceneObject* col, F32 fade, Point3F pos, Point3F normal ) );
public:
ProjectileData(const ProjectileData&, bool = false);
virtual bool allowSubstitutions() const { return true; }
};
@ -279,6 +287,10 @@ protected:
Point3F mExplosionPosition;
Point3F mExplosionNormal;
U32 mCollideHitType;
public:
bool ignoreSourceTimeout;
U32 dynamicCollisionMask;
U32 staticCollisionMask;
};
#endif // _PROJECTILE_H_

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "platform/platform.h"
#include "T3D/shapeBase.h"
@ -194,6 +199,69 @@ ShapeBaseData::ShapeBaseData()
inheritEnergyFromMount( false )
{
dMemset( mountPointNode, -1, sizeof( S32 ) * SceneObject::NumMountPoints );
remap_txr_tags = NULL;
remap_buffer = NULL;
silent_bbox_check = false;
}
ShapeBaseData::ShapeBaseData(const ShapeBaseData& other, bool temp_clone) : GameBaseData(other, temp_clone)
{
shadowEnable = other.shadowEnable;
shadowSize = other.shadowSize;
shadowMaxVisibleDistance = other.shadowMaxVisibleDistance;
shadowProjectionDistance = other.shadowProjectionDistance;
shadowSphereAdjust = other.shadowSphereAdjust;
shapeName = other.shapeName;
cloakTexName = other.cloakTexName;
cubeDescName = other.cubeDescName;
cubeDescId = other.cubeDescId;
reflectorDesc = other.reflectorDesc;
debris = other.debris;
debrisID = other.debrisID; // -- for pack/unpack of debris ptr
debrisShapeName = other.debrisShapeName;
debrisShape = other.debrisShape; // -- TSShape loaded using debrisShapeName
explosion = other.explosion;
explosionID = other.explosionID; // -- for pack/unpack of explosion ptr
underwaterExplosion = other.underwaterExplosion;
underwaterExplosionID = other.underwaterExplosionID; // -- for pack/unpack of underwaterExplosion ptr
mass = other.mass;
drag = other.drag;
density = other.density;
maxEnergy = other.maxEnergy;
maxDamage = other.maxDamage;
repairRate = other.repairRate;
disabledLevel = other.disabledLevel;
destroyedLevel = other.destroyedLevel;
cameraMaxDist = other.cameraMaxDist;
cameraMinDist = other.cameraMinDist;
cameraDefaultFov = other.cameraDefaultFov;
cameraMinFov = other.cameraMinFov;
cameraMaxFov = other.cameraMaxFov;
cameraCanBank = other.cameraCanBank;
mountedImagesBank = other.mountedImagesBank;
mShape = other.mShape; // -- TSShape loaded using shapeName
mCRC = other.mCRC; // -- from shape, used to verify client shape
computeCRC = other.computeCRC;
eyeNode = other.eyeNode; // -- from shape node "eye"
earNode = other.earNode; // -- from shape node "ear"
cameraNode = other.cameraNode; // -- from shape node "cam"
dMemcpy(mountPointNode, other.mountPointNode, sizeof(mountPointNode)); // -- from shape nodes "mount#" 0-31
debrisDetail = other.debrisDetail; // -- from shape detail "Debris-17"
damageSequence = other.damageSequence; // -- from shape sequence "Damage"
hulkSequence = other.hulkSequence; // -- from shape sequence "Visibility"
observeThroughObject = other.observeThroughObject;
collisionDetails = other.collisionDetails; // -- calc from shape (this is a Vector copy)
collisionBounds = other.collisionBounds; // -- calc from shape (this is a Vector copy)
LOSDetails = other.LOSDetails; // -- calc from shape (this is a Vector copy)
firstPersonOnly = other.firstPersonOnly;
useEyePoint = other.useEyePoint;
isInvincible = other.isInvincible;
renderWhenDestroyed = other.renderWhenDestroyed;
inheritEnergyFromMount = other.inheritEnergyFromMount;
remap_txr_tags = other.remap_txr_tags;
remap_buffer = other.remap_buffer;
txr_tag_remappings = other.txr_tag_remappings;
silent_bbox_check = other.silent_bbox_check;
}
struct ShapeBaseDataProto
@ -228,6 +296,8 @@ static ShapeBaseDataProto gShapeBaseDataProto;
ShapeBaseData::~ShapeBaseData()
{
if (remap_buffer && !isTempClone())
dFree(remap_buffer);
}
bool ShapeBaseData::preload(bool server, String &errorStr)
@ -337,11 +407,13 @@ bool ShapeBaseData::preload(bool server, String &errorStr)
if (!mShape->bounds.isContained(collisionBounds.last()))
{
if (!silent_bbox_check)
Con::warnf("Warning: shape %s collision detail %d (Collision-%d) bounds exceed that of shape.", shapeName, collisionDetails.size() - 1, collisionDetails.last());
collisionBounds.last() = mShape->bounds;
}
else if (collisionBounds.last().isValidBox() == false)
{
if (!silent_bbox_check)
Con::errorf("Error: shape %s-collision detail %d (Collision-%d) bounds box invalid!", shapeName, collisionDetails.size() - 1, collisionDetails.last());
collisionBounds.last() = mShape->bounds;
}
@ -413,6 +485,29 @@ bool ShapeBaseData::preload(bool server, String &errorStr)
F32 w = mShape->bounds.len_y() / 2;
if (cameraMaxDist < w)
cameraMaxDist = w;
// just parse up the string and collect the remappings in txr_tag_remappings.
if (!server && remap_txr_tags != NULL && remap_txr_tags != StringTable->insert(""))
{
txr_tag_remappings.clear();
if (remap_buffer)
dFree(remap_buffer);
remap_buffer = dStrdup(remap_txr_tags);
char* remap_token = dStrtok(remap_buffer, " \t");
while (remap_token != NULL)
{
char* colon = dStrchr(remap_token, ':');
if (colon)
{
*colon = '\0';
txr_tag_remappings.increment();
txr_tag_remappings.last().old_tag = remap_token;
txr_tag_remappings.last().new_tag = colon+1;
}
remap_token = dStrtok(NULL, " \t");
}
}
}
if(!server)
@ -586,6 +681,12 @@ void ShapeBaseData::initPersistFields()
endGroup( "Reflection" );
addField("remapTextureTags", TypeString, Offset(remap_txr_tags, ShapeBaseData));
addField("silentBBoxValidation", TypeBool, Offset(silent_bbox_check, ShapeBaseData));
// disallow some field substitutions
onlyKeepClearSubstitutions("debris"); // subs resolving to "~~", or "~0" are OK
onlyKeepClearSubstitutions("explosion");
onlyKeepClearSubstitutions("underwaterExplosion");
Parent::initPersistFields();
}
@ -738,6 +839,8 @@ void ShapeBaseData::packData(BitStream* stream)
//stream->write(reflectMinDist);
//stream->write(reflectMaxDist);
//stream->write(reflectDetailAdjust);
stream->writeString(remap_txr_tags);
stream->writeFlag(silent_bbox_check);
}
void ShapeBaseData::unpackData(BitStream* stream)
@ -839,6 +942,8 @@ void ShapeBaseData::unpackData(BitStream* stream)
//stream->read(&reflectMinDist);
//stream->read(&reflectMaxDist);
//stream->read(&reflectDetailAdjust);
remap_txr_tags = stream->readSTString();
silent_bbox_check = stream->readFlag();
}
@ -941,6 +1046,13 @@ ShapeBase::ShapeBase()
for (i = 0; i < MaxTriggerKeys; i++)
mTrigger[i] = false;
anim_clip_flags = 0;
last_anim_id = -1;
last_anim_tag = 0;
last_anim_lock_tag = 0;
saved_seq_id = -1;
saved_pos = 0.0f;
saved_rate = 1.0f;
}
@ -1079,6 +1191,16 @@ void ShapeBase::onSceneRemove()
bool ShapeBase::onNewDataBlock( GameBaseData *dptr, bool reload )
{
// need to destroy blend-clips or we crash
if (isGhost())
{
for (S32 i = 0; i < blend_clips.size(); i++)
{
if (blend_clips[i].thread)
mShapeInstance->destroyThread(blend_clips[i].thread);
blend_clips.erase_fast(i);
}
}
ShapeBaseData *prevDB = dynamic_cast<ShapeBaseData*>( mDataBlock );
bool isInitialDataBlock = ( mDataBlock == 0 );
@ -1098,10 +1220,61 @@ bool ShapeBase::onNewDataBlock( GameBaseData *dptr, bool reload )
// a shape assigned to this object.
if (bool(mDataBlock->mShape)) {
delete mShapeInstance;
if (isClientObject() && mDataBlock->txr_tag_remappings.size() > 0)
{
// temporarily substitute material tags with alternates
TSMaterialList* mat_list = mDataBlock->mShape->materialList;
if (mat_list)
{
for (S32 i = 0; i < mDataBlock->txr_tag_remappings.size(); i++)
{
ShapeBaseData::TextureTagRemapping* remap = &mDataBlock->txr_tag_remappings[i];
Vector<String> & mat_names = (Vector<String>&) mat_list->getMaterialNameList();
for (S32 j = 0; j < mat_names.size(); j++)
{
if (mat_names[j].compare(remap->old_tag, dStrlen(remap->old_tag), String::NoCase) == 0)
{
mat_names[j] = String(remap->new_tag);
mat_names[j].insert(0,'#');
break;
}
}
}
}
}
mShapeInstance = new TSShapeInstance(mDataBlock->mShape, isClientObject());
if (isClientObject())
{
mShapeInstance->cloneMaterialList();
// restore the material tags to original form
if (mDataBlock->txr_tag_remappings.size() > 0)
{
TSMaterialList* mat_list = mDataBlock->mShape->materialList;
if (mat_list)
{
for (S32 i = 0; i < mDataBlock->txr_tag_remappings.size(); i++)
{
ShapeBaseData::TextureTagRemapping* remap = &mDataBlock->txr_tag_remappings[i];
Vector<String> & mat_names = (Vector<String>&) mat_list->getMaterialNameList();
for (S32 j = 0; j < mat_names.size(); j++)
{
String::SizeType len = mat_names[j].length();
if (len > 1)
{
String temp_name = mat_names[j].substr(1,len-1);
if (temp_name.compare(remap->new_tag, dStrlen(remap->new_tag)) == 0)
{
mat_names[j] = String(remap->old_tag);
break;
}
}
}
}
}
}
}
mObjBox = mDataBlock->mShape->bounds;
resetWorldBox();
@ -3550,6 +3723,31 @@ void ShapeBase::setCurrentWaterObject( WaterObject *obj )
mCurrentWaterObject = obj;
}
void ShapeBase::notifyCollisionCallbacks(SceneObject* obj, const VectorF& vel)
{
for (S32 i = 0; i < collision_callbacks.size(); i++)
if (collision_callbacks[i])
collision_callbacks[i]->collisionNotify(this, obj, vel);
}
void ShapeBase::registerCollisionCallback(CollisionEventCallback* ce_cb)
{
for (S32 i = 0; i < collision_callbacks.size(); i++)
if (collision_callbacks[i] == ce_cb)
return;
collision_callbacks.push_back(ce_cb);
}
void ShapeBase::unregisterCollisionCallback(CollisionEventCallback* ce_cb)
{
for (S32 i = 0; i < collision_callbacks.size(); i++)
if (collision_callbacks[i] == ce_cb)
{
collision_callbacks.erase(i);
return;
}
}
//--------------------------------------------------------------------------
//----------------------------------------------------------------------------
DefineEngineMethod( ShapeBase, setHidden, void, ( bool show ),,
@ -4945,3 +5143,202 @@ DefineEngineMethod( ShapeBase, getModelFile, const char *, (),,
const char *fieldName = StringTable->insert( String("shapeFile") );
return datablock->getDataField( fieldName, NULL );
}
U32 ShapeBase::unique_anim_tag_counter = 1;
U32 ShapeBase::playBlendAnimation(S32 seq_id, F32 pos, F32 rate)
{
BlendThread blend_clip;
blend_clip.tag = ((unique_anim_tag_counter++) | BLENDED_CLIP);
blend_clip.thread = 0;
if (isClientObject())
{
blend_clip.thread = mShapeInstance->addThread();
mShapeInstance->setSequence(blend_clip.thread, seq_id, pos);
mShapeInstance->setTimeScale(blend_clip.thread, rate);
}
blend_clips.push_back(blend_clip);
return blend_clip.tag;
}
void ShapeBase::restoreBlendAnimation(U32 tag)
{
for (S32 i = 0; i < blend_clips.size(); i++)
{
if (blend_clips[i].tag == tag)
{
if (blend_clips[i].thread)
{
mShapeInstance->destroyThread(blend_clips[i].thread);
}
blend_clips.erase_fast(i);
break;
}
}
}
//
void ShapeBase::restoreAnimation(U32 tag)
{
if (!isClientObject())
return;
// check if this is a blended clip
if ((tag & BLENDED_CLIP) != 0)
{
restoreBlendAnimation(tag);
return;
}
if (tag != 0 && tag == last_anim_tag)
{
anim_clip_flags &= ~(ANIM_OVERRIDDEN | IS_DEATH_ANIM);
stopThread(0);
if (saved_seq_id != -1)
{
setThreadSequence(0, saved_seq_id);
setThreadPosition(0, saved_pos);
setThreadTimeScale(0, saved_rate);
setThreadDir(0, (saved_rate >= 0));
playThread(0);
saved_seq_id = -1;
saved_pos = 0.0f;
saved_rate = 1.0f;
}
last_anim_tag = 0;
last_anim_id = -1;
}
}
U32 ShapeBase::getAnimationID(const char* name)
{
const TSShape* ts_shape = getShape();
S32 seq_id = (ts_shape) ? ts_shape->findSequence(name) : -1;
return (seq_id >= 0) ? (U32) seq_id : BAD_ANIM_ID;
}
U32 ShapeBase::playAnimationByID(U32 anim_id, F32 pos, F32 rate, F32 trans, bool hold, bool wait, bool is_death_anim)
{
if (!isClientObject())
return 0;
if (anim_id == BAD_ANIM_ID)
return 0;
const TSShape* ts_shape = getShape();
if (!ts_shape)
return 0;
S32 seq_id = (S32) anim_id;
if (mShapeInstance->getShape()->sequences[seq_id].isBlend())
return playBlendAnimation(seq_id, pos, rate);
if (last_anim_tag == 0)
{
// try to save state of playing animation
Thread& st = mScriptThread[0];
if (st.sequence != -1)
{
saved_seq_id = st.sequence;
saved_pos = st.position;
saved_rate = st.timescale;
}
}
// START OR TRANSITION TO SEQUENCE HERE
setThreadSequence(0, seq_id);
setThreadPosition(0, pos);
setThreadTimeScale(0, rate);
setThreadDir(0, (rate >= 0));
playThread(0);
if (is_death_anim)
anim_clip_flags |= IS_DEATH_ANIM;
else
anim_clip_flags &= ~IS_DEATH_ANIM;
anim_clip_flags |= ANIM_OVERRIDDEN;
last_anim_tag = unique_anim_tag_counter++;
last_anim_id = anim_id;
return last_anim_tag;
}
F32 ShapeBase::getAnimationDurationByID(U32 anim_id)
{
if (anim_id == BAD_ANIM_ID)
return 0.0f;
S32 seq_id = (S32) anim_id;
if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size())
return mDataBlock->mShape->sequences[seq_id].duration;
return 0.0f;
}
bool ShapeBase::isBlendAnimation(const char* name)
{
U32 anim_id = getAnimationID(name);
if (anim_id == BAD_ANIM_ID)
return false;
S32 seq_id = (S32) anim_id;
if (seq_id >= 0 && seq_id < mDataBlock->mShape->sequences.size())
return mDataBlock->mShape->sequences[seq_id].isBlend();
return false;
}
const char* ShapeBase::getLastClipName(U32 clip_tag)
{
if (clip_tag != last_anim_tag)
return "";
S32 seq_id = (S32) last_anim_id;
S32 idx = mDataBlock->mShape->sequences[seq_id].nameIndex;
if (idx < 0 || idx >= mDataBlock->mShape->names.size())
return 0;
return mDataBlock->mShape->names[idx];
}
//
U32 ShapeBase::playAnimation(const char* name, F32 pos, F32 rate, F32 trans, bool hold, bool wait, bool is_death_anim)
{
return playAnimationByID(getAnimationID(name), pos, rate, trans, hold, wait, is_death_anim);
}
F32 ShapeBase::getAnimationDuration(const char* name)
{
return getAnimationDurationByID(getAnimationID(name));
}
void ShapeBase::setSelectionFlags(U8 flags)
{
Parent::setSelectionFlags(flags);
if (!mShapeInstance || !isClientObject())
return;
if (!mShapeInstance->ownMaterialList())
return;
TSMaterialList* pMatList = mShapeInstance->getMaterialList();
for (S32 j = 0; j < pMatList->size(); j++)
{
BaseMatInstance * bmi = pMatList->getMaterialInst(j);
bmi->setSelectionHighlighting(needsSelectionHighlighting());
}
}

View file

@ -20,6 +20,10 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#ifndef _SHAPEBASE_H_
#define _SHAPEBASE_H_
@ -654,6 +658,17 @@ public:
DECLARE_CALLBACK(void, onEndSequence, (ShapeBase* obj, S32 slot, const char* name));
DECLARE_CALLBACK( void, onForceUncloak, ( ShapeBase* obj, const char* reason ) );
/// @}
struct TextureTagRemapping
{
char* old_tag;
char* new_tag;
};
StringTableEntry remap_txr_tags;
char* remap_buffer;
Vector<TextureTagRemapping> txr_tag_remappings;
bool silent_bbox_check;
public:
ShapeBaseData(const ShapeBaseData&, bool = false);
};
@ -1845,7 +1860,58 @@ public:
protected:
DECLARE_CALLBACK( F32, validateCameraFov, (F32 fov) );
public:
class CollisionEventCallback
{
public:
virtual void collisionNotify(SceneObject* shape0, SceneObject* shape1, const VectorF& vel)=0;
};
private:
Vector<CollisionEventCallback*> collision_callbacks;
void notifyCollisionCallbacks(SceneObject*, const VectorF& vel);
public:
void registerCollisionCallback(CollisionEventCallback*);
void unregisterCollisionCallback(CollisionEventCallback*);
protected:
enum {
ANIM_OVERRIDDEN = BIT(0),
BLOCK_USER_CONTROL = BIT(1),
IS_DEATH_ANIM = BIT(2),
BAD_ANIM_ID = 999999999,
BLENDED_CLIP = 0x80000000,
};
struct BlendThread
{
TSThread* thread;
U32 tag;
};
Vector<BlendThread> blend_clips;
static U32 unique_anim_tag_counter;
U8 anim_clip_flags;
S32 last_anim_id;
U32 last_anim_tag;
U32 last_anim_lock_tag;
S32 saved_seq_id;
F32 saved_pos;
F32 saved_rate;
U32 playBlendAnimation(S32 seq_id, F32 pos, F32 rate);
void restoreBlendAnimation(U32 tag);
public:
U32 playAnimation(const char* name, F32 pos, F32 rate, F32 trans, bool hold, bool wait, bool is_death_anim);
F32 getAnimationDuration(const char* name);
virtual void restoreAnimation(U32 tag);
virtual U32 getAnimationID(const char* name);
virtual U32 playAnimationByID(U32 anim_id, F32 pos, F32 rate, F32 trans, bool hold, bool wait, bool is_death_anim);
virtual F32 getAnimationDurationByID(U32 anim_id);
virtual bool isBlendAnimation(const char* name);
virtual const char* getLastClipName(U32 clip_tag);
virtual void unlockAnimation(U32 tag, bool force=false) { }
virtual U32 lockAnimation() { return 0; }
virtual bool isAnimationLocked() const { return false; }
virtual void setSelectionFlags(U8 flags);
};

View file

@ -20,6 +20,10 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "platform/platform.h"
#include "core/dnet.h"
#include "core/stream/bitStream.h"
@ -97,6 +101,14 @@ StaticShapeData::StaticShapeData()
noIndividualDamage = false;
}
StaticShapeData::StaticShapeData(const StaticShapeData& other, bool temp_clone) : ShapeBaseData(other, temp_clone)
{
noIndividualDamage = other.noIndividualDamage;
dynamicTypeField = other.dynamicTypeField;
isShielded = other.isShielded; // -- uninitialized, unused
energyPerDamagePoint = other.energyPerDamagePoint; // -- uninitialized, unused
}
void StaticShapeData::initPersistFields()
{
addField("noIndividualDamage", TypeBool, Offset(noIndividualDamage, StaticShapeData), "Deprecated\n\n @internal");

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#ifndef _STATICSHAPE_H_
#define _STATICSHAPE_H_
@ -38,12 +43,16 @@ struct StaticShapeData: public ShapeBaseData {
bool noIndividualDamage;
S32 dynamicTypeField;
bool isShielded;
F32 energyPerDamagePoint; // Re-added for AFX
//
DECLARE_CONOBJECT(StaticShapeData);
static void initPersistFields();
virtual void packData(BitStream* stream);
virtual void unpackData(BitStream* stream);
public:
StaticShapeData(const StaticShapeData&, bool = false);
virtual bool allowSubstitutions() const { return true; }
};

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#include "platform/platform.h"
#include "T3D/tsStatic.h"
@ -54,6 +59,8 @@ using namespace Torque;
extern bool gEditingMission;
#include "afx/ce/afxZodiacMgr.h"
IMPLEMENT_CO_NETOBJECT_V1(TSStatic);
ConsoleDocClass( TSStatic,
@ -124,6 +131,12 @@ TSStatic::TSStatic()
mCollisionType = CollisionMesh;
mDecalType = CollisionMesh;
mIgnoreZodiacs = false;
mHasGradients = false;
mInvertGradientRange = false;
mGradientRangeUser.set(0.0f, 180.0f);
afxZodiacData::convertGradientRangeFromDegrees(mGradientRange, mGradientRangeUser);
}
TSStatic::~TSStatic()
@ -222,6 +235,12 @@ void TSStatic::initPersistFields()
endGroup("Debug");
addGroup("AFX");
addField("ignoreZodiacs", TypeBool, Offset(mIgnoreZodiacs, TSStatic));
addField("useGradientRange", TypeBool, Offset(mHasGradients, TSStatic));
addField("gradientRange", TypePoint2F, Offset(mGradientRangeUser, TSStatic));
addField("invertGradientRange", TypeBool, Offset(mInvertGradientRange, TSStatic));
endGroup("AFX");
Parent::initPersistFields();
}
@ -323,6 +342,8 @@ bool TSStatic::_createShape()
{
// Cleanup before we create.
mCollisionDetails.clear();
mDecalDetails.clear();
mDecalDetailsPtr = 0;
mLOSDetails.clear();
SAFE_DELETE( mPhysicsRep );
SAFE_DELETE( mShapeInstance );
@ -354,6 +375,8 @@ bool TSStatic::_createShape()
mShapeInstance = new TSShapeInstance( mShape, isClientObject() );
if (isClientObject())
mShapeInstance->cloneMaterialList();
if( isGhost() )
{
// Reapply the current skin
@ -396,11 +419,29 @@ void TSStatic::prepCollision()
// Cleanup any old collision data
mCollisionDetails.clear();
mDecalDetails.clear();
mDecalDetailsPtr = 0;
mLOSDetails.clear();
mConvexList->nukeList();
if ( mCollisionType == CollisionMesh || mCollisionType == VisibleMesh )
{
mShape->findColDetails( mCollisionType == VisibleMesh, &mCollisionDetails, &mLOSDetails );
if ( mDecalType == mCollisionType )
{
mDecalDetailsPtr = &mCollisionDetails;
}
else if ( mDecalType == CollisionMesh || mDecalType == VisibleMesh )
{
mShape->findColDetails( mDecalType == VisibleMesh, &mDecalDetails, 0 );
mDecalDetailsPtr = &mDecalDetails;
}
}
else if ( mDecalType == CollisionMesh || mDecalType == VisibleMesh )
{
mShape->findColDetails( mDecalType == VisibleMesh, &mDecalDetails, 0 );
mDecalDetailsPtr = &mDecalDetails;
}
_updatePhysics();
}
@ -681,6 +722,8 @@ void TSStatic::prepRenderImage( SceneRenderState* state )
}
mShapeInstance->render( rdata );
if (!mIgnoreZodiacs && mDecalDetailsPtr != 0)
afxZodiacMgr::renderPolysoupZodiacs(state, this);
if ( mRenderNormalScalar > 0 )
{
ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
@ -786,6 +829,13 @@ U32 TSStatic::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
stream->write(mInvertAlphaFade);
}
stream->writeFlag(mIgnoreZodiacs);
if (stream->writeFlag(mHasGradients))
{
stream->writeFlag(mInvertGradientRange);
stream->write(mGradientRange.x);
stream->write(mGradientRange.y);
}
if ( mLightPlugin )
retMask |= mLightPlugin->packUpdate(this, AdvancedStaticOptionsMask, con, mask, stream);
@ -870,6 +920,14 @@ void TSStatic::unpackUpdate(NetConnection *con, BitStream *stream)
stream->read(&mInvertAlphaFade);
}
mIgnoreZodiacs = stream->readFlag();
mHasGradients = stream->readFlag();
if (mHasGradients)
{
mInvertGradientRange = stream->readFlag();
stream->read(&mGradientRange.x);
stream->read(&mGradientRange.y);
}
if ( mLightPlugin )
{
mLightPlugin->unpackUpdate(this, con, stream);
@ -882,6 +940,7 @@ void TSStatic::unpackUpdate(NetConnection *con, BitStream *stream)
if ( isProperlyAdded() )
_updateShouldTick();
set_special_typing();
}
//----------------------------------------------------------------------------
@ -992,6 +1051,11 @@ bool TSStatic::buildPolyList(PolyListContext context, AbstractPolyList* polyList
polyList->addBox( mObjBox );
else if ( meshType == VisibleMesh )
mShapeInstance->buildPolyList( polyList, 0 );
else if (context == PLC_Decal && mDecalDetailsPtr != 0)
{
for ( U32 i = 0; i < mDecalDetailsPtr->size(); i++ )
mShapeInstance->buildPolyListOpcode( (*mDecalDetailsPtr)[i], polyList, box );
}
else
{
// Everything else is done from the collision meshes
@ -1324,3 +1388,41 @@ DefineEngineMethod( TSStatic, getModelFile, const char *, (),,
{
return object->getShapeFileName();
}
void TSStatic::set_special_typing()
{
if (mCollisionType == VisibleMesh || mCollisionType == CollisionMesh)
mTypeMask |= InteriorLikeObjectType;
else
mTypeMask &= ~InteriorLikeObjectType;
}
void TSStatic::onStaticModified(const char* slotName, const char*newValue)
{
if (slotName == afxZodiacData::GradientRangeSlot)
{
afxZodiacData::convertGradientRangeFromDegrees(mGradientRange, mGradientRangeUser);
return;
}
set_special_typing();
}
void TSStatic::setSelectionFlags(U8 flags)
{
Parent::setSelectionFlags(flags);
if (!mShapeInstance || !isClientObject())
return;
if (!mShapeInstance->ownMaterialList())
return;
TSMaterialList* pMatList = mShapeInstance->getMaterialList();
for (S32 j = 0; j < pMatList->size(); j++)
{
BaseMatInstance * bmi = pMatList->getMaterialInst(j);
bmi->setSelectionHighlighting(needsSelectionHighlighting());
}
}

File diff suppressed because it is too large Load diff

View file

@ -20,6 +20,11 @@
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
#ifndef _TSSTATIC_H_
#define _TSSTATIC_H_
@ -236,6 +241,20 @@ public:
const Vector<S32>& getLOSDetails() const { return mLOSDetails; }
private:
virtual void onStaticModified(const char* slotName, const char*newValue = NULL);
protected:
Vector<S32> mDecalDetails;
Vector<S32>* mDecalDetailsPtr;
public:
bool mIgnoreZodiacs;
bool mHasGradients;
bool mInvertGradientRange;
Point2F mGradientRangeUser;
Point2F mGradientRange;
private:
void set_special_typing();
virtual void setSelectionFlags(U8 flags);
};
typedef TSStatic::MeshType TSMeshType;

View file

@ -151,16 +151,36 @@ public:
//----------------------------------------------------------------------------
// As shipped, AITurretShape plus the chain of classes it inherits from, consumes
// all 32 mask-bits. AFX uses one additional mask-bit in GameBase, which pushes
// AITurretShape over the mask-bit limit which will cause runtime crashes. As
// a workaround, AFX modifies AITurretShape so that it reuses the TurretUpdateMask
// defined by TurretShape rather than adding a unique TurretStateMask. This will
// make AITurretShape's network updates slightly less efficient, but should be
// acceptable for most uses of AITurretShape. If you plan to populate your levels
// with many AITurretShape objects, consider restoring it to use of a unique
// bit-mask, but if you do that, you will have to eliminate at use of at least one
// bit by one of it's parent classes. (FYI ShapeBase uses 20 bits.)
//
// Comment out this define if you want AITurretShape to define it's own bit-mask.
#define AFX_REUSE_TURRETSHAPE_MASKBITS
class AITurretShape: public TurretShape
{
typedef TurretShape Parent;
protected:
#ifdef AFX_REUSE_TURRETSHAPE_MASKBITS
enum MaskBits {
TurretStateMask = Parent::TurretUpdateMask,
NextFreeMask = Parent::NextFreeMask
};
#else // ORIGINAL CODE
enum MaskBits {
TurretStateMask = Parent::NextFreeMask,
NextFreeMask = Parent::NextFreeMask << 1
};
#endif
struct TargetInfo
{