|
@@ -24,6 +24,7 @@
|
|
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
|
|
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
|
|
// Copyright (C) 2015 Faust Logic, Inc.
|
|
// Copyright (C) 2015 Faust Logic, Inc.
|
|
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
|
|
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
|
|
|
|
+
|
|
#include "platform/platform.h"
|
|
#include "platform/platform.h"
|
|
#include "T3D/fx/particleEmitter.h"
|
|
#include "T3D/fx/particleEmitter.h"
|
|
|
|
|
|
@@ -42,6 +43,10 @@
|
|
#include "lighting/lightInfo.h"
|
|
#include "lighting/lightInfo.h"
|
|
#include "console/engineAPI.h"
|
|
#include "console/engineAPI.h"
|
|
|
|
|
|
|
|
+#if defined(AFX_CAP_PARTICLE_POOLS)
|
|
|
|
+#include "afx/util/afxParticlePool.h"
|
|
|
|
+#endif
|
|
|
|
+
|
|
Point3F ParticleEmitter::mWindVelocity( 0.0, 0.0, 0.0 );
|
|
Point3F ParticleEmitter::mWindVelocity( 0.0, 0.0, 0.0 );
|
|
const F32 ParticleEmitter::AgedSpinToRadians = (1.0f/1000.0f) * (1.0f/360.0f) * M_PI_F * 2.0f;
|
|
const F32 ParticleEmitter::AgedSpinToRadians = (1.0f/1000.0f) * (1.0f/360.0f) * M_PI_F * 2.0f;
|
|
|
|
|
|
@@ -153,6 +158,21 @@ ParticleEmitterData::ParticleEmitterData()
|
|
|
|
|
|
alignParticles = false;
|
|
alignParticles = false;
|
|
alignDirection = Point3F(0.0f, 1.0f, 0.0f);
|
|
alignDirection = Point3F(0.0f, 1.0f, 0.0f);
|
|
|
|
+
|
|
|
|
+ ejectionInvert = false;
|
|
|
|
+ fade_color = false;
|
|
|
|
+ fade_alpha = false;
|
|
|
|
+ fade_size = false;
|
|
|
|
+ parts_per_eject = 1;
|
|
|
|
+ use_emitter_xfm = false;
|
|
|
|
+
|
|
|
|
+#if defined(AFX_CAP_PARTICLE_POOLS)
|
|
|
|
+ pool_datablock = 0;
|
|
|
|
+ pool_index = 0;
|
|
|
|
+ pool_depth_fade = false;
|
|
|
|
+ pool_radial_fade = false;
|
|
|
|
+ do_pool_id_convert = false;
|
|
|
|
+#endif
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -297,6 +317,23 @@ void ParticleEmitterData::initPersistFields()
|
|
|
|
|
|
endGroup( "ParticleEmitterData" );
|
|
endGroup( "ParticleEmitterData" );
|
|
|
|
|
|
|
|
+ addGroup("AFX");
|
|
|
|
+ addField("ejectionInvert", TypeBool, Offset(ejectionInvert, ParticleEmitterData));
|
|
|
|
+ addField("fadeColor", TypeBool, Offset(fade_color, ParticleEmitterData));
|
|
|
|
+ addField("fadeAlpha", TypeBool, Offset(fade_alpha, ParticleEmitterData));
|
|
|
|
+ addField("fadeSize", TypeBool, Offset(fade_size, ParticleEmitterData));
|
|
|
|
+ // useEmitterTransform currently does not work in TGEA or T3D
|
|
|
|
+ addField("useEmitterTransform", TypeBool, Offset(use_emitter_xfm, ParticleEmitterData));
|
|
|
|
+ endGroup("AFX");
|
|
|
|
+
|
|
|
|
+#if defined(AFX_CAP_PARTICLE_POOLS)
|
|
|
|
+ addGroup("AFX Pooled Particles");
|
|
|
|
+ addField("poolData", TYPEID<afxParticlePoolData>(), Offset(pool_datablock, ParticleEmitterData));
|
|
|
|
+ addField("poolIndex", TypeS32, Offset(pool_index, ParticleEmitterData));
|
|
|
|
+ addField("poolDepthFade", TypeBool, Offset(pool_depth_fade, ParticleEmitterData));
|
|
|
|
+ addField("poolRadialFade", TypeBool, Offset(pool_radial_fade, ParticleEmitterData));
|
|
|
|
+ endGroup("AFX Pooled Particles");
|
|
|
|
+#endif
|
|
// disallow some field substitutions
|
|
// disallow some field substitutions
|
|
disableFieldSubstitutions("particles");
|
|
disableFieldSubstitutions("particles");
|
|
onlyKeepClearSubstitutions("poolData"); // subs resolving to "~~", or "~0" are OK
|
|
onlyKeepClearSubstitutions("poolData"); // subs resolving to "~~", or "~0" are OK
|
|
@@ -365,6 +402,22 @@ void ParticleEmitterData::packData(BitStream* stream)
|
|
stream->writeFlag(renderReflection);
|
|
stream->writeFlag(renderReflection);
|
|
stream->writeFlag(glow);
|
|
stream->writeFlag(glow);
|
|
stream->writeInt( blendStyle, 4 );
|
|
stream->writeInt( blendStyle, 4 );
|
|
|
|
+
|
|
|
|
+ stream->writeFlag(ejectionInvert);
|
|
|
|
+ stream->writeFlag(fade_color);
|
|
|
|
+ stream->writeFlag(fade_alpha);
|
|
|
|
+ stream->writeFlag(fade_size);
|
|
|
|
+ stream->writeFlag(use_emitter_xfm);
|
|
|
|
+
|
|
|
|
+#if defined(AFX_CAP_PARTICLE_POOLS)
|
|
|
|
+ if (stream->writeFlag(pool_datablock))
|
|
|
|
+ {
|
|
|
|
+ stream->writeRangedU32(packed ? SimObjectId((uintptr_t)pool_datablock) : pool_datablock->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast);
|
|
|
|
+ stream->write(pool_index);
|
|
|
|
+ stream->writeFlag(pool_depth_fade);
|
|
|
|
+ stream->writeFlag(pool_radial_fade);
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
@@ -428,6 +481,22 @@ void ParticleEmitterData::unpackData(BitStream* stream)
|
|
renderReflection = stream->readFlag();
|
|
renderReflection = stream->readFlag();
|
|
glow = stream->readFlag();
|
|
glow = stream->readFlag();
|
|
blendStyle = stream->readInt( 4 );
|
|
blendStyle = stream->readInt( 4 );
|
|
|
|
+ ejectionInvert = stream->readFlag();
|
|
|
|
+ fade_color = stream->readFlag();
|
|
|
|
+ fade_alpha = stream->readFlag();
|
|
|
|
+ fade_size = stream->readFlag();
|
|
|
|
+ use_emitter_xfm = stream->readFlag();
|
|
|
|
+
|
|
|
|
+#if defined(AFX_CAP_PARTICLE_POOLS)
|
|
|
|
+ if (stream->readFlag())
|
|
|
|
+ {
|
|
|
|
+ pool_datablock = (afxParticlePoolData*)stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
|
|
|
|
+ stream->read(&pool_index);
|
|
|
|
+ pool_depth_fade = stream->readFlag();
|
|
|
|
+ pool_radial_fade = stream->readFlag();
|
|
|
|
+ do_pool_id_convert = true;
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
@@ -607,6 +676,22 @@ bool ParticleEmitterData::preload(bool server, String &errorStr)
|
|
|
|
|
|
if (!server)
|
|
if (!server)
|
|
{
|
|
{
|
|
|
|
+#if defined(AFX_CAP_PARTICLE_POOLS)
|
|
|
|
+ if (do_pool_id_convert)
|
|
|
|
+ {
|
|
|
|
+ SimObjectId db_id = (SimObjectId)(uintptr_t)pool_datablock;
|
|
|
|
+ if (db_id != 0)
|
|
|
|
+ {
|
|
|
|
+ // try to convert id to pointer
|
|
|
|
+ if (!Sim::findObject(db_id, pool_datablock))
|
|
|
|
+ {
|
|
|
|
+ Con::errorf("ParticleEmitterData::reload() -- bad datablockId: 0x%x (poolData)", db_id);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ do_pool_id_convert = false;
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+
|
|
// load emitter texture if specified
|
|
// load emitter texture if specified
|
|
if (textureName && textureName[0])
|
|
if (textureName && textureName[0])
|
|
{
|
|
{
|
|
@@ -676,6 +761,8 @@ void ParticleEmitterData::allocPrimBuffer( S32 overrideSize )
|
|
|
|
|
|
partListInitSize = maxPartLife / (ejectionPeriodMS - periodVarianceMS);
|
|
partListInitSize = maxPartLife / (ejectionPeriodMS - periodVarianceMS);
|
|
partListInitSize += 8; // add 8 as "fudge factor" to make sure it doesn't realloc if it goes over by 1
|
|
partListInitSize += 8; // add 8 as "fudge factor" to make sure it doesn't realloc if it goes over by 1
|
|
|
|
+ if (parts_per_eject > 1)
|
|
|
|
+ partListInitSize *= parts_per_eject;
|
|
|
|
|
|
// if override size is specified, then the emitter overran its buffer and needs a larger allocation
|
|
// if override size is specified, then the emitter overran its buffer and needs a larger allocation
|
|
if( overrideSize != -1 )
|
|
if( overrideSize != -1 )
|
|
@@ -871,7 +958,16 @@ ParticleEmitter::ParticleEmitter()
|
|
|
|
|
|
// ParticleEmitter should be allocated on the client only.
|
|
// ParticleEmitter should be allocated on the client only.
|
|
mNetFlags.set( IsGhost );
|
|
mNetFlags.set( IsGhost );
|
|
|
|
+ fade_amt = 1.0f;
|
|
|
|
+ forced_bbox = false;
|
|
|
|
+ db_temp_clone = false;
|
|
|
|
+ pos_pe.set(0,0,0);
|
|
|
|
+ sort_priority = 0;
|
|
mDataBlock = 0;
|
|
mDataBlock = 0;
|
|
|
|
+
|
|
|
|
+#if defined(AFX_CAP_PARTICLE_POOLS)
|
|
|
|
+ pool = 0;
|
|
|
|
+#endif
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
@@ -920,6 +1016,11 @@ bool ParticleEmitter::onAdd()
|
|
mObjBox.maxExtents = Point3F(radius, radius, radius);
|
|
mObjBox.maxExtents = Point3F(radius, radius, radius);
|
|
resetWorldBox();
|
|
resetWorldBox();
|
|
|
|
|
|
|
|
+#if defined(AFX_CAP_PARTICLE_POOLS)
|
|
|
|
+ if (pool)
|
|
|
|
+ pool->addParticleEmitter(this);
|
|
|
|
+#endif
|
|
|
|
+
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -929,6 +1030,14 @@ bool ParticleEmitter::onAdd()
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
void ParticleEmitter::onRemove()
|
|
void ParticleEmitter::onRemove()
|
|
{
|
|
{
|
|
|
|
+#if defined(AFX_CAP_PARTICLE_POOLS)
|
|
|
|
+ if (pool)
|
|
|
|
+ {
|
|
|
|
+ pool->removeParticleEmitter(this);
|
|
|
|
+ pool = 0;
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+
|
|
removeFromScene();
|
|
removeFromScene();
|
|
Parent::onRemove();
|
|
Parent::onRemove();
|
|
}
|
|
}
|
|
@@ -1015,6 +1124,11 @@ LinearColorF ParticleEmitter::getCollectiveColor()
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
void ParticleEmitter::prepRenderImage(SceneRenderState* state)
|
|
void ParticleEmitter::prepRenderImage(SceneRenderState* state)
|
|
{
|
|
{
|
|
|
|
+#if defined(AFX_CAP_PARTICLE_POOLS)
|
|
|
|
+ if (pool)
|
|
|
|
+ return;
|
|
|
|
+#endif
|
|
|
|
+
|
|
if( state->isReflectPass() && !getDataBlock()->renderReflection )
|
|
if( state->isReflectPass() && !getDataBlock()->renderReflection )
|
|
return;
|
|
return;
|
|
|
|
|
|
@@ -1043,6 +1157,7 @@ void ParticleEmitter::prepRenderImage(SceneRenderState* state)
|
|
ri->translucentSort = true;
|
|
ri->translucentSort = true;
|
|
ri->type = RenderPassManager::RIT_Particle;
|
|
ri->type = RenderPassManager::RIT_Particle;
|
|
ri->sortDistSq = getRenderWorldBox().getSqDistanceToPoint( camPos );
|
|
ri->sortDistSq = getRenderWorldBox().getSqDistanceToPoint( camPos );
|
|
|
|
+ ri->defaultKey = (-sort_priority*100);
|
|
|
|
|
|
// Draw the system offscreen unless the highResOnly flag is set on the datablock
|
|
// Draw the system offscreen unless the highResOnly flag is set on the datablock
|
|
ri->systemState = ( getDataBlock()->highResOnly ? PSS_AwaitingHighResDraw : PSS_AwaitingOffscreenDraw );
|
|
ri->systemState = ( getDataBlock()->highResOnly ? PSS_AwaitingHighResDraw : PSS_AwaitingOffscreenDraw );
|
|
@@ -1146,6 +1261,7 @@ void ParticleEmitter::emitParticles(const Point3F& point,
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ pos_pe = point;
|
|
Point3F realStart;
|
|
Point3F realStart;
|
|
if( useLastPosition && mHasLastPosition )
|
|
if( useLastPosition && mHasLastPosition )
|
|
realStart = mLastPosition;
|
|
realStart = mLastPosition;
|
|
@@ -1213,7 +1329,8 @@ void ParticleEmitter::emitParticles(const Point3F& start,
|
|
// Create particle at the correct position
|
|
// Create particle at the correct position
|
|
Point3F pos;
|
|
Point3F pos;
|
|
pos.interpolate(start, end, F32(currTime) / F32(numMilliseconds));
|
|
pos.interpolate(start, end, F32(currTime) / F32(numMilliseconds));
|
|
- addParticle(pos, axis, velocity, axisx);
|
|
|
|
|
|
+ addParticle(pos, axis, velocity, axisx, numMilliseconds-currTime);
|
|
|
|
+
|
|
particlesAdded = true;
|
|
particlesAdded = true;
|
|
mNextParticleTime = 0;
|
|
mNextParticleTime = 0;
|
|
}
|
|
}
|
|
@@ -1243,7 +1360,7 @@ void ParticleEmitter::emitParticles(const Point3F& start,
|
|
// Create particle at the correct position
|
|
// Create particle at the correct position
|
|
Point3F pos;
|
|
Point3F pos;
|
|
pos.interpolate(start, end, F32(currTime) / F32(numMilliseconds));
|
|
pos.interpolate(start, end, F32(currTime) / F32(numMilliseconds));
|
|
- addParticle(pos, axis, velocity, axisx);
|
|
|
|
|
|
+ addParticle(pos, axis, velocity, axisx, numMilliseconds-currTime);
|
|
particlesAdded = true;
|
|
particlesAdded = true;
|
|
|
|
|
|
// This override-advance code is restored in order to correctly adjust
|
|
// This override-advance code is restored in order to correctly adjust
|
|
@@ -1268,17 +1385,27 @@ void ParticleEmitter::emitParticles(const Point3F& start,
|
|
{
|
|
{
|
|
if (advanceMS != 0)
|
|
if (advanceMS != 0)
|
|
{
|
|
{
|
|
- F32 t = F32(advanceMS) / 1000.0;
|
|
|
|
|
|
+ F32 t = F32(advanceMS) / 1000.0;
|
|
|
|
+
|
|
|
|
+ Point3F a = last_part->acc;
|
|
|
|
+ a -= last_part->vel * last_part->dataBlock->dragCoefficient;
|
|
|
|
+ a -= mWindVelocity * last_part->dataBlock->windCoefficient;
|
|
|
|
+ //a += Point3F(0.0f, 0.0f, -9.81f) * last_part->dataBlock->gravityCoefficient;
|
|
|
|
+ a.z += -9.81f*last_part->dataBlock->gravityCoefficient; // as long as gravity is a constant, this is faster
|
|
|
|
+
|
|
|
|
+ last_part->vel += a * t;
|
|
|
|
+ //last_part->pos += last_part->vel * t;
|
|
|
|
+ last_part->pos_local += last_part->vel * t;
|
|
|
|
|
|
- Point3F a = last_part->acc;
|
|
|
|
- a -= last_part->vel * last_part->dataBlock->dragCoefficient;
|
|
|
|
- a -= mWindVelocity * last_part->dataBlock->windCoefficient;
|
|
|
|
- a += Point3F(0.0f, 0.0f, -9.81f) * last_part->dataBlock->gravityCoefficient;
|
|
|
|
|
|
+ // AFX -- allow subclasses to adjust the particle params here
|
|
|
|
+ sub_particleUpdate(last_part);
|
|
|
|
|
|
- last_part->vel += a * t;
|
|
|
|
- last_part->pos += last_part->vel * t;
|
|
|
|
|
|
+ if (last_part->dataBlock->constrain_pos)
|
|
|
|
+ last_part->pos = last_part->pos_local + this->pos_pe;
|
|
|
|
+ else
|
|
|
|
+ last_part->pos = last_part->pos_local;
|
|
|
|
|
|
- updateKeyData( last_part );
|
|
|
|
|
|
+ updateKeyData( last_part );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -1350,7 +1477,7 @@ void ParticleEmitter::emitParticles(const Point3F& rCenter,
|
|
axis.normalize();
|
|
axis.normalize();
|
|
pos += rCenter;
|
|
pos += rCenter;
|
|
|
|
|
|
- addParticle(pos, axis, velocity, axisz);
|
|
|
|
|
|
+ addParticle(pos, axis, velocity, axisz, 0);
|
|
}
|
|
}
|
|
|
|
|
|
// Set world bounding box
|
|
// Set world bounding box
|
|
@@ -1373,6 +1500,8 @@ void ParticleEmitter::emitParticles(const Point3F& rCenter,
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
void ParticleEmitter::updateBBox()
|
|
void ParticleEmitter::updateBBox()
|
|
{
|
|
{
|
|
|
|
+ if (forced_bbox)
|
|
|
|
+ return;
|
|
Point3F minPt(1e10, 1e10, 1e10);
|
|
Point3F minPt(1e10, 1e10, 1e10);
|
|
Point3F maxPt(-1e10, -1e10, -1e10);
|
|
Point3F maxPt(-1e10, -1e10, -1e10);
|
|
|
|
|
|
@@ -1393,15 +1522,18 @@ void ParticleEmitter::updateBBox()
|
|
boxScale.y = getMax(boxScale.y, 1.0f);
|
|
boxScale.y = getMax(boxScale.y, 1.0f);
|
|
boxScale.z = getMax(boxScale.z, 1.0f);
|
|
boxScale.z = getMax(boxScale.z, 1.0f);
|
|
mBBObjToWorld.scale(boxScale);
|
|
mBBObjToWorld.scale(boxScale);
|
|
|
|
+
|
|
|
|
+#if defined(AFX_CAP_PARTICLE_POOLS)
|
|
|
|
+ if (pool)
|
|
|
|
+ pool->updatePoolBBox(this);
|
|
|
|
+#endif
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
// addParticle
|
|
// addParticle
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
-void ParticleEmitter::addParticle(const Point3F& pos,
|
|
|
|
- const Point3F& axis,
|
|
|
|
- const Point3F& vel,
|
|
|
|
- const Point3F& axisx)
|
|
|
|
|
|
+void ParticleEmitter::addParticle(const Point3F& pos, const Point3F& axis, const Point3F& vel,
|
|
|
|
+ const Point3F& axisx, const U32 age_offset)
|
|
{
|
|
{
|
|
n_parts++;
|
|
n_parts++;
|
|
if (n_parts > n_part_capacity || n_parts > mDataBlock->partListInitSize)
|
|
if (n_parts > n_part_capacity || n_parts > mDataBlock->partListInitSize)
|
|
@@ -1423,6 +1555,16 @@ void ParticleEmitter::addParticle(const Point3F& pos,
|
|
pNew->next = part_list_head.next;
|
|
pNew->next = part_list_head.next;
|
|
part_list_head.next = pNew;
|
|
part_list_head.next = pNew;
|
|
|
|
|
|
|
|
+ // for earlier access to constrain_pos, the ParticleData datablock is chosen here instead
|
|
|
|
+ // of later in the method.
|
|
|
|
+ U32 dBlockIndex = gRandGen.randI() % mDataBlock->particleDataBlocks.size();
|
|
|
|
+ ParticleData* part_db = mDataBlock->particleDataBlocks[dBlockIndex];
|
|
|
|
+ // set start position to world or local space
|
|
|
|
+ Point3F pos_start;
|
|
|
|
+ if (part_db->constrain_pos)
|
|
|
|
+ pos_start.set(0,0,0);
|
|
|
|
+ else
|
|
|
|
+ pos_start = pos;
|
|
Point3F ejectionAxis = axis;
|
|
Point3F ejectionAxis = axis;
|
|
F32 theta = (mDataBlock->thetaMax - mDataBlock->thetaMin) * gRandGen.randF() +
|
|
F32 theta = (mDataBlock->thetaMax - mDataBlock->thetaMin) * gRandGen.randF() +
|
|
mDataBlock->thetaMin;
|
|
mDataBlock->thetaMin;
|
|
@@ -1444,14 +1586,17 @@ void ParticleEmitter::addParticle(const Point3F& pos,
|
|
F32 initialVel = mDataBlock->ejectionVelocity;
|
|
F32 initialVel = mDataBlock->ejectionVelocity;
|
|
initialVel += (mDataBlock->velocityVariance * 2.0f * gRandGen.randF()) - mDataBlock->velocityVariance;
|
|
initialVel += (mDataBlock->velocityVariance * 2.0f * gRandGen.randF()) - mDataBlock->velocityVariance;
|
|
|
|
|
|
- pNew->pos = pos + (ejectionAxis * (mDataBlock->ejectionOffset + mDataBlock->ejectionOffsetVariance* gRandGen.randF()) );
|
|
|
|
- pNew->vel = ejectionAxis * initialVel;
|
|
|
|
- pNew->orientDir = ejectionAxis;
|
|
|
|
|
|
+ pNew->pos = pos_start + (ejectionAxis * (mDataBlock->ejectionOffset + mDataBlock->ejectionOffsetVariance* gRandGen.randF()) );
|
|
|
|
+ pNew->pos_local = pNew->pos;
|
|
|
|
+ pNew->vel = mDataBlock->ejectionInvert ? ejectionAxis * -initialVel : ejectionAxis * initialVel;
|
|
|
|
+ if (mDataBlock->orientParticles)
|
|
|
|
+ pNew->orientDir = ejectionAxis;
|
|
|
|
+ else
|
|
|
|
+ // note -- for non-oriented particles, we use orientDir.x to store the billboard start angle.
|
|
|
|
+ pNew->orientDir.x = mDegToRad(part_db->start_angle + part_db->angle_variance*2.0f*gRandGen.randF() - part_db->angle_variance);
|
|
pNew->acc.set(0, 0, 0);
|
|
pNew->acc.set(0, 0, 0);
|
|
- pNew->currentAge = 0;
|
|
|
|
-
|
|
|
|
- // Choose a new particle datablack randomly from the list
|
|
|
|
- U32 dBlockIndex = gRandGen.randI() % mDataBlock->particleDataBlocks.size();
|
|
|
|
|
|
+ pNew->currentAge = age_offset;
|
|
|
|
+ pNew->t_last = 0.0f;
|
|
mDataBlock->particleDataBlocks[dBlockIndex]->initializeParticle(pNew, vel);
|
|
mDataBlock->particleDataBlocks[dBlockIndex]->initializeParticle(pNew, vel);
|
|
updateKeyData( pNew );
|
|
updateKeyData( pNew );
|
|
|
|
|
|
@@ -1533,8 +1678,10 @@ void ParticleEmitter::updateKeyData( Particle *part )
|
|
if( part->totalLifetime < 1 )
|
|
if( part->totalLifetime < 1 )
|
|
part->totalLifetime = 1;
|
|
part->totalLifetime = 1;
|
|
|
|
|
|
- F32 t = F32(part->currentAge) / F32(part->totalLifetime);
|
|
|
|
- AssertFatal(t <= 1.0f, "Out out bounds filter function for particle.");
|
|
|
|
|
|
+ if (part->currentAge > part->totalLifetime)
|
|
|
|
+ part->currentAge = part->totalLifetime;
|
|
|
|
+ F32 t = (F32)part->currentAge / (F32)part->totalLifetime;
|
|
|
|
+
|
|
|
|
|
|
for( U32 i = 1; i < ParticleData::PDC_NUM_KEYS; i++ )
|
|
for( U32 i = 1; i < ParticleData::PDC_NUM_KEYS; i++ )
|
|
{
|
|
{
|
|
@@ -1566,7 +1713,25 @@ void ParticleEmitter::updateKeyData( Particle *part )
|
|
{
|
|
{
|
|
part->size = (part->dataBlock->sizes[i-1] * (1.0 - firstPart)) +
|
|
part->size = (part->dataBlock->sizes[i-1] * (1.0 - firstPart)) +
|
|
(part->dataBlock->sizes[i] * firstPart);
|
|
(part->dataBlock->sizes[i] * firstPart);
|
|
|
|
+ part->size *= part->dataBlock->sizeBias;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ if (mDataBlock->fade_color)
|
|
|
|
+ {
|
|
|
|
+ if (mDataBlock->fade_alpha)
|
|
|
|
+ part->color *= fade_amt;
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ part->color.red *= fade_amt;
|
|
|
|
+ part->color.green *= fade_amt;
|
|
|
|
+ part->color.blue *= fade_amt;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if (mDataBlock->fade_alpha)
|
|
|
|
+ part->color.alpha *= fade_amt;
|
|
|
|
+
|
|
|
|
+ if (mDataBlock->fade_size)
|
|
|
|
+ part->size *= fade_amt;
|
|
break;
|
|
break;
|
|
|
|
|
|
}
|
|
}
|
|
@@ -1578,19 +1743,25 @@ void ParticleEmitter::updateKeyData( Particle *part )
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
void ParticleEmitter::update( U32 ms )
|
|
void ParticleEmitter::update( U32 ms )
|
|
{
|
|
{
|
|
- // TODO: Prefetch
|
|
|
|
|
|
+ F32 t = F32(ms)/1000.0f; // AFX -- moved outside loop, no need to recalculate this for every particle
|
|
|
|
|
|
for (Particle* part = part_list_head.next; part != NULL; part = part->next)
|
|
for (Particle* part = part_list_head.next; part != NULL; part = part->next)
|
|
{
|
|
{
|
|
- F32 t = F32(ms) / 1000.0;
|
|
|
|
-
|
|
|
|
Point3F a = part->acc;
|
|
Point3F a = part->acc;
|
|
- a -= part->vel * part->dataBlock->dragCoefficient;
|
|
|
|
|
|
+ a -= part->vel * part->dataBlock->dragCoefficient;
|
|
a -= mWindVelocity * part->dataBlock->windCoefficient;
|
|
a -= mWindVelocity * part->dataBlock->windCoefficient;
|
|
- a += Point3F(0.0f, 0.0f, -9.81f) * part->dataBlock->gravityCoefficient;
|
|
|
|
|
|
+ a.z += -9.81f*part->dataBlock->gravityCoefficient; // AFX -- as long as gravity is a constant, this is faster
|
|
|
|
|
|
part->vel += a * t;
|
|
part->vel += a * t;
|
|
- part->pos += part->vel * t;
|
|
|
|
|
|
+ part->pos_local += part->vel * t;
|
|
|
|
+
|
|
|
|
+ // AFX -- allow subclasses to adjust the particle params here
|
|
|
|
+ sub_particleUpdate(part);
|
|
|
|
+
|
|
|
|
+ if (part->dataBlock->constrain_pos)
|
|
|
|
+ part->pos = part->pos_local + this->pos_pe;
|
|
|
|
+ else
|
|
|
|
+ part->pos = part->pos_local;
|
|
|
|
|
|
updateKeyData( part );
|
|
updateKeyData( part );
|
|
}
|
|
}
|
|
@@ -2153,3 +2324,43 @@ DefineEngineMethod(ParticleEmitterData, reload, void,(),,
|
|
{
|
|
{
|
|
object->reload();
|
|
object->reload();
|
|
}
|
|
}
|
|
|
|
+void ParticleEmitter::emitParticlesExt(const MatrixF& xfm, const Point3F& point,
|
|
|
|
+ const Point3F& velocity, const U32 numMilliseconds)
|
|
|
|
+{
|
|
|
|
+ if (mDataBlock->use_emitter_xfm)
|
|
|
|
+ {
|
|
|
|
+ Point3F zero_point(0.0f, 0.0f, 0.0f);
|
|
|
|
+ this->pos_pe = zero_point;
|
|
|
|
+ this->setTransform(xfm);
|
|
|
|
+ Point3F axis(0.0,0.0,1.0);
|
|
|
|
+ xfm.mulV(axis);
|
|
|
|
+ emitParticles(zero_point, true, axis, velocity, numMilliseconds);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ this->pos_pe = point;
|
|
|
|
+ Point3F axis(0.0,0.0,1.0);
|
|
|
|
+ xfm.mulV(axis);
|
|
|
|
+ emitParticles(point, true, axis, velocity, numMilliseconds);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void ParticleEmitter::setForcedObjBox(Box3F& box)
|
|
|
|
+{
|
|
|
|
+ mObjBox = box;
|
|
|
|
+ forced_bbox = true;
|
|
|
|
+#if defined(AFX_CAP_PARTICLE_POOLS)
|
|
|
|
+ if (pool)
|
|
|
|
+ pool->updatePoolBBox(this);
|
|
|
|
+#endif
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void ParticleEmitter::setSortPriority(S8 priority)
|
|
|
|
+{
|
|
|
|
+ sort_priority = (priority == 0) ? 1 : priority;
|
|
|
|
+#if defined(AFX_CAP_PARTICLE_POOLS)
|
|
|
|
+ if (pool)
|
|
|
|
+ pool->setSortPriority(sort_priority);
|
|
|
|
+#endif
|
|
|
|
+}
|
|
|
|
+
|