afxParticleEmitter.cpp
Engine/source/afx/ce/afxParticleEmitter.cpp
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