diff --git a/Engine/source/T3D/debris.cpp b/Engine/source/T3D/debris.cpp index 02b475c80..57d9a0577 100644 --- a/Engine/source/T3D/debris.cpp +++ b/Engine/source/T3D/debris.cpp @@ -24,6 +24,7 @@ // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames // Copyright (C) 2015 Faust Logic, Inc. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// + #include "platform/platform.h" #include "T3D/debris.h" @@ -633,7 +634,8 @@ bool Debris::onAdd() { sizeList[0] = mSize * 0.5; sizeList[1] = mSize; - sizeList[2] = mSize * 1.5; + for (U32 i = 2; i < ParticleData::PDC_NUM_KEYS; i++) + sizeList[i] = mSize * 1.5; mEmitterList[0]->setSizes( sizeList ); } @@ -642,7 +644,8 @@ bool Debris::onAdd() { sizeList[0] = 0.0; sizeList[1] = mSize * 0.5; - sizeList[2] = mSize; + for (U32 i = 2; i < ParticleData::PDC_NUM_KEYS; i++) + sizeList[i] = mSize; mEmitterList[1]->setSizes( sizeList ); } diff --git a/Engine/source/T3D/fx/particle.cpp b/Engine/source/T3D/fx/particle.cpp index f887a021e..823376fc7 100644 --- a/Engine/source/T3D/fx/particle.cpp +++ b/Engine/source/T3D/fx/particle.cpp @@ -24,6 +24,7 @@ // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames // Copyright (C) 2015 Faust Logic, Inc. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// + #include "particle.h" #include "console/consoleTypes.h" #include "console/typeValidators.h" @@ -77,6 +78,8 @@ static const F32 sgDefaultSpinSpeed = 1.f; static const F32 sgDefaultSpinRandomMin = 0.f; static const F32 sgDefaultSpinRandomMax = 0.f; +static const F32 sgDefaultSpinBias = 1.0f; +static const F32 sgDefaultSizeBias = 1.0f; //----------------------------------------------------------------------------- // Constructor @@ -107,9 +110,9 @@ ParticleData::ParticleData() } times[0] = 0.0f; - times[1] = 0.33f; - times[2] = 0.66f; - times[3] = 1.0f; + times[1] = 1.0f; + for (i = 2; i < PDC_NUM_KEYS; i++) + times[i] = -1.0f; texCoords[0].set(0.0,0.0); // texture coords at 4 corners texCoords[1].set(0.0,1.0); // of particle quad @@ -120,18 +123,20 @@ ParticleData::ParticleData() animTexUVs = NULL; // array of tile vertex UVs textureName = NULL; // texture filename textureHandle = NULL; // loaded texture handle + textureExtName = NULL; + textureExtHandle = NULL; + constrain_pos = false; + start_angle = 0.0f; + angle_variance = 0.0f; + sizeBias = sgDefaultSizeBias; + spinBias = sgDefaultSpinBias; + randomizeSpinDir = false; } //----------------------------------------------------------------------------- // Destructor //----------------------------------------------------------------------------- -ParticleData::~ParticleData() -{ - if (animTexUVs) - { - delete [] animTexUVs; - } -} + FRangeValidator dragCoefFValidator(0.f, 5.f); FRangeValidator gravCoefFValidator(-10.f, 10.f); @@ -219,6 +224,15 @@ void ParticleData::initPersistFields() "@brief Time keys used with the colors and sizes keyframes.\n\n" "Values are from 0.0 (particle creation) to 1.0 (end of lifespace)." ); + addGroup("AFX"); + addField("textureExtName", TypeFilename, Offset(textureExtName, ParticleData)); + addField("constrainPos", TypeBool, Offset(constrain_pos, ParticleData)); + addField("angle", TypeF32, Offset(start_angle, ParticleData)); + addField("angleVariance", TypeF32, Offset(angle_variance, ParticleData)); + addField("sizeBias", TypeF32, Offset(sizeBias, ParticleData)); + addField("spinBias", TypeF32, Offset(spinBias, ParticleData)); + addField("randomizeSpinDir", TypeBool, Offset(randomizeSpinDir, ParticleData)); + endGroup("AFX"); Parent::initPersistFields(); } @@ -248,18 +262,22 @@ void ParticleData::packData(BitStream* stream) stream->writeInt((S32)(spinRandomMin + 1000), 11); stream->writeInt((S32)(spinRandomMax + 1000), 11); } + if(stream->writeFlag(spinBias != sgDefaultSpinBias)) + stream->write(spinBias); + stream->writeFlag(randomizeSpinDir); stream->writeFlag(useInvAlpha); S32 i, count; // see how many frames there are: - for(count = 0; count < 3; count++) + for(count = 0; count < ParticleData::PDC_NUM_KEYS-1; count++) if(times[count] >= 1) break; count++; - stream->writeInt(count-1, 2); + // An extra bit is needed for 8 keys. + stream->writeInt(count-1, 3); for( i=0; iwriteFloat( colors[i].green, 7); stream->writeFloat( colors[i].blue, 7); stream->writeFloat( colors[i].alpha, 7); - stream->writeFloat( sizes[i]/MaxParticleSize, 14); + // AFX bits raised from 14 to 16 to allow larger sizes + stream->writeFloat( sizes[i]/MaxParticleSize, 16); stream->writeFloat( times[i], 8); } @@ -284,6 +303,13 @@ void ParticleData::packData(BitStream* stream) mathWrite(*stream, animTexTiling); stream->writeInt(framesPerSec, 8); } + if (stream->writeFlag(textureExtName && textureExtName[0])) + stream->writeString(textureExtName); + stream->writeFlag(constrain_pos); + stream->writeFloat(start_angle/360.0f, 11); + stream->writeFloat(angle_variance/180.0f, 10); + if(stream->writeFlag(sizeBias != sgDefaultSizeBias)) + stream->write(sizeBias); } //----------------------------------------------------------------------------- @@ -327,17 +353,24 @@ void ParticleData::unpackData(BitStream* stream) spinRandomMax = sgDefaultSpinRandomMax; } + if(stream->readFlag()) + stream->read(&spinBias); + else + spinBias = sgDefaultSpinBias; + randomizeSpinDir = stream->readFlag(); useInvAlpha = stream->readFlag(); S32 i; - S32 count = stream->readInt(2) + 1; + // An extra bit is needed for 8 keys. + S32 count = stream->readInt(3) + 1; for(i = 0;i < count; i++) { colors[i].red = stream->readFloat(7); colors[i].green = stream->readFloat(7); colors[i].blue = stream->readFloat(7); colors[i].alpha = stream->readFloat(7); - sizes[i] = stream->readFloat(14) * MaxParticleSize; + // AFX bits raised from 14 to 16 to allow larger sizes + sizes[i] = stream->readFloat(16) * MaxParticleSize; times[i] = stream->readFloat(8); } textureName = (stream->readFlag()) ? stream->readSTString() : 0; @@ -351,6 +384,14 @@ void ParticleData::unpackData(BitStream* stream) mathRead(*stream, &animTexTiling); framesPerSec = stream->readInt(8); } + textureExtName = (stream->readFlag()) ? stream->readSTString() : 0; + constrain_pos = stream->readFlag(); + start_angle = 360.0f*stream->readFloat(11); + angle_variance = 180.0f*stream->readFloat(10); + if(stream->readFlag()) + stream->read(&sizeBias); + else + sizeBias = sgDefaultSizeBias; } bool ParticleData::protectedSetSizes( void *object, const char *index, const char *data) @@ -432,11 +473,33 @@ bool ParticleData::onAdd() } times[0] = 0.0f; - for (U32 i = 1; i < 4; i++) { - if (times[i] < times[i-1]) { - Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) times[%d] < times[%d]", getName(), i, i-1); - times[i] = times[i-1]; - } + for (U32 i = 1; i < PDC_NUM_KEYS; i++) + { + if (times[i] < 0.0f) + break; + if (times[i] < times[i-1]) + { + Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) times[%d] < times[%d]", getName(), i, i-1); + times[i] = times[i-1]; + } + } + + times[0] = 0.0f; + + U32 last_idx = 0; + for (U32 i = 1; i < PDC_NUM_KEYS; i++) + { + if (times[i] < 0.0f) + break; + else + last_idx = i; + } + + for (U32 i = last_idx+1; i < PDC_NUM_KEYS; i++) + { + times[i] = times[last_idx]; + colors[i] = colors[last_idx]; + sizes[i] = sizes[last_idx]; } // Here we validate parameters @@ -475,6 +538,10 @@ bool ParticleData::onAdd() } } + start_angle = mFmod(start_angle, 360.0f); + if (start_angle < 0.0f) + start_angle += 360.0f; + angle_variance = mClampF(angle_variance, -180.0f, 180.0f); return true; } @@ -500,6 +567,15 @@ bool ParticleData::preload(bool server, String &errorStr) error = true; } } + if (textureExtName && textureExtName[0]) + { + textureExtHandle = GFXTexHandle(textureExtName, &GFXStaticTextureSRGBProfile, avar("%s() - textureExtHandle (line %d)", __FUNCTION__, __LINE__)); + if (!textureExtHandle) + { + errorStr = String::ToString("Missing particle texture: %s", textureName); + error = true; + } + } if (animateTexture) { @@ -611,6 +687,11 @@ void ParticleData::initializeParticle(Particle* init, const Point3F& inheritVelo // assign spin amount init->spinSpeed = spinSpeed * gRandGen.randF( spinRandomMin, spinRandomMax ); + // apply spin bias + init->spinSpeed *= spinBias; + // randomize spin direction + if (randomizeSpinDir && (gRandGen.randI( 0, 1 ) == 1)) + init->spinSpeed = -init->spinSpeed; } bool ParticleData::reload(char errorBuffer[256]) diff --git a/Engine/source/T3D/fx/particle.h b/Engine/source/T3D/fx/particle.h index 17c173e67..b1c25f7f3 100644 --- a/Engine/source/T3D/fx/particle.h +++ b/Engine/source/T3D/fx/particle.h @@ -49,7 +49,9 @@ class ParticleData : public SimDataBlock public: enum PDConst { - PDC_NUM_KEYS = 4, + // This increase the keyframes from 4 to 8. Especially useful for premult-alpha blended particles + // for which 4 keyframes is often not enough. + PDC_NUM_KEYS = 8, }; F32 dragCoefficient; @@ -106,6 +108,16 @@ class ParticleData : public SimDataBlock /*C*/ ParticleData(const ParticleData&, bool = false); virtual void onPerformSubstitutions(); virtual bool allowSubstitutions() const { return true; } + protected: + F32 spinBias; + bool randomizeSpinDir; + StringTableEntry textureExtName; + public: + GFXTexHandle textureExtHandle; + bool constrain_pos; + F32 start_angle; + F32 angle_variance; + F32 sizeBias; public: bool loadParameters(); bool reload(String &errorStr); @@ -135,6 +147,10 @@ struct Particle F32 spinSpeed; Particle * next; + Point3F pos_local; + F32 t_last; + Point3F radial_v; // radial vector for concentric effects + // note -- for non-oriented particles, we use orientDir.x to store the billboard start angle. };