Torque3D Documentation / _generateds / afxParticleEmitter.cpp

afxParticleEmitter.cpp

Engine/source/afx/ce/afxParticleEmitter.cpp

More...

Public Variables

Public Functions

ConsoleDocClass(afxParticleEmitterConeData , "@brief An AFX customized particle emitter that emits particles within <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> cone <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape.\n\n</a>" "An AFX customized particle emitter that emits particles within <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> cone shape." "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxEffects\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )
ConsoleDocClass(afxParticleEmitterData , "@brief A base datablock inherited by AFX <a href="/coding/class/structparticle/">Particle</a> Emitter <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">effects.\n\n</a>" "A base datablock inherited by AFX <a href="/coding/class/structparticle/">Particle</a> Emitter effects." "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxEffects\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )
ConsoleDocClass(afxParticleEmitterDiscData , "@brief An AFX customized particle emitter that emits particles within <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> disc <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape.\n\n</a>" "An AFX customized particle emitter that emits particles within <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> disc shape." "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxEffects\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )
ConsoleDocClass(afxParticleEmitterPathData , "@brief An AFX customized particle emitter that emits particles along <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">path.\n\n</a>" "An AFX customized particle emitter that emits particles along <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> path." "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxEffects\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )
ConsoleDocClass(afxParticleEmitterVectorData , "@brief An AFX customized particle emitter that emits particles along <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> 3D <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vector.\n\n</a>" "An AFX customized particle emitter that emits particles along <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> 3D vector." "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxEffects\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )
ImplementEnumType(afxParticleEmitterPath_OriginType , "Possible particle emitter path origin <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">types.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxParticleEmitterPath\n\n</a>" )

Detailed Description

Public Variables

 EndImplementEnumType 

Public Functions

ConsoleDocClass(afxParticleEmitterConeData , "@brief An AFX customized particle emitter that emits particles within <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> cone <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape.\n\n</a>" "An AFX customized particle emitter that emits particles within <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> cone shape." "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxEffects\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )

ConsoleDocClass(afxParticleEmitterData , "@brief A base datablock inherited by AFX <a href="/coding/class/structparticle/">Particle</a> Emitter <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">effects.\n\n</a>" "A base datablock inherited by AFX <a href="/coding/class/structparticle/">Particle</a> Emitter effects." "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxEffects\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )

ConsoleDocClass(afxParticleEmitterDiscData , "@brief An AFX customized particle emitter that emits particles within <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> disc <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape.\n\n</a>" "An AFX customized particle emitter that emits particles within <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> disc shape." "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxEffects\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )

ConsoleDocClass(afxParticleEmitterPathData , "@brief An AFX customized particle emitter that emits particles along <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">path.\n\n</a>" "An AFX customized particle emitter that emits particles along <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> path." "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxEffects\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )

ConsoleDocClass(afxParticleEmitterVectorData , "@brief An AFX customized particle emitter that emits particles along <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> 3D <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vector.\n\n</a>" "An AFX customized particle emitter that emits particles along <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> 3D vector." "\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxEffects\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )

IMPLEMENT_CO_DATABLOCK_V1(afxParticleEmitterConeData )

IMPLEMENT_CO_DATABLOCK_V1(afxParticleEmitterData )

IMPLEMENT_CO_DATABLOCK_V1(afxParticleEmitterDiscData )

IMPLEMENT_CO_DATABLOCK_V1(afxParticleEmitterPathData )

IMPLEMENT_CO_DATABLOCK_V1(afxParticleEmitterVectorData )

ImplementEnumType(afxParticleEmitterPath_OriginType , "Possible particle emitter path origin <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">types.\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxParticleEmitterPath\n\n</a>" )

   1
   2
   3//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
   4// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
   5// Copyright (C) 2015 Faust Logic, Inc.
   6//
   7// Permission is hereby granted, free of charge, to any person obtaining a copy
   8// of this software and associated documentation files (the "Software"), to
   9// deal in the Software without restriction, including without limitation the
  10// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  11// sell copies of the Software, and to permit persons to whom the Software is
  12// furnished to do so, subject to the following conditions:
  13//
  14// The above copyright notice and this permission notice shall be included in
  15// all copies or substantial portions of the Software.
  16//
  17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  23// IN THE SOFTWARE.
  24//
  25//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  26
  27#include "afx/arcaneFX.h"
  28
  29#include "math/mathIO.h"
  30#include "scene/sceneManager.h"
  31#include "T3D/gameBase/gameProcess.h"
  32
  33#include "afx/util/afxPath.h"
  34#include "afx/util/afxPath3D.h"
  35#include "afx/ce/afxParticleEmitter.h"
  36
  37IMPLEMENT_CO_DATABLOCK_V1(afxParticleEmitterData);
  38
  39ConsoleDocClass( afxParticleEmitterData,
  40   "@brief A base datablock inherited by AFX Particle Emitter effects.\n\n"
  41
  42   "A base datablock inherited by AFX Particle Emitter effects."
  43   "\n\n"
  44
  45   "@ingroup afxEffects\n"
  46   "@ingroup AFX\n"
  47   "@ingroup Datablocks\n"
  48);
  49
  50afxParticleEmitterData::afxParticleEmitterData()
  51{
  52  fade_velocity = false;  // coordinate velocity amount with fade amout
  53  fade_offset   = false;  // coordinate ejection-offset amount with fade amount
  54  pe_vector.set(0.0,0.0,0.0);
  55  pe_vector_is_world = false;
  56  tpaths_string = ST_NULLSTRING;
  57  tPathDataBlocks.clear();
  58  tPathDataBlockIds.clear();
  59}
  60
  61afxParticleEmitterData::afxParticleEmitterData(const afxParticleEmitterData& other, bool temp_clone) : ParticleEmitterData(other, temp_clone)
  62{
  63  fade_velocity = other.fade_velocity;
  64  fade_offset = other.fade_offset;
  65  pe_vector = other.pe_vector;
  66  pe_vector_is_world = other.pe_vector_is_world;
  67  tpaths_string = other.tpaths_string;
  68  tPathDataBlocks = other.tPathDataBlocks;
  69  //tPathDataBlockIds = other.tPathDataBlockIds;
  70}
  71
  72void afxParticleEmitterData::initPersistFields()
  73{
  74  addField("fadeVelocity",      TypeBool,    Offset(fade_velocity, afxParticleEmitterData),
  75    "If true, the initial velocity of emitted particles is multiplied by the fade amount "
  76    "of the containing effect wrapper. As the effect fades-in and out, so does the "
  77    "initial velocity of new particles.");
  78  addField("fadeOffset",        TypeBool,    Offset(fade_offset, afxParticleEmitterData),
  79    "If true, the ejection offset of emitted particles is multiplied by the fade amount "
  80    "of the containing effect wrapper. As the effect fades-in and out, so does the "
  81    "ejection offset of new particles.");
  82  addField("vector",            TypePoint3F, Offset(pe_vector, afxParticleEmitterData),
  83    "General direction vector used for emitting particles. Its exact interpretation is "
  84    "determined by the particle emitter subclass.");
  85  addField("vectorIsWorld",     TypeBool,    Offset(pe_vector_is_world, afxParticleEmitterData),
  86    "Sets whether the vector field should be interpreted as a vector in the world "
  87    "coordinate system.");
  88  addField("pathsTransform",    TypeString,  Offset(tpaths_string, afxParticleEmitterData),
  89    "A string of paths to be used as transform paths. Each path name must reference an "
  90    "afxPathData datablock. Transform paths are used to translate particles along a given "
  91    "path or series of paths.");
  92
  93  Parent::initPersistFields();
  94}
  95
  96void afxParticleEmitterData::packData(BitStream* stream)
  97{
  98  Parent::packData(stream);
  99
 100  stream->writeFlag(fade_velocity);
 101  stream->writeFlag(fade_offset);
 102  mathWrite(*stream, pe_vector);
 103  stream->writeFlag(pe_vector_is_world);
 104
 105  stream->write(tPathDataBlockIds.size());
 106  for (int i = 0; i < tPathDataBlockIds.size(); i++)
 107    stream->write(tPathDataBlockIds[i]);
 108}
 109
 110void afxParticleEmitterData::unpackData(BitStream* stream)
 111{
 112  Parent::unpackData(stream);
 113
 114  fade_velocity = stream->readFlag();
 115  fade_offset   = stream->readFlag();
 116  mathRead(*stream, &pe_vector);
 117  pe_vector_is_world = stream->readFlag();
 118
 119  U32 n_db; stream->read(&n_db);
 120  tPathDataBlockIds.setSize(n_db);
 121  for (U32 i = 0; i < n_db; i++)
 122    stream->read(&tPathDataBlockIds[i]);
 123}
 124
 125bool afxParticleEmitterData::onAdd()
 126{
 127  if( Parent::onAdd() == false )
 128    return false;
 129
 130  if (tpaths_string != ST_NULLSTRING && tpaths_string[0] == '\0')
 131  {
 132    Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) empty transform paths string.", getName());
 133    return false;
 134  }
 135  
 136  if (tpaths_string != ST_NULLSTRING && dStrlen(tpaths_string) > 255) 
 137  {
 138    Con::errorf(ConsoleLogEntry::General, "ParticleEmitterData(%s) transform paths string too long [> 255 chars].", getName());
 139    return false;
 140  }
 141  
 142  if (tpaths_string != ST_NULLSTRING) 
 143  {
 144    Vector<char*> dataBlocks(__FILE__, __LINE__);
 145    dsize_t tokCopyLen = dStrlen(tpaths_string) + 1;
 146    char* tokCopy = new char[tokCopyLen];
 147    dStrcpy(tokCopy, tpaths_string, tokCopyLen);
 148    
 149    char* currTok = dStrtok(tokCopy, " \t");
 150    while (currTok != NULL) 
 151    {
 152      dataBlocks.push_back(currTok);
 153      currTok = dStrtok(NULL, " \t");
 154    }
 155    if (dataBlocks.size() == 0) 
 156    {
 157      Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) invalid transform paths string.  No datablocks found", getName());
 158      delete [] tokCopy;
 159      return false;
 160    }
 161    tPathDataBlocks.clear();
 162    tPathDataBlockIds.clear();
 163    
 164    for (U32 i = 0; i < dataBlocks.size(); i++) 
 165    {
 166      afxPathData* pData = NULL;
 167      if (Sim::findObject(dataBlocks[i], pData) == false) 
 168      {
 169        Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) unable to find transform path datablock: %s", getName(), dataBlocks[i]);
 170      } 
 171      else 
 172      {
 173        tPathDataBlocks.push_back(pData);
 174        tPathDataBlockIds.push_back(pData->getId());
 175      }
 176    }
 177    delete [] tokCopy;
 178    if (tPathDataBlocks.size() == 0) 
 179    {
 180      Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) unable to find any transform path datablocks", getName());
 181      return false;
 182    }
 183  }
 184
 185  return true;
 186}
 187
 188bool afxParticleEmitterData::preload(bool server, String &errorStr)
 189{
 190  if (Parent::preload(server, errorStr) == false)
 191    return false;
 192
 193  tPathDataBlocks.clear();
 194  for (U32 i = 0; i < tPathDataBlockIds.size(); i++) 
 195  {
 196    afxPathData* pData = NULL;
 197    if (Sim::findObject(tPathDataBlockIds[i], pData) == false)
 198    {
 199      Con::warnf(ConsoleLogEntry::General, 
 200                 "ParticleEmitterData(%s) unable to find transform path datablock: %d", 
 201                 getName(), tPathDataBlockIds[i]);
 202    }
 203    else
 204      tPathDataBlocks.push_back(pData);
 205  }
 206
 207  return true;
 208}
 209
 210//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 211// VECTOR
 212
 213IMPLEMENT_CO_DATABLOCK_V1(afxParticleEmitterVectorData);
 214
 215ConsoleDocClass( afxParticleEmitterVectorData,
 216   "@brief An AFX customized particle emitter that emits particles along a 3D vector.\n\n"
 217
 218   "An AFX customized particle emitter that emits particles along a 3D vector."
 219   "\n\n"
 220
 221   "@ingroup afxEffects\n"
 222   "@ingroup AFX\n"
 223   "@ingroup Datablocks\n"
 224);
 225
 226afxParticleEmitterVectorData::afxParticleEmitterVectorData()
 227{
 228}
 229
 230afxParticleEmitterVectorData::afxParticleEmitterVectorData(const afxParticleEmitterVectorData& other, bool temp_clone) : afxParticleEmitterData(other, temp_clone)
 231{
 232}
 233
 234void afxParticleEmitterVectorData::initPersistFields()
 235{
 236  Parent::initPersistFields();
 237}
 238
 239void afxParticleEmitterVectorData::packData(BitStream* stream)
 240{
 241  Parent::packData(stream);
 242}
 243
 244void afxParticleEmitterVectorData::unpackData(BitStream* stream)
 245{
 246  Parent::unpackData(stream);
 247}
 248
 249bool afxParticleEmitterVectorData::onAdd()
 250{
 251  if (Parent::onAdd() == false)
 252    return false;
 253  return true;
 254}
 255
 256bool afxParticleEmitterVectorData::preload(bool server, String &errorStr)
 257{
 258  if (Parent::preload(server, errorStr) == false)
 259    return false;
 260  return true;
 261}
 262
 263//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 264// CONE
 265
 266IMPLEMENT_CO_DATABLOCK_V1(afxParticleEmitterConeData);
 267
 268ConsoleDocClass( afxParticleEmitterConeData,
 269   "@brief An AFX customized particle emitter that emits particles within a cone shape.\n\n"
 270
 271   "An AFX customized particle emitter that emits particles within a cone shape."
 272   "\n\n"
 273
 274   "@ingroup afxEffects\n"
 275   "@ingroup AFX\n"
 276   "@ingroup Datablocks\n"
 277);
 278
 279afxParticleEmitterConeData::afxParticleEmitterConeData()
 280{
 281  spread_min = 0.0f;
 282  spread_max = 90.0f;
 283}
 284
 285afxParticleEmitterConeData::afxParticleEmitterConeData(const afxParticleEmitterConeData& other, bool temp_clone) : afxParticleEmitterData(other, temp_clone)
 286{
 287  spread_min = other.spread_min;
 288  spread_max = other.spread_max;
 289}
 290
 291void afxParticleEmitterConeData::initPersistFields()
 292{
 293  addField("spreadMin",   TypeF32,    Offset(spread_min, afxParticleEmitterConeData),
 294    "...");
 295  addField("spreadMax",   TypeF32,    Offset(spread_max, afxParticleEmitterConeData),
 296    "...");
 297
 298  Parent::initPersistFields();
 299}
 300
 301void afxParticleEmitterConeData::packData(BitStream* stream)
 302{
 303  Parent::packData(stream);
 304
 305  stream->writeRangedU32((U32)spread_min, 0, 180);
 306  stream->writeRangedU32((U32)spread_max, 0, 180);
 307}
 308
 309void afxParticleEmitterConeData::unpackData(BitStream* stream)
 310{
 311  Parent::unpackData(stream);
 312
 313  spread_min = stream->readRangedU32(0, 180);
 314  spread_max = stream->readRangedU32(0, 180);
 315}
 316
 317bool afxParticleEmitterConeData::onAdd()
 318{
 319  if( Parent::onAdd() == false )
 320    return false;
 321
 322  if (spread_min < 0.0f) 
 323  {
 324    Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) spreadMin < 0.0", getName());
 325    spread_min = 0.0f;
 326  }
 327  if (spread_max > 180.0f) 
 328  {
 329    Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) spreadMax > 180.0f", getName());
 330    spread_max = 180.0f;
 331  }
 332
 333  if (spread_max > 179.5f) 
 334    spread_max = 179.5f;
 335  if (spread_min > 179.5f) 
 336    spread_min = 179.5f;
 337
 338  if (spread_min > spread_max) 
 339  {
 340    Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) spreadMin > spreadMax", getName());
 341    spread_min = spread_max;
 342  }
 343
 344  return true;
 345}
 346
 347bool afxParticleEmitterConeData::preload(bool server, String &errorStr)
 348{
 349  if (Parent::preload(server, errorStr) == false)
 350    return false;
 351  return true;
 352}
 353
 354//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 355// PATH
 356
 357IMPLEMENT_CO_DATABLOCK_V1(afxParticleEmitterPathData);
 358
 359ConsoleDocClass( afxParticleEmitterPathData,
 360   "@brief An AFX customized particle emitter that emits particles along a path.\n\n"
 361
 362   "An AFX customized particle emitter that emits particles along a path."
 363   "\n\n"
 364
 365   "@ingroup afxEffects\n"
 366   "@ingroup AFX\n"
 367   "@ingroup Datablocks\n"
 368);
 369
 370afxParticleEmitterPathData::afxParticleEmitterPathData()
 371{
 372  epaths_string = ST_NULLSTRING;
 373  epathDataBlocks.clear();
 374  epathDataBlockIds.clear();
 375  path_origin_type = PATHEMIT_ORIGIN;
 376  ground_conform = false;
 377  ground_conform_terrain = true;
 378  ground_conform_interiors = true;
 379  ground_conform_height = 0.0f;
 380}
 381
 382afxParticleEmitterPathData::afxParticleEmitterPathData(const afxParticleEmitterPathData& other, bool temp_clone) : afxParticleEmitterData(other, temp_clone)
 383{
 384  epaths_string = other.epaths_string;
 385  epathDataBlocks = other.epathDataBlocks;
 386  //epathDataBlockIds = other.epathDataBlockIds;
 387  path_origin_type = other.path_origin_type;
 388  ground_conform = other.ground_conform;
 389  ground_conform_terrain = other.ground_conform_terrain;
 390  ground_conform_interiors = other.ground_conform_interiors;
 391  ground_conform_height = other.ground_conform_height;
 392}
 393
 394ImplementEnumType( afxParticleEmitterPath_OriginType, "Possible particle emitter path origin types.\n" "@ingroup afxParticleEmitterPath\n\n" )
 395  { afxParticleEmitterPathData::PATHEMIT_ORIGIN,  "origin",      "..." },
 396  { afxParticleEmitterPathData::PATHEMIT_POINT,   "point",      "..." },
 397  { afxParticleEmitterPathData::PATHEMIT_VECTOR,  "vector",      "..." },
 398  { afxParticleEmitterPathData::PATHEMIT_TANGENT, "tangent",      "..." },
 399EndImplementEnumType;
 400
 401void afxParticleEmitterPathData::initPersistFields()
 402{
 403  addField("paths",         TypeString,  Offset(epaths_string,    afxParticleEmitterPathData),
 404    "...");
 405
 406  addField("pathOrigin", TYPEID<afxParticleEmitterPathData::PathOriginType>(), Offset(path_origin_type, afxParticleEmitterPathData),
 407    "...");
 408
 409  // JTF Note: take a look at these and make sure they are ok.
 410  addField("groundConform",           TypeBool, Offset(ground_conform,            afxParticleEmitterPathData),
 411    "...");
 412  addField("groundConformTerrain",    TypeBool, Offset(ground_conform_terrain,    afxParticleEmitterPathData),
 413    "...");
 414  addField("groundConformInteriors",  TypeBool, Offset(ground_conform_interiors,  afxParticleEmitterPathData),
 415    "...");
 416  addField("groundConformHeight",     TypeF32,  Offset(ground_conform_height,     afxParticleEmitterPathData),
 417    "...");
 418
 419  Parent::initPersistFields();
 420}
 421
 422void afxParticleEmitterPathData::packData(BitStream* stream)
 423{
 424  Parent::packData(stream);
 425
 426  stream->write(epathDataBlockIds.size());
 427  for (int i = 0; i < epathDataBlockIds.size(); i++)
 428    stream->write(epathDataBlockIds[i]);
 429  stream->write(path_origin_type);
 430  stream->writeFlag(ground_conform);
 431  stream->writeFlag(ground_conform_terrain);
 432  stream->writeFlag(ground_conform_interiors);
 433  stream->write(ground_conform_height);
 434}
 435
 436void afxParticleEmitterPathData::unpackData(BitStream* stream)
 437{
 438  Parent::unpackData(stream);
 439
 440  U32 n_db; stream->read(&n_db);
 441  epathDataBlockIds.setSize(n_db);
 442  for (U32 i = 0; i < n_db; i++)
 443    stream->read(&epathDataBlockIds[i]);
 444  stream->read(&path_origin_type);
 445  ground_conform = stream->readFlag();
 446  ground_conform_terrain = stream->readFlag();
 447  ground_conform_interiors = stream->readFlag();
 448  stream->read(&ground_conform_height);
 449}
 450
 451bool afxParticleEmitterPathData::onAdd()
 452{
 453  if( Parent::onAdd() == false )
 454    return false;
 455
 456  // path
 457  if (epaths_string != ST_NULLSTRING && epaths_string[0] == '\0')
 458  {
 459    Con::warnf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) empty paths string.", getName());
 460    return false;
 461  }
 462
 463  if (epaths_string != ST_NULLSTRING && dStrlen(epaths_string) > 255) 
 464  {
 465    Con::errorf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) paths string too long [> 255 chars].", getName());
 466    return false;
 467  }
 468
 469  if (epaths_string != ST_NULLSTRING) 
 470  {
 471    Vector<char*> dataBlocks(__FILE__, __LINE__);
 472    dsize_t tokCopyLen = dStrlen(epaths_string) + 1;
 473    char* tokCopy = new char[tokCopyLen];
 474    dStrcpy(tokCopy, epaths_string, tokCopyLen);
 475
 476    char* currTok = dStrtok(tokCopy, " \t");
 477    while (currTok != NULL) 
 478    {
 479      dataBlocks.push_back(currTok);
 480      currTok = dStrtok(NULL, " \t");
 481    }
 482    if (dataBlocks.size() == 0) 
 483    {
 484      Con::warnf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) invalid paths string.  No datablocks found", getName());
 485      delete [] tokCopy;
 486      return false;
 487    }
 488    epathDataBlocks.clear();
 489    epathDataBlockIds.clear();
 490
 491    for (U32 i = 0; i < dataBlocks.size(); i++) 
 492    {
 493      afxPathData* pData = NULL;
 494      if (Sim::findObject(dataBlocks[i], pData) == false) 
 495      {
 496        Con::warnf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) unable to find path datablock: %s", getName(), dataBlocks[i]);
 497      } 
 498      else 
 499      {
 500        epathDataBlocks.push_back(pData);
 501        epathDataBlockIds.push_back(pData->getId());
 502      }
 503    }
 504    delete [] tokCopy;
 505    if (epathDataBlocks.size() == 0) 
 506    {
 507      Con::warnf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) unable to find any path datablocks", getName());
 508      return false;
 509    }
 510  }
 511
 512  return true;
 513}
 514
 515bool afxParticleEmitterPathData::preload(bool server, String &errorStr)
 516{
 517  if (Parent::preload(server, errorStr) == false)
 518    return false;
 519
 520  epathDataBlocks.clear();
 521  for (U32 i = 0; i < epathDataBlockIds.size(); i++) 
 522  {
 523    afxPathData* pData = NULL;
 524    if (Sim::findObject(epathDataBlockIds[i], pData) == false)
 525    {
 526      Con::warnf(ConsoleLogEntry::General, 
 527                 "afxParticleEmitterPathData(%s) unable to find path datablock: %d", 
 528                 getName(), epathDataBlockIds[i]);
 529    }
 530    else
 531      epathDataBlocks.push_back(pData);
 532  }
 533  parts_per_eject = epathDataBlocks.size();
 534
 535  return true;
 536}
 537
 538void afxParticleEmitterPathData::onPerformSubstitutions()
 539{
 540  Parent::onPerformSubstitutions();
 541
 542
 543  if (epaths_string != ST_NULLSTRING && epaths_string[0] == '\0')
 544  {
 545    Con::warnf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) empty paths string.", getName());
 546    return;// false;
 547  }
 548
 549  if (epaths_string != ST_NULLSTRING && dStrlen(epaths_string) > 255) 
 550  {
 551    Con::errorf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) paths string too long [> 255 chars].", getName());
 552    return;// false;
 553  }
 554
 555  if (epaths_string != ST_NULLSTRING) 
 556  {
 557    Vector<char*> dataBlocks(__FILE__, __LINE__);
 558    dsize_t tokCopyLen = dStrlen(epaths_string) + 1;
 559    char* tokCopy = new char[tokCopyLen];
 560    dStrcpy(tokCopy, epaths_string, tokCopyLen);
 561
 562    char* currTok = dStrtok(tokCopy, " \t");
 563    while (currTok != NULL) 
 564    {
 565      dataBlocks.push_back(currTok);
 566      currTok = dStrtok(NULL, " \t");
 567    }
 568    if (dataBlocks.size() == 0) 
 569    {
 570      Con::warnf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) invalid paths string.  No datablocks found", getName());
 571      delete [] tokCopy;
 572      return;// false;
 573    }
 574    epathDataBlocks.clear();
 575    epathDataBlockIds.clear();
 576
 577    for (U32 i = 0; i < dataBlocks.size(); i++) 
 578    {
 579      afxPathData* pData = NULL;
 580      if (Sim::findObject(dataBlocks[i], pData) == false) 
 581      {
 582        Con::warnf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) unable to find path datablock: %s", getName(), dataBlocks[i]);
 583      } 
 584      else 
 585      {
 586        epathDataBlocks.push_back(pData);
 587        epathDataBlockIds.push_back(pData->getId());
 588      }
 589    }
 590    delete [] tokCopy;
 591    if (epathDataBlocks.size() == 0) 
 592    {
 593      Con::warnf(ConsoleLogEntry::General, "afxParticleEmitterPathData(%s) unable to find any path datablocks", getName());
 594      return;// false;
 595    }
 596  }
 597
 598
 599  /*epathDataBlocks.clear();
 600  for (U32 i = 0; i < epathDataBlockIds.size(); i++) 
 601  {
 602    afxPathData* pData = NULL;
 603    if (Sim::findObject(epathDataBlockIds[i], pData) == false)
 604    {
 605      Con::warnf(ConsoleLogEntry::General, 
 606                 "afxParticleEmitterPathData(%s) unable to find path datablock: %d", 
 607                 getName(), epathDataBlockIds[i]);
 608    }
 609    else
 610      epathDataBlocks.push_back(pData);
 611  }
 612  */
 613  parts_per_eject = epathDataBlocks.size();
 614}
 615
 616//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 617// DISC
 618
 619IMPLEMENT_CO_DATABLOCK_V1(afxParticleEmitterDiscData);
 620
 621ConsoleDocClass( afxParticleEmitterDiscData,
 622   "@brief An AFX customized particle emitter that emits particles within a disc shape.\n\n"
 623
 624   "An AFX customized particle emitter that emits particles within a disc shape."
 625   "\n\n"
 626
 627   "@ingroup afxEffects\n"
 628   "@ingroup AFX\n"
 629   "@ingroup Datablocks\n"
 630);
 631
 632afxParticleEmitterDiscData::afxParticleEmitterDiscData()
 633{
 634  pe_radius_min = 0.0f;
 635  pe_radius_max = 1.0f;
 636}
 637
 638afxParticleEmitterDiscData::afxParticleEmitterDiscData(const afxParticleEmitterDiscData& other, bool temp_clone) : afxParticleEmitterData(other, temp_clone)
 639{
 640  pe_radius_min = other.pe_radius_min;
 641  pe_radius_max = other.pe_radius_max;
 642}
 643
 644void afxParticleEmitterDiscData::initPersistFields()
 645{
 646  addField("radiusMin",   TypeF32,    Offset(pe_radius_min, afxParticleEmitterDiscData),
 647    "...");
 648  addField("radiusMax",   TypeF32,    Offset(pe_radius_max, afxParticleEmitterDiscData),
 649    "...");
 650
 651  Parent::initPersistFields();
 652}
 653
 654void afxParticleEmitterDiscData::packData(BitStream* stream)
 655{
 656  Parent::packData(stream);
 657
 658  stream->writeInt((S32)(pe_radius_min * 100), 16);
 659  stream->writeInt((S32)(pe_radius_max * 100), 16);
 660}
 661
 662void afxParticleEmitterDiscData::unpackData(BitStream* stream)
 663{
 664  Parent::unpackData(stream);
 665
 666  pe_radius_min = stream->readInt(16) / 100.0f;
 667  pe_radius_max = stream->readInt(16) / 100.0f;
 668}
 669
 670bool afxParticleEmitterDiscData::onAdd()
 671{
 672  if( Parent::onAdd() == false )
 673    return false;
 674
 675  return true;
 676}
 677
 678bool afxParticleEmitterDiscData::preload(bool server, String &errorStr)
 679{
 680  if (Parent::preload(server, errorStr) == false)
 681    return false;
 682
 683  return true;
 684}
 685
 686//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 687//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 688
 689afxParticleEmitter::afxParticleEmitter()
 690{
 691  mDataBlock = NULL;
 692  pe_vector.set(0,0,1);
 693  pe_vector_norm.set(0,0,1);
 694  tpaths.clear();
 695  tpath_mults.clear();
 696  n_tpath_points = 0;
 697  tpath_points = NULL;
 698  afx_owner = 0;
 699}
 700
 701afxParticleEmitter::~afxParticleEmitter()
 702{
 703}
 704
 705bool afxParticleEmitter::onAdd()
 706{
 707  if( !Parent::onAdd() )
 708    return false;
 709
 710  if (dynamic_cast<afxParticleEmitterData*>(mDataBlock))
 711    init_paths();
 712
 713  return true;
 714}
 715
 716void afxParticleEmitter::onRemove()
 717{
 718  if (dynamic_cast<afxParticleEmitterData*>(mDataBlock))
 719    cleanup_paths();
 720
 721  Parent::onRemove();
 722}
 723
 724void afxParticleEmitter::init_paths()
 725{
 726  if (!mDataBlock)
 727  {
 728    n_tpath_points = 0;
 729    tpath_points = NULL;
 730    return;
 731  }
 732
 733  if (mDataBlock->tPathDataBlocks.size() < 1)
 734  {
 735    n_tpath_points = 0;
 736    tpath_points = NULL;
 737  }
 738  else
 739  {
 740    n_tpath_points = mDataBlock->tPathDataBlocks.size();
 741    tpath_points = new Point3F*[n_tpath_points];
 742  
 743    for (U32 i=0; i < n_tpath_points; i++)
 744    {
 745      afxPathData* pd = mDataBlock->tPathDataBlocks[i]; 
 746      if (!pd)
 747        continue;
 748
 749      if (pd->getSubstitutionCount() > 0 && afx_owner)
 750      {
 751        afxPathData* orig_db = pd;
 752        pd = new afxPathData(*orig_db, true);
 753        orig_db->performSubstitutions(pd, afx_owner);
 754      }
 755
 756      if (pd->num_points > 0)
 757      {
 758        afxPath3D* path = new afxPath3D();
 759        if (pd->times)
 760          path->buildPath( pd->num_points, pd->points, pd->times, pd->delay, 1.0f );
 761        else if (pd->lifetime == 0)
 762          path->buildPath( pd->num_points, pd->points, pd->delay, 1.0f );
 763        else
 764          path->buildPath( pd->num_points, pd->points, pd->delay, pd->delay+pd->lifetime ); 
 765        path->setLoopType( pd->loop_type );
 766        tpaths.push_back(path);  
 767
 768        tpath_mults.push_back( pd->mult );
 769
 770        tpath_points[i] = new Point3F[pd->num_points];
 771        for (U32 j=0; j<pd->num_points; j++)
 772          tpath_points[i][j] = pd->points[j];
 773      }
 774      else
 775      {
 776        Con::warnf("afxParticleEmitter::init_paths() -- pathsTransform datablock (%d) has no points.", i);
 777      }
 778
 779      if (pd->isTempClone())
 780        delete pd;
 781    }
 782  }
 783}
 784
 785void afxParticleEmitter::cleanup_paths()
 786{
 787  if (n_tpath_points < 1)
 788    return;
 789
 790  for (U32 i=0; i < tpaths.size(); i++)
 791  {
 792    if (tpaths[i])
 793      delete tpaths[i];
 794  }
 795  tpaths.clear();
 796  
 797  if (tpath_points)
 798  {
 799    if (mDataBlock)
 800    {
 801      for (U32 i=0; i < n_tpath_points; i++)
 802      {
 803        if (tpath_points[i])
 804          delete [] tpath_points[i];
 805      }
 806    }
 807    
 808    delete [] tpath_points;
 809    tpath_points = 0;
 810  }
 811}
 812
 813void afxParticleEmitter::sub_particleUpdate(Particle* part)
 814{
 815   if (tpaths.size() < 1)
 816      return;
 817
 818   F32 t = ((F32)part->currentAge)/((F32)part->totalLifetime);
 819   for (U32 i=0; i < tpaths.size(); i++)
 820   {
 821      F32 t_last = part->t_last;
 822      Point3F path_delta = (t_last <= 0.0f) ? tpaths[i]->evaluateAtTime(t) : tpaths[i]->evaluateAtTime(t_last, t);
 823
 824      if (mDataBlock->tPathDataBlocks[i]->concentric)
 825      {
 826         // scale radial vector by x-component of path
 827         part->pos_local += part->radial_v*path_delta.x;
 828         // scale axis vector by z-component of path
 829         part->pos_local += pe_vector_norm*path_delta.z;
 830         // y-component is ignored
 831      }
 832      else
 833      {
 834         part->pos_local += path_delta;     
 835      }
 836   }
 837
 838   part->t_last = t;
 839}
 840
 841void afxParticleEmitter::preCompute(const MatrixF& mat)
 842{
 843   // Put vector into the space of the input matrix
 844   pe_vector = mDataBlock->pe_vector;
 845   if (!mDataBlock->pe_vector_is_world)
 846     mat.mulV(pe_vector);
 847
 848   pe_vector_norm = pe_vector;
 849   pe_vector_norm.normalize();
 850
 851   // Transform Paths: rebuild with current matrix
 852   for( U32 i=0; i < tpaths.size(); i++ )
 853   {
 854      for( U32 j=0; j < tpaths[i]->getNumPoints(); j++ )
 855      {
 856         Point3F p = tpath_points[i][j];
 857         mat.mulV(p);
 858         tpaths[i]->setPointPosition(j, p);
 859      }
 860
 861      tpaths[i]->reBuildPath();
 862   }
 863
 864   sub_preCompute(mat);
 865}
 866
 867void afxParticleEmitter::afx_emitParticles(const Point3F& point, const bool useLastPosition, const Point3F& velocity, const U32 numMilliseconds)
 868{
 869  if (mDead) return;
 870
 871  // lifetime over - no more particles
 872  if (mLifetimeMS > 0 && mElapsedTimeMS > mLifetimeMS)
 873    return;
 874
 875  Point3F realStart;
 876  if (useLastPosition && mHasLastPosition)
 877    realStart = mLastPosition;
 878  else
 879    realStart = point;
 880
 881  afx_emitParticles(realStart, point, velocity, numMilliseconds);
 882}
 883
 884void afxParticleEmitter::afx_emitParticles(const Point3F& start, const Point3F& end, const Point3F& velocity, const U32 numMilliseconds)
 885{
 886  if (mDead) return;
 887
 888  // lifetime over - no more particles
 889  if (mLifetimeMS > 0 && mElapsedTimeMS > mLifetimeMS)
 890    return;
 891
 892  U32 currTime = 0;
 893  bool particlesAdded = false;
 894
 895  if (mNextParticleTime != 0) 
 896  {
 897    // Need to handle next particle
 898    //
 899    if (mNextParticleTime > numMilliseconds) 
 900    {
 901      // Defer to next update
 902      //  (Note that this introduces a potential spatial irregularity if the owning
 903      //   object is accelerating, and updating at a low frequency)
 904      //
 905      mNextParticleTime -= numMilliseconds;
 906      mInternalClock += numMilliseconds;
 907      mLastPosition = end;
 908      mHasLastPosition = true;
 909      return;
 910    } 
 911    else 
 912    {
 913      currTime       += mNextParticleTime;
 914      mInternalClock += mNextParticleTime;
 915      // Emit particle at curr time
 916
 917      // Create particle at the correct position
 918      Point3F pos;
 919      pos.interpolate(start, end, F32(currTime) / F32(numMilliseconds));
 920
 921      for (S32 p = 0; p < mDataBlock->parts_per_eject; p++)
 922         {
 923            sub_addParticle(pos, velocity, numMilliseconds-currTime, p);
 924            particlesAdded = true;
 925         }
 926      mNextParticleTime = 0;
 927    }
 928  }
 929
 930  while (currTime < numMilliseconds) 
 931  {
 932     S32 nextTime = mDataBlock->ejectionPeriodMS;
 933     if (mDataBlock->periodVarianceMS != 0) 
 934     {
 935        nextTime += S32(gRandGen.randI() % (2 * mDataBlock->periodVarianceMS + 1)) -
 936           S32(mDataBlock->periodVarianceMS);
 937     }
 938     AssertFatal(nextTime > 0, "Error, next particle ejection time must always be greater than 0");
 939
 940     if (currTime + nextTime > numMilliseconds) 
 941     {
 942        mNextParticleTime = (currTime + nextTime) - numMilliseconds;
 943        mInternalClock   += numMilliseconds - currTime;
 944        AssertFatal(mNextParticleTime > 0, "Error, should not have deferred this particle!");
 945        break;
 946     }
 947
 948     currTime       += nextTime;
 949     mInternalClock += nextTime;
 950
 951     // Create particle at the correct position
 952     Point3F pos;
 953     pos.interpolate(start, end, F32(currTime) / F32(numMilliseconds));
 954
 955     U32 advanceMS = numMilliseconds - currTime;
 956     if (mDataBlock->overrideAdvance == false && advanceMS != 0)
 957     {
 958        for (S32 p = 0; p < mDataBlock->parts_per_eject; p++)
 959        {
 960           sub_addParticle(pos, velocity, numMilliseconds-currTime, p);
 961           particlesAdded = true;
 962
 963           Particle* last_part = part_list_head.next;
 964           if (last_part)
 965           {
 966             if (advanceMS > last_part->totalLifetime) 
 967             {
 968               part_list_head.next = last_part->next;
 969               n_parts--;
 970               last_part->next = part_freelist;
 971               part_freelist = last_part;
 972             } 
 973             else 
 974             {
 975               F32 t = F32(advanceMS) / 1000.0;
 976
 977               Point3F a = last_part->acc;
 978               a -= last_part->vel*last_part->dataBlock->dragCoefficient;
 979               a -= mWindVelocity*last_part->dataBlock->windCoefficient;
 980               //a += Point3F(0, 0, -9.81) * last_part->dataBlock->gravityCoefficient;
 981               a.z += -9.81f*last_part->dataBlock->gravityCoefficient; // as long as gravity is a constant, this is faster
 982
 983               last_part->vel += a * t;
 984               last_part->pos_local += last_part->vel * t;
 985
 986               // allow subclasses to adjust the particle params here
 987               sub_particleUpdate(last_part);
 988
 989               if (last_part->dataBlock->constrain_pos)
 990                 last_part->pos = last_part->pos_local + this->pos_pe;
 991               else
 992                 last_part->pos = last_part->pos_local;
 993
 994               updateKeyData(last_part);
 995             }
 996           }
 997        }
 998     }
 999     else
1000     {
1001        for (S32 p = 0; p < mDataBlock->parts_per_eject; p++)
1002        {
1003           sub_addParticle(pos, velocity, numMilliseconds-currTime, p);
1004           particlesAdded = true;
1005        }
1006     }
1007  }
1008
1009  if( particlesAdded == true )
1010     updateBBox();
1011
1012  if( n_parts > 0 && mSceneManager == NULL )
1013  {
1014     gClientSceneGraph->addObjectToScene(this);
1015     ClientProcessList::get()->addObject(this);
1016  }
1017
1018  mLastPosition = end;
1019  mHasLastPosition = true;
1020}
1021
1022Particle* afxParticleEmitter::alloc_particle()
1023{
1024  n_parts++;
1025
1026  // this should happen rarely
1027  if (n_parts > n_part_capacity)
1028  {
1029    Particle* store_block = new Particle[16];
1030    part_store.push_back(store_block);
1031    n_part_capacity += 16;
1032    for (S32 i = 0; i < 16; i++)
1033    {
1034      store_block[i].next = part_freelist;
1035      part_freelist = &store_block[i];
1036    }
1037    mDataBlock->allocPrimBuffer(n_part_capacity);
1038  }
1039
1040  Particle* pNew = part_freelist;
1041  part_freelist = pNew->next;
1042  pNew->next = part_list_head.next;
1043  part_list_head.next = pNew;
1044
1045  return pNew;
1046}
1047
1048ParticleData* afxParticleEmitter::pick_particle_type()
1049{
1050  U32 dBlockIndex = (U32)(mCeil(gRandGen.randF() * F32(mDataBlock->particleDataBlocks.size())) - 1);
1051  return mDataBlock->particleDataBlocks[dBlockIndex];
1052}
1053
1054bool afxParticleEmitter::onNewDataBlock(GameBaseData* dptr, bool reload)
1055{
1056  mDataBlock = dynamic_cast<afxParticleEmitterData*>(dptr);
1057  if( !mDataBlock || !Parent::onNewDataBlock(dptr, reload) )
1058    return false;
1059
1060  if (mDataBlock->isTempClone())
1061    return true;
1062
1063  scriptOnNewDataBlock();
1064  return true;
1065}
1066
1067void afxParticleEmitter::emitParticlesExt(const MatrixF& xfm, const Point3F& point, const Point3F& velocity, const U32 numMilliseconds)
1068{
1069   if (mDataBlock->use_emitter_xfm)
1070   {
1071      Point3F zero_point(0.0f, 0.0f, 0.0f);
1072      pos_pe = zero_point;
1073      setTransform(xfm);
1074
1075      preCompute(xfm);
1076      afx_emitParticles(zero_point, true, velocity, numMilliseconds);
1077   }
1078   else
1079   {
1080      pos_pe = point;
1081      preCompute(xfm);
1082      afx_emitParticles(point, true, velocity, numMilliseconds);
1083   }
1084}  
1085
1086//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
1087// VECTOR
1088
1089afxParticleEmitterVector::afxParticleEmitterVector()
1090{
1091   mDataBlock = NULL;
1092}
1093
1094afxParticleEmitterVector::~afxParticleEmitterVector()
1095{
1096}
1097
1098bool afxParticleEmitterVector::onNewDataBlock(GameBaseData* dptr, bool reload)
1099{
1100  mDataBlock = dynamic_cast<afxParticleEmitterVectorData*>(dptr);
1101  if( !mDataBlock || !Parent::onNewDataBlock(dptr, reload) )
1102    return false;
1103
1104  if (mDataBlock->isTempClone())
1105    return true;
1106
1107  scriptOnNewDataBlock();
1108  return true;
1109}
1110
1111void afxParticleEmitterVector::sub_addParticle(const Point3F& pos, const Point3F& vel, const U32 age_offset, S32 part_idx)
1112{
1113  Particle* pNew = alloc_particle();
1114  ParticleData* part_db = pick_particle_type();
1115
1116  Point3F pos_start = pos;
1117  if (part_db->constrain_pos)
1118    pos_start.set(0,0,0);
1119
1120  F32 initialVel = mDataBlock->ejectionVelocity;
1121  initialVel    += (mDataBlock->velocityVariance * 2.0f * gRandGen.randF()) - mDataBlock->velocityVariance;
1122  if(mDataBlock->fade_velocity)
1123    initialVel *= fade_amt;
1124
1125  F32 ejection_offset = mDataBlock->ejectionOffset;
1126  if(mDataBlock->fade_offset)
1127    ejection_offset *= fade_amt;
1128
1129  pNew->pos = pos_start + (pe_vector_norm * ejection_offset);
1130  pNew->pos_local = pNew->pos;
1131
1132  pNew->vel = pe_vector_norm * initialVel;
1133  if (mDataBlock->orientParticles)
1134    pNew->orientDir = pe_vector_norm;
1135  else
1136    // note -- for non-oriented particles, we use orientDir.x to store the billboard start angle.
1137    pNew->orientDir.x = mDegToRad(part_db->start_angle + part_db->angle_variance*2.0f*gRandGen.randF() - part_db->angle_variance);
1138  pNew->acc.set(0, 0, 0);
1139  pNew->currentAge = age_offset;
1140  pNew->t_last = 0.0f;
1141
1142  pNew->radial_v.set(0.0f, 0.0f, 0.0f);
1143
1144  part_db->initializeParticle(pNew, vel);
1145  updateKeyData( pNew );
1146}
1147
1148void afxParticleEmitterVector::sub_preCompute(const MatrixF& mat)
1149{
1150}
1151
1152//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
1153// CONE
1154
1155afxParticleEmitterCone::afxParticleEmitterCone()
1156{
1157  mDataBlock = NULL;
1158  cone_v.set(0,0,1);
1159  cone_s0.set(0,0,1);
1160  cone_s1.set(0,0,1);
1161}
1162
1163afxParticleEmitterCone::~afxParticleEmitterCone()
1164{
1165}
1166
1167bool afxParticleEmitterCone::onNewDataBlock(GameBaseData* dptr, bool reload)
1168{
1169  mDataBlock = dynamic_cast<afxParticleEmitterConeData*>(dptr);
1170  if( !mDataBlock || !Parent::onNewDataBlock(dptr, reload) )
1171    return false;
1172
1173  if (mDataBlock->isTempClone())
1174    return true;
1175
1176  scriptOnNewDataBlock();
1177  return true;
1178}
1179
1180void afxParticleEmitterCone::sub_addParticle(const Point3F& pos, const Point3F& vel, const U32 age_offset, S32 part_idx)
1181{
1182  Particle* pNew = alloc_particle();
1183  ParticleData* part_db = pick_particle_type();
1184
1185  Point3F pos_start = pos;
1186  if (part_db->constrain_pos)
1187    pos_start.set(0,0,0);
1188
1189  F32 initialVel = mDataBlock->ejectionVelocity;
1190  initialVel    += (mDataBlock->velocityVariance * 2.0f * gRandGen.randF()) - mDataBlock->velocityVariance;
1191  if(mDataBlock->fade_velocity)
1192    initialVel *= fade_amt;
1193
1194  // Randomly choose a vector between cone_s0 and cone_s1 and normalize:
1195  Point3F vec;
1196  F32 t = mRandF(0.0f, 1.0f);   
1197  vec.interpolate(cone_s0, cone_s1, t);
1198  vec.normalize();
1199
1200  // Randomly rotate about cone_v
1201  F32 theta = mRandF(0.0f, M_2PI_F);
1202  AngAxisF thetaRot(cone_v, theta);
1203  MatrixF temp(true);
1204  thetaRot.setMatrix(&temp);
1205  temp.mulP(vec);   
1206
1207  F32 ejection_offset = mDataBlock->ejectionOffset;
1208  if(mDataBlock->fade_offset)
1209    ejection_offset *= fade_amt;
1210
1211  pNew->pos = pos_start + (vec * ejection_offset);   
1212  pNew->pos_local = pNew->pos;
1213
1214  pNew->vel = mDataBlock->ejectionInvert ? vec * -initialVel : vec * initialVel;
1215  if (mDataBlock->orientParticles)
1216    pNew->orientDir = vec;
1217  else
1218    // note -- for non-oriented particles, we use orientDir.x to store the billboard start angle.
1219    pNew->orientDir.x = mDegToRad(part_db->start_angle + part_db->angle_variance*2.0f*gRandGen.randF() - part_db->angle_variance);
1220  pNew->acc.set(0, 0, 0);
1221  pNew->currentAge = age_offset;
1222  pNew->t_last = 0.0f;
1223
1224  pNew->radial_v.set(0.0f, 0.0f, 0.0f);
1225
1226  part_db->initializeParticle(pNew, vel);
1227  updateKeyData( pNew );
1228}
1229
1230void afxParticleEmitterCone::sub_preCompute(const MatrixF& mat)
1231{
1232  // Find vectors on the XZ plane corresponding to the inner and outer spread angles:
1233  //    (tan is infinite at PI/4 or 90 degrees)
1234  cone_v.set( 0.0f, 0.0f, 1.0f );
1235
1236  cone_s0.x = mTan( mDegToRad( ((afxParticleEmitterConeData*)mDataBlock)->spread_min / 2.0f ));
1237  cone_s0.y = 0.0f;
1238  cone_s0.z = 1.0f;
1239
1240  cone_s1.x = mTan( mDegToRad(((afxParticleEmitterConeData*)mDataBlock)->spread_max / 2.0f ));
1241  cone_s1.y = 0.0f;
1242  cone_s1.z = 1.0f;         
1243
1244  Point3F axis;
1245  F32 theta = mAcos( mDot(cone_v, pe_vector_norm) );
1246
1247  if( M_PI_F-theta < POINT_EPSILON )
1248  {
1249    cone_v.neg();
1250    cone_s0.neg();
1251    cone_s1.neg();
1252  }
1253  else if( theta > POINT_EPSILON )
1254  {
1255    mCross(pe_vector_norm, cone_v, &axis);
1256    axis.normalize();
1257
1258    AngAxisF thetaRot(axis, theta);
1259    MatrixF temp(true);
1260    thetaRot.setMatrix(&temp);
1261
1262    temp.mulP(cone_v);            
1263    temp.mulP(cone_s0);
1264    temp.mulP(cone_s1);                      
1265  }
1266}
1267
1268//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
1269// PATH
1270
1271afxParticleEmitterPath::afxParticleEmitterPath()
1272{
1273  mDataBlock = NULL;
1274  epaths.clear();
1275  epath_mults.clear();
1276  n_epath_points = 0;
1277  epath_points = NULL;
1278}
1279
1280afxParticleEmitterPath::~afxParticleEmitterPath()
1281{
1282}
1283
1284bool afxParticleEmitterPath::onNewDataBlock(GameBaseData* dptr, bool reload)
1285{
1286  mDataBlock = dynamic_cast<afxParticleEmitterPathData*>(dptr);
1287  if( !mDataBlock || !Parent::onNewDataBlock(dptr, reload) )
1288    return false;
1289
1290  if (mDataBlock->isTempClone())
1291    return true;
1292
1293  scriptOnNewDataBlock();
1294  return true;
1295}
1296
1297bool afxParticleEmitterPath::onAdd()
1298{
1299  if( !Parent::onAdd() )
1300    return false;
1301
1302  if (dynamic_cast<afxParticleEmitterPathData*>(mDataBlock))
1303    init_paths();
1304
1305  return true;
1306}
1307
1308void afxParticleEmitterPath::onRemove()
1309{
1310  if (dynamic_cast<afxParticleEmitterPathData*>(mDataBlock))
1311    cleanup_paths();
1312
1313  Parent::onRemove();
1314}
1315
1316void afxParticleEmitterPath::init_paths()
1317{
1318  if (!mDataBlock || ((afxParticleEmitterPathData*)mDataBlock)->epathDataBlocks.size() < 1)
1319  {
1320    n_epath_points = 0;
1321    epath_points = NULL;
1322    return;
1323  }
1324
1325  n_epath_points = ((afxParticleEmitterPathData*)mDataBlock)->epathDataBlocks.size();
1326  epath_points = new Point3F*[n_epath_points];
1327  dMemset(epath_points, 0, n_epath_points*sizeof(Point3F*));
1328  
1329  for (U32 i=0; i < n_epath_points; i++)
1330  {
1331    afxPathData* pd = ((afxParticleEmitterPathData*)mDataBlock)->epathDataBlocks[i];
1332    if (!pd)
1333      continue;
1334
1335    if (pd->getSubstitutionCount() > 0 && afx_owner)
1336    {
1337      afxPathData* orig_db = pd;
1338      pd = new afxPathData(*orig_db, true);
1339      orig_db->performSubstitutions(pd, afx_owner);
1340    }
1341   
1342    if (pd->num_points > 0)
1343    {
1344      afxPath3D* path = new afxPath3D();
1345      if (pd->times)
1346        path->buildPath( pd->num_points, pd->points, pd->times, 0.0f, 1.0f );
1347      else
1348        path->buildPath( pd->num_points, pd->points, 0.0f, 1.0f );  
1349      epaths.push_back(path);  
1350
1351      epath_mults.push_back( pd->mult );
1352
1353      epath_points[i] = new Point3F[pd->num_points];
1354      for (U32 j=0; j<pd->num_points; j++)
1355        epath_points[i][j] = pd->points[j];
1356    }
1357    else
1358    {
1359      Con::warnf("afxParticleEmitterPath::init_paths() -- paths datablock (%d) has no points.", i);
1360    }
1361
1362    if (pd->isTempClone())
1363      delete pd;
1364  }
1365}
1366
1367void afxParticleEmitterPath::cleanup_paths()
1368{
1369  if (n_epath_points < 1)
1370    return;
1371
1372  for (U32 i=0; i < epaths.size(); i++)
1373  {
1374    if (epaths[i])
1375      delete epaths[i];
1376  }
1377  epaths.clear();
1378  
1379  if (epath_points)
1380  {
1381    if (mDataBlock)
1382    {
1383      for (U32 i=0; i < n_epath_points; i++)
1384      {
1385        if (epath_points[i])
1386          delete [] epath_points[i];
1387      }
1388    }
1389    
1390    delete [] epath_points;
1391    epath_points = 0;
1392  }
1393}
1394
1395void afxParticleEmitterPath::sub_addParticle(const Point3F& pos, const Point3F& vel, const U32 age_offset, S32 part_idx)
1396{
1397   if (part_idx >= epaths.size() || !epaths[part_idx])
1398     return;
1399
1400   Particle* pNew = alloc_particle();
1401   ParticleData* part_db = pick_particle_type();
1402
1403   Point3F pos_start = pos;
1404   if (part_db->constrain_pos)
1405      pos_start.set(0,0,0);
1406
1407   F32 initialVel = mDataBlock->ejectionVelocity;
1408   initialVel    += (mDataBlock->velocityVariance * 2.0f * gRandGen.randF()) - mDataBlock->velocityVariance;
1409   if(mDataBlock->fade_velocity)
1410      initialVel *= fade_amt;
1411
1412   // Randomly choose a curve parameter between [0.0,1.0] and evaluate the curve there
1413   F32 param = mRandF(0.0f, 1.0f);
1414
1415   Point3F curve_pos = epaths[part_idx]->evaluateAtTime(param);
1416
1417   Point3F vec;
1418   switch (((afxParticleEmitterPathData*)mDataBlock)->path_origin_type)
1419   { 
1420   case afxParticleEmitterPathData::PATHEMIT_ORIGIN :
1421      vec = curve_pos;
1422      vec.normalize();
1423      break;
1424   case afxParticleEmitterPathData::PATHEMIT_POINT :
1425      vec = curve_pos-pe_vector;
1426      vec.normalize();
1427      break;
1428   case afxParticleEmitterPathData::PATHEMIT_VECTOR :
1429      vec = pe_vector_norm;
1430      break;
1431   case afxParticleEmitterPathData::PATHEMIT_TANGENT :
1432      vec = epaths[part_idx]->evaluateTangentAtTime(param);
1433      vec.normalize();
1434      break;
1435   }
1436
1437   F32 ejection_offset = mDataBlock->ejectionOffset;
1438   if(mDataBlock->fade_offset)
1439      ejection_offset *= fade_amt;
1440
1441   pNew->pos = pos_start + curve_pos + (vec * ejection_offset);
1442   pNew->pos_local = pNew->pos;
1443
1444   pNew->vel = mDataBlock->ejectionInvert ? vec * -initialVel : vec * initialVel;
1445   if (mDataBlock->orientParticles)
1446     pNew->orientDir = vec;
1447   else
1448     // note -- for non-oriented particles, we use orientDir.x to store the billboard start angle.
1449     pNew->orientDir.x = mDegToRad(part_db->start_angle + part_db->angle_variance*2.0f*gRandGen.randF() - part_db->angle_variance);
1450   pNew->acc.set(0, 0, 0);
1451   pNew->currentAge = age_offset;
1452   pNew->t_last = 0.0f;
1453
1454   pNew->radial_v.set(0.0f, 0.0f, 0.0f);
1455
1456   part_db->initializeParticle(pNew, vel);
1457   updateKeyData( pNew );
1458}
1459
1460void afxParticleEmitterPath::sub_preCompute(const MatrixF& mat)
1461{
1462  for( U32 i=0; i < epaths.size(); i++ )
1463  {
1464    for( U32 j=0; j < epaths[i]->getNumPoints(); j++ )
1465    {
1466      Point3F p = epath_points[i][j];
1467      mat.mulV(p);
1468
1469      p *= epath_mults[i];
1470      if(mDataBlock->ground_conform) {
1471        groundConformPoint(p, mat);         
1472      }
1473
1474      epaths[i]->setPointPosition(j, p);
1475    }
1476
1477    epaths[i]->reBuildPath();
1478  } 
1479}
1480
1481void afxParticleEmitterPath::groundConformPoint(Point3F& point, const MatrixF& mat)
1482{
1483  point += mat.getPosition();
1484
1485  RayInfo rInfo;
1486  bool hit = false;
1487
1488  if (mDataBlock->ground_conform_interiors)
1489  {
1490    U32 mask = InteriorLikeObjectType;
1491    if (mDataBlock->ground_conform_terrain)
1492    {
1493      mask |= TerrainObjectType | TerrainLikeObjectType;
1494    }
1495    
1496    Point3F above_pos(point); above_pos.z += 0.1f;
1497    Point3F below_pos(point); below_pos.z -= 10000;
1498    hit = gClientContainer.castRay(above_pos, below_pos, mask, &rInfo);
1499    if (!hit)
1500    {
1501      above_pos.z = point.z + 10000;
1502      below_pos.z = point.z - 0.1f;
1503      hit = gClientContainer.castRay(below_pos, above_pos, mask, &rInfo);
1504    }
1505  }
1506  else if (mDataBlock->ground_conform_terrain)
1507  {
1508    U32 mask = TerrainObjectType | TerrainLikeObjectType;
1509    Point3F above_pos(point); above_pos.z += 10000;
1510    Point3F below_pos(point); below_pos.z -= 10000;
1511    hit = gClientContainer.castRay(above_pos, below_pos, mask, &rInfo);
1512  }
1513
1514  if (hit)
1515  {
1516    F32 terrain_z = rInfo.point.z;
1517    F32 new_z = terrain_z + mDataBlock->ground_conform_height;
1518    point.z = new_z;    
1519  }
1520
1521  point -= mat.getPosition();
1522}
1523
1524//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
1525// DISC
1526
1527afxParticleEmitterDisc::afxParticleEmitterDisc()
1528{
1529  mDataBlock = NULL;
1530  disc_v.set(0,0,1);
1531  disc_r.set(1,0,0);
1532}
1533
1534afxParticleEmitterDisc::~afxParticleEmitterDisc()
1535{
1536}
1537
1538bool afxParticleEmitterDisc::onNewDataBlock(GameBaseData* dptr, bool reload)
1539{
1540  mDataBlock = dynamic_cast<afxParticleEmitterDiscData*>(dptr);
1541  if( !mDataBlock || !Parent::onNewDataBlock(dptr, reload) )
1542    return false;
1543
1544  if (mDataBlock->isTempClone())
1545    return true;
1546
1547  return true;
1548}
1549
1550void afxParticleEmitterDisc::sub_addParticle(const Point3F& pos, const Point3F& vel, const U32 age_offset, S32 part_idx)
1551{
1552  Particle* pNew = alloc_particle();
1553  ParticleData* part_db = pick_particle_type();
1554
1555  Point3F pos_start = pos;
1556  if (part_db->constrain_pos)
1557    pos_start.set(0,0,0);
1558
1559  F32 initialVel = mDataBlock->ejectionVelocity;
1560  initialVel    += (mDataBlock->velocityVariance * 2.0f * gRandGen.randF()) - mDataBlock->velocityVariance;
1561  if(mDataBlock->fade_velocity)
1562    initialVel *= fade_amt;
1563
1564  // Randomly choose a radius vector
1565  Point3F r( disc_r );
1566  F32 radius = mRandF(((afxParticleEmitterDiscData*)mDataBlock)->pe_radius_min, ((afxParticleEmitterDiscData*)mDataBlock)->pe_radius_max);   
1567  r *= radius;
1568
1569  // Randomly rotate r about disc_v
1570  F32 theta = mRandF(0.0f, M_2PI_F);
1571  AngAxisF thetaRot(disc_v, theta);
1572  MatrixF temp(true);
1573  thetaRot.setMatrix(&temp);
1574  temp.mulP(r);   
1575
1576  F32 ejection_offset = mDataBlock->ejectionOffset;
1577  if(mDataBlock->fade_offset)
1578    ejection_offset *= fade_amt;
1579
1580  pNew->pos = pos_start + r + (disc_v * ejection_offset);
1581  pNew->pos_local = pNew->pos;
1582
1583  pNew->vel = (mDataBlock->ejectionInvert) ? (disc_v * -initialVel) : (disc_v * initialVel);
1584  if (mDataBlock->orientParticles)
1585    pNew->orientDir = disc_v;
1586  else
1587    // note -- for non-oriented particles, we use orientDir.x to store the billboard start angle.
1588    pNew->orientDir.x = mDegToRad(part_db->start_angle + part_db->angle_variance*2.0f*gRandGen.randF() - part_db->angle_variance);
1589  pNew->acc.set(0, 0, 0);
1590  pNew->currentAge = age_offset;
1591  pNew->t_last = 0.0f;
1592
1593  pNew->radial_v = r;
1594
1595  part_db->initializeParticle(pNew, vel);
1596  updateKeyData( pNew );
1597}
1598
1599void afxParticleEmitterDisc::sub_preCompute(const MatrixF& mat)
1600{
1601  disc_v.set(0.0f, 0.0f, 1.0f);
1602  disc_r.set(1.0f, 0.0f, 0.0f);
1603
1604  Point3F axis;
1605  F32 theta = mAcos( mDot(disc_v, pe_vector_norm) );
1606
1607  if( M_PI_F-theta < POINT_EPSILON )
1608  {
1609    disc_v.neg();
1610  }
1611  else if( theta > POINT_EPSILON )
1612  {
1613    mCross(pe_vector_norm, disc_v, &axis);
1614    axis.normalize();
1615
1616    AngAxisF thetaRot(axis, theta);
1617    MatrixF temp(true);
1618    thetaRot.setMatrix(&temp);
1619
1620    temp.mulP(disc_v);            
1621    temp.mulP(disc_r);            
1622  }         
1623}
1624
1625//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
1626
1627