소스 검색

datablock-temp-clone -- Implements creation of temporary datablock clones to allow late substitution of datablock fields.

Marc Chapman 8 년 전
부모
커밋
f9f05f154f

+ 92 - 2
Engine/source/T3D/debris.cpp

@@ -117,6 +117,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())
@@ -458,6 +537,8 @@ Debris::Debris()
 
    // Only allocated client side.
    mNetFlags.set( IsGhost );
+   ss_object = 0;
+   ss_index = 0;
 }
 
 Debris::~Debris()
@@ -473,6 +554,12 @@ Debris::~Debris()
       delete mPart;
       mPart = NULL;
    }
+   
+   if (mDataBlock && mDataBlock->isTempClone())
+   { 
+      delete mDataBlock;
+      mDataBlock = 0;
+   }
 }
 
 void Debris::initPersistFields()
@@ -502,6 +589,8 @@ bool Debris::onNewDataBlock( GameBaseData *dptr, bool reload )
    if( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
       return false;
 
+   if (mDataBlock->isTempClone())
+      return true;
    scriptOnNewDataBlock();
    return true;
 
@@ -526,7 +615,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() );
@@ -805,7 +894,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() );

+ 16 - 0
Engine/source/T3D/debris.h

@@ -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; }
 };
 
 

+ 134 - 6
Engine/source/T3D/fx/explosion.cpp

@@ -24,6 +24,7 @@
 // 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"
 
@@ -54,6 +55,8 @@
 #include "renderInstance/renderPassManager.h"
 #include "console/engineAPI.h"
 
+#include "sfx/sfxProfile.h"
+
 IMPLEMENT_CONOBJECT(Explosion);
 
 ConsoleDocClass( Explosion,
@@ -285,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),
@@ -818,6 +920,10 @@ Explosion::Explosion()
    mLight = LIGHTMGR->createLightInfo();
 
    mNetFlags.set( IsGhost );
+   ss_object = 0;
+   ss_index = 0;
+   mDataBlock = 0;
+   soundProfile_clone = 0;
 }
 
 Explosion::~Explosion()
@@ -830,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;
+   }
 }
 
 
@@ -988,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;
 }
@@ -1200,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 );
 
@@ -1228,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())
@@ -1266,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,
@@ -1283,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() );

+ 17 - 0
Engine/source/T3D/fx/explosion.h

@@ -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

+ 80 - 0
Engine/source/T3D/fx/particle.cpp

@@ -19,6 +19,11 @@
 // 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"
@@ -653,3 +658,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);
+}

+ 4 - 0
Engine/source/T3D/fx/particle.h

@@ -102,6 +102,10 @@ 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; }
   public:
    bool loadParameters();  
    bool reload(String &errorStr);

+ 147 - 0
Engine/source/T3D/fx/particleEmitter.cpp

@@ -712,6 +712,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
@@ -743,6 +871,7 @@ ParticleEmitter::ParticleEmitter()
 
    // ParticleEmitter should be allocated on the client only.
    mNetFlags.set( IsGhost );
+   mDataBlock = 0;
 }
 
 //-----------------------------------------------------------------------------
@@ -754,6 +883,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;
+   }
 }
 
 //-----------------------------------------------------------------------------
@@ -832,6 +974,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;

+ 9 - 0
Engine/source/T3D/fx/particleEmitter.h

@@ -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 _H_PARTICLE_EMITTER
 #define _H_PARTICLE_EMITTER
 
@@ -113,6 +117,11 @@ class ParticleEmitterData : public GameBaseData
    bool glow;                                ///< Renders this emitter into the glow buffer.
 
    bool reload();
+public:
+   /*C*/ ParticleEmitterData(const ParticleEmitterData&, bool = false);
+   /*D*/ ~ParticleEmitterData();
+   virtual ParticleEmitterData* cloneAndPerformSubstitutions(const SimObject*, S32 index=0);
+   virtual bool allowSubstitutions() const { return true; }
 };
 
 //*****************************************************************************

+ 13 - 0
Engine/source/T3D/gameBase/gameBase.cpp

@@ -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/gameBase/gameBase.h"
 #include "console/consoleTypes.h"
@@ -122,6 +126,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()
 {
@@ -290,6 +300,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;

+ 6 - 0
Engine/source/T3D/gameBase/gameBase.h

@@ -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 _GAMEBASE_H_
 #define _GAMEBASE_H_
 
@@ -113,6 +117,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);
 };
 
 //----------------------------------------------------------------------------

+ 41 - 0
Engine/source/T3D/projectile.cpp

@@ -194,6 +194,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()
@@ -585,6 +619,8 @@ Projectile::Projectile()
 
    mLightState.clear();
    mLightState.setLightInfo( mLight );
+
+   mDataBlock = 0;
 }
 
 Projectile::~Projectile()
@@ -593,6 +629,11 @@ Projectile::~Projectile()
 
    delete mProjectileShape;
    mProjectileShape = NULL;
+   if (mDataBlock && mDataBlock->isTempClone())
+   {
+      delete mDataBlock;
+      mDataBlock = 0;
+   }
 }
 
 //--------------------------------------------------------------------------

+ 7 - 0
Engine/source/T3D/projectile.h

@@ -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 _PROJECTILE_H_
 #define _PROJECTILE_H_
 
@@ -144,6 +148,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; }
 };
 
 

+ 60 - 0
Engine/source/T3D/shapeBase.cpp

@@ -200,6 +200,66 @@ ShapeBaseData::ShapeBaseData()
    dMemset( mountPointNode, -1, sizeof( S32 ) * SceneObject::NumMountPoints );
 }
 
+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
 {
    F32 mass;

+ 6 - 0
Engine/source/T3D/shapeBase.h

@@ -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,8 @@ public:
    DECLARE_CALLBACK(void, onEndSequence, (ShapeBase* obj, S32 slot, const char* name));
    DECLARE_CALLBACK( void, onForceUncloak, ( ShapeBase* obj, const char* reason ) );
    /// @}
+public:
+   ShapeBaseData(const ShapeBaseData&, bool = false);
 };
 
 

+ 12 - 0
Engine/source/T3D/staticShape.cpp

@@ -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");

+ 9 - 0
Engine/source/T3D/staticShape.h

@@ -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; }
 };
 
 

+ 42 - 0
Engine/source/console/simObject.cpp

@@ -92,12 +92,17 @@ SimObject::SimObject()
 
    mCopySource = NULL;
    mPersistentId = NULL;
+   is_temp_clone = false;
 }
 
 //-----------------------------------------------------------------------------
 
 SimObject::~SimObject()
 {
+   // if this is a temp-clone, we don't delete any members that were shallow-copied
+   // over from the source datablock.
+   if (is_temp_clone)
+      return;
    if( mFieldDictionary )
    {
       delete mFieldDictionary;
@@ -1327,6 +1332,43 @@ void SimObject::setDataFieldType(const char *typeName, StringTableEntry slotName
    }
 }
 
+// This is the copy-constructor used to create temporary datablock clones.
+// The <temp_clone> argument is added to distinguish this copy-constructor
+// from any general-purpose copy-constructor that might be needed in the
+// future. <temp_clone> should always be true when creating temporary 
+// datablock clones.
+//
+SimObject::SimObject(const SimObject& other, bool temp_clone)
+{
+   is_temp_clone = temp_clone;
+
+   objectName = other.objectName;
+   mOriginalName = other.mOriginalName;
+   nextNameObject = other.nextNameObject;
+   nextManagerNameObject = other.nextManagerNameObject;
+   nextIdObject = other.nextIdObject;
+   mGroup = other.mGroup;
+   mFlags = other.mFlags;
+   mCopySource = other.mCopySource;
+   mFieldDictionary = other.mFieldDictionary;
+   //mIdString = other.mIdString; // special treatment (see below)
+   mFilename = other.mFilename;
+   mDeclarationLine = other.mDeclarationLine;
+   mNotifyList = other.mNotifyList;
+   mId = other.mId;
+   mInternalName = other.mInternalName;
+   mCanSaveFieldDictionary = other.mCanSaveFieldDictionary;
+   mPersistentId = other.mPersistentId;
+   mNameSpace = other.mNameSpace;
+   mClassName = other.mClassName;
+   mSuperClassName = other.mSuperClassName;
+   preventNameChanging = other.preventNameChanging;
+
+   if (mId)
+      dSprintf( mIdString, sizeof( mIdString ), "%u", mId );
+   else
+      mIdString[ 0 ] = '\0';
+}
 //-----------------------------------------------------------------------------
 
 void SimObject::dumpClassHierarchy()

+ 10 - 0
Engine/source/console/simObject.h

@@ -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 _SIMOBJECT_H_
 #define _SIMOBJECT_H_
 
@@ -970,6 +974,12 @@ class SimObject: public ConsoleObject, public TamlCallbacks
 
       // EngineObject.
       virtual void destroySelf();
+protected:
+   bool   is_temp_clone;
+public:
+   /*C*/  SimObject(const SimObject&, bool = false);
+   bool   isTempClone() const { return is_temp_clone; }
+   virtual bool allowSubstitutions() const { return false; }
 };
 
 

+ 35 - 0
Engine/source/sfx/sfxDescription.cpp

@@ -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 "sfx/sfxDescription.h"
@@ -176,6 +180,37 @@ SFXDescription::SFXDescription( const SFXDescription& desc )
 
 //-----------------------------------------------------------------------------
 
+SFXDescription::SFXDescription(const SFXDescription& other, bool temp_clone)
+   : SimDataBlock(other, temp_clone),
+      mVolume( other.mVolume ),
+      mPitch( other.mPitch ),
+      mIsLooping( other.mIsLooping ),
+      mIsStreaming( other.mIsStreaming ),
+      mIs3D( other.mIs3D ),
+      mUseHardware( other.mUseHardware ),
+      mMinDistance( other.mMinDistance ),
+      mMaxDistance( other.mMaxDistance ),
+      mConeInsideAngle( other.mConeInsideAngle ),
+      mConeOutsideAngle( other.mConeOutsideAngle ),
+      mConeOutsideVolume( other.mConeOutsideVolume ),
+      mRolloffFactor( other.mRolloffFactor ),
+      mSourceGroup( other.mSourceGroup ),
+      mFadeInTime( other.mFadeInTime ),
+      mFadeOutTime( other.mFadeOutTime ),
+      mFadeInEase( other.mFadeInEase ),
+      mFadeOutEase( other.mFadeOutEase ),
+      mFadeLoops( other.mFadeLoops ),
+      mStreamPacketSize( other.mStreamPacketSize ),
+      mStreamReadAhead( other.mStreamReadAhead ),
+      mUseReverb( other.mUseReverb ),
+      mReverb( other.mReverb ),
+      mPriority( other.mPriority ),
+      mScatterDistance( other.mScatterDistance )
+{
+   for( U32 i = 0; i < MaxNumParameters; ++ i )
+      mParameters[ i ] = other.mParameters[ i ];
+}
+
 void SFXDescription::initPersistFields()
 {
    addGroup( "Playback" );

+ 7 - 0
Engine/source/sfx/sfxDescription.h

@@ -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 _SFXDESCRIPTION_H_
 #define _SFXDESCRIPTION_H_
 
@@ -192,6 +196,9 @@ class SFXDescription : public SimDataBlock
       /// Validates the description fixing any
       /// parameters that are out of range.
       void validate();
+   public:
+      SFXDescription(const SFXDescription&, bool);
+      virtual bool allowSubstitutions() const { return true; }
 };
 
 

+ 93 - 3
Engine/source/sfx/sfxProfile.cpp

@@ -24,6 +24,7 @@
 // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
 // Copyright (C) 2015 Faust Logic, Inc.
 //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
+
 #include "platform/platform.h"
 
 #include "sfx/sfxProfile.h"
@@ -96,9 +97,6 @@ SFXProfile::SFXProfile( SFXDescription* desc, const String& filename, bool prelo
 
 //-----------------------------------------------------------------------------
 
-SFXProfile::~SFXProfile()
-{
-}
 
 //-----------------------------------------------------------------------------
 
@@ -380,3 +378,95 @@ DefineEngineMethod( SFXProfile, getSoundDuration, F32, (),,
 {
    return ( F32 ) object->getSoundDuration() * 0.001f;
 }
+
+// enable this to help verify that temp-clones of AudioProfile are being deleted
+//#define TRACK_AUDIO_PROFILE_CLONES
+
+#ifdef TRACK_AUDIO_PROFILE_CLONES
+static int audio_prof_clones = 0;
+#endif
+
+SFXProfile::SFXProfile(const SFXProfile& other, bool temp_clone) : SFXTrack(other, temp_clone)
+{
+#ifdef TRACK_AUDIO_PROFILE_CLONES
+   audio_prof_clones++;
+   if (audio_prof_clones == 1)
+     Con::errorf("SFXProfile -- Clones are on the loose!");
+#endif
+   mResource = other.mResource;
+   mFilename = other.mFilename;
+   mPreload = other.mPreload;
+   mBuffer = other.mBuffer; // -- AudioBuffer loaded using mFilename
+   mChangedSignal = other.mChangedSignal;
+}
+
+SFXProfile::~SFXProfile()
+{
+  if (!isTempClone())
+    return;
+
+  // cleanup after a temp-clone
+
+  if (mDescription && mDescription->isTempClone())
+  {
+    delete mDescription;
+    mDescription = 0;
+  }
+
+#ifdef TRACK_AUDIO_PROFILE_CLONES
+  if (audio_prof_clones > 0)
+  {
+    audio_prof_clones--;
+    if (audio_prof_clones == 0)
+      Con::errorf("SFXProfile -- Clones eliminated!");
+  }
+  else
+    Con::errorf("SFXProfile -- Too many clones deleted!");
+#endif
+}
+
+// Clone and perform substitutions on the SFXProfile and on any SFXDescription
+// it references.
+SFXProfile* SFXProfile::cloneAndPerformSubstitutions(const SimObject* owner, S32 index)
+{
+  if (!owner)
+    return this;
+
+  SFXProfile* sub_profile_db = this;
+
+  // look for mDescriptionObject subs
+  SFXDescription* desc_db;
+  if (mDescription && mDescription->getSubstitutionCount() > 0)
+  {
+    SFXDescription* orig_db = mDescription;
+    desc_db = new SFXDescription(*orig_db, true);
+    orig_db->performSubstitutions(desc_db, owner, index);
+  }
+  else
+    desc_db = 0;
+
+  if (this->getSubstitutionCount() > 0 || desc_db)
+  {
+    sub_profile_db = new SFXProfile(*this, true);
+    performSubstitutions(sub_profile_db, owner, index);
+    if (desc_db)
+      sub_profile_db->mDescription = desc_db;
+  }
+
+  return sub_profile_db;
+}
+
+void SFXProfile::onPerformSubstitutions() 
+{ 
+   if ( SFX )
+   {
+      // If preload is enabled we load the resource
+      // and device buffer now to avoid a delay on
+      // first playback.
+      if ( mPreload && !_preloadBuffer() )
+         Con::errorf( "SFXProfile(%s)::onPerformSubstitutions: The preload failed!", getName() );
+
+      // We need to get device change notifications.
+      SFX->getEventSignal().notify( this, &SFXProfile::_onDeviceEvent );
+   }
+}

+ 10 - 0
Engine/source/sfx/sfxProfile.h

@@ -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 _SFXPROFILE_H_
 #define _SFXPROFILE_H_
 
@@ -175,6 +180,11 @@ class SFXProfile : public SFXTrack
       
       ///
       ChangedSignal& getChangedSignal() { return mChangedSignal; }
+   public:
+      /*C*/          SFXProfile(const SFXProfile&, bool = false);
+      SFXProfile*    cloneAndPerformSubstitutions(const SimObject*, S32 index=0);
+      virtual void   onPerformSubstitutions();
+      virtual bool   allowSubstitutions() const { return true; }
 };
 
 

+ 9 - 0
Engine/source/sfx/sfxTrack.cpp

@@ -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 "sfx/sfxTrack.h"
 #include "sfx/sfxTypes.h"
 #include "sfx/sfxDescription.h"
@@ -65,6 +69,11 @@ SFXTrack::SFXTrack( SFXDescription* description )
    dMemset( mParameters, 0, sizeof( mParameters ) );
 }
 
+SFXTrack::SFXTrack(const SFXTrack& other, bool temp_clone) : SimDataBlock(other, temp_clone)
+{
+   mDescription = other.mDescription;
+   dMemcpy(mParameters, other.mParameters, sizeof(mParameters));
+}
 //-----------------------------------------------------------------------------
 
 void SFXTrack::initPersistFields()

+ 6 - 0
Engine/source/sfx/sfxTrack.h

@@ -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 _SFXTRACK_H_
 #define _SFXTRACK_H_
 
@@ -92,6 +96,8 @@ class SFXTrack : public SimDataBlock
       DECLARE_CONOBJECT( SFXTrack );
       DECLARE_CATEGORY( "SFX" );
       DECLARE_DESCRIPTION( "Abstract base class for any kind of data that can be turned into SFXSources." );
+   public:
+      /*C*/ SFXTrack(const SFXTrack&, bool = false);
 };
 
 #endif // !_SFXTRACK_H_