Merge pull request #464 from Azaezel/alpha40/ribbonParticles

ribbon particle resource port
This commit is contained in:
Brian Roberts 2021-02-15 22:28:10 -06:00 committed by GitHub
commit f4982f3b7d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 354 additions and 5 deletions

View file

@ -91,6 +91,7 @@ ConsoleDocClass( ParticleEmitterData,
" ejectionOffset = 0.0;\n"
" thetaMin = 85;\n"
" thetaMax = 85;\n"
" thetaVariance = 0;\n"
" phiReferenceVel = 0;\n"
" phiVariance = 360;\n"
" overrideAdvance = false;\n"
@ -127,6 +128,7 @@ ParticleEmitterData::ParticleEmitterData()
thetaMin = 0.0f; // All heights
thetaMax = 90.0f;
thetaVariance = 0.0f;
phiReferenceVel = sgDefaultPhiReferenceVel; // All directions
phiVariance = sgDefaultPhiVariance;
@ -140,6 +142,7 @@ ParticleEmitterData::ParticleEmitterData()
overrideAdvance = true;
orientParticles = false;
orientOnVelocity = true;
ribbonParticles = false;
useEmitterSizes = false;
useEmitterColors = false;
particleString = NULL;
@ -158,7 +161,6 @@ ParticleEmitterData::ParticleEmitterData()
alignParticles = false;
alignDirection = Point3F(0.0f, 1.0f, 0.0f);
ejectionInvert = false;
fade_color = false;
fade_alpha = false;
@ -231,6 +233,9 @@ void ParticleEmitterData::initPersistFields()
addFieldV( "thetaMax", TYPEID< F32 >(), Offset(thetaMax, ParticleEmitterData), &thetaFValidator,
"Maximum angle, from the horizontal plane, to eject particles from." );
addFieldV( "thetaVariance", TYPEID< F32 >(), Offset(thetaVariance, ParticleEmitterData), &thetaFValidator,
"Angle variance from the previous particle, from 0 - 180." );
addFieldV( "phiReferenceVel", TYPEID< F32 >(), Offset(phiReferenceVel, ParticleEmitterData), &phiFValidator,
"Reference angle, from the vertical plane, to eject particles from." );
@ -258,6 +263,9 @@ void ParticleEmitterData::initPersistFields()
addField( "orientOnVelocity", TYPEID< bool >(), Offset(orientOnVelocity, ParticleEmitterData),
"If true, particles will be oriented to face in the direction they are moving." );
addField( "ribbonParticles", TYPEID< bool >(), Offset(ribbonParticles, ParticleEmitterData),
"If true, particles are rendered as a continous ribbon." );
addField( "particles", TYPEID< StringTableEntry >(), Offset(particleString, ParticleEmitterData),
"@brief List of space or TAB delimited ParticleData datablock names.\n\n"
"A random one of these datablocks is selected each time a particle is "
@ -368,6 +376,7 @@ void ParticleEmitterData::packData(BitStream* stream)
stream->writeInt((S32)(ejectionOffsetVariance * 100), 16);
stream->writeRangedU32((U32)thetaMin, 0, 180);
stream->writeRangedU32((U32)thetaMax, 0, 180);
stream->writeRangedU32((U32)thetaVariance, 0, 180);
if( stream->writeFlag( phiReferenceVel != sgDefaultPhiReferenceVel ) )
stream->writeRangedU32((U32)phiReferenceVel, 0, 360);
if( stream->writeFlag( phiVariance != sgDefaultPhiVariance ) )
@ -379,6 +388,7 @@ void ParticleEmitterData::packData(BitStream* stream)
stream->writeFlag(overrideAdvance);
stream->writeFlag(orientParticles);
stream->writeFlag(orientOnVelocity);
stream->writeFlag(ribbonParticles);
stream->write( lifetimeMS );
stream->write( lifetimeVarianceMS );
stream->writeFlag(useEmitterSizes);
@ -441,6 +451,7 @@ void ParticleEmitterData::unpackData(BitStream* stream)
ejectionOffsetVariance = 0.0f;
thetaMin = (F32)stream->readRangedU32(0, 180);
thetaMax = (F32)stream->readRangedU32(0, 180);
thetaVariance = (F32)stream->readRangedU32(0, 180);
if( stream->readFlag() )
phiReferenceVel = (F32)stream->readRangedU32(0, 360);
else
@ -457,6 +468,7 @@ void ParticleEmitterData::unpackData(BitStream* stream)
overrideAdvance = stream->readFlag();
orientParticles = stream->readFlag();
orientOnVelocity = stream->readFlag();
ribbonParticles = stream->readFlag();
stream->read( &lifetimeMS );
stream->read( &lifetimeVarianceMS );
useEmitterSizes = stream->readFlag();
@ -544,6 +556,11 @@ bool ParticleEmitterData::onAdd()
Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) ejectionOffset < 0", getName());
ejectionOffset = 0.0f;
}
if( ejectionOffsetVariance < 0.0f )
{
Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) ejectionOffset < 0", getName());
ejectionOffsetVariance = 0.0f;
}
if( thetaMin < 0.0f )
{
Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) thetaMin < 0.0", getName());
@ -559,11 +576,29 @@ bool ParticleEmitterData::onAdd()
Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) thetaMin > thetaMax", getName());
thetaMin = thetaMax;
}
if( thetaVariance > 180.0f )
{
Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) thetaVariance > 180.0", getName());
thetaVariance = 180.0f;
}
if( thetaVariance < 0.0f )
{
Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) thetaVariance < 0.0", getName());
thetaVariance = 0.0f;
}
if( phiVariance < 0.0f || phiVariance > 360.0f )
{
Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) invalid phiVariance", getName());
phiVariance = phiVariance < 0.0f ? 0.0f : 360.0f;
}
if( thetaVariance < 0.0f || thetaVariance > 180.0f )
{
Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) invalid thetaVariance", getName());
thetaVariance = thetaVariance < 0.0f ? 0.0f : 180.0f;
}
if ( softnessDistance < 0.0f )
{
@ -822,6 +857,7 @@ ParticleEmitterData::ParticleEmitterData(const ParticleEmitterData& other, bool
ejectionOffsetVariance = other.ejectionOffsetVariance;
thetaMin = other.thetaMin;
thetaMax = other.thetaMax;
thetaVariance = other.thetaVariance;
phiReferenceVel = other.phiReferenceVel;
phiVariance = other.phiVariance;
softnessDistance = other.softnessDistance;
@ -831,6 +867,7 @@ ParticleEmitterData::ParticleEmitterData(const ParticleEmitterData& other, bool
overrideAdvance = other.overrideAdvance;
orientParticles = other.orientParticles;
orientOnVelocity = other.orientOnVelocity;
ribbonParticles = other.ribbonParticles;
useEmitterSizes = other.useEmitterSizes;
useEmitterColors = other.useEmitterColors;
alignParticles = other.alignParticles;
@ -953,6 +990,9 @@ ParticleEmitter::ParticleEmitter()
n_part_capacity = 0;
n_parts = 0;
mThetaOld = 0;
mPhiOld = 0;
mCurBuffSize = 0;
mDead = false;
@ -1570,11 +1610,34 @@ void ParticleEmitter::addParticle(const Point3F& pos, const Point3F& axis, const
else
pos_start = pos;
Point3F ejectionAxis = axis;
F32 theta = (mDataBlock->thetaMax - mDataBlock->thetaMin) * gRandGen.randF() +
mDataBlock->thetaMin;
F32 theta = 0.0f;
F32 thetaTarget = (mDataBlock->thetaMax + mDataBlock->thetaMin) / 2.0f;
if (mDataBlock->thetaVariance <= 0.0f)
theta = (mDataBlock->thetaMax - mDataBlock->thetaMin) * gRandGen.randF() + mDataBlock->thetaMin;
else
{
F32 thetaDelta = ( gRandGen.randF() - 0.5f) * mDataBlock->thetaVariance * 2.0f;
thetaDelta += ( (thetaTarget - mThetaOld) / mDataBlock->thetaMax ) * mDataBlock->thetaVariance * 0.25f;
theta = mThetaOld + thetaDelta;
}
mThetaOld = theta;
F32 ref = (F32(mInternalClock) / 1000.0) * mDataBlock->phiReferenceVel;
F32 phi = ref + gRandGen.randF() * mDataBlock->phiVariance;
F32 phi = 0.0f;
if (mDataBlock->thetaVariance <= 0.0f)
{
phi = ref + gRandGen.randF() * mDataBlock->phiVariance;
}
else
{
F32 phiDelta = (gRandGen.randF() - 0.5f) * mDataBlock->thetaVariance * 2.0f;
phi = ref + mPhiOld + phiDelta;
if (phi > mDataBlock->phiVariance)
phi += fabs(phiDelta) * -2.0f;
if (phi < 0.0f)
phi += fabs(phiDelta) * 2.0f;
}
mPhiOld = phi;
// Both phi and theta are in degs. Create axis angles out of them, and create the
// appropriate rotation matrix...
@ -1601,7 +1664,16 @@ void ParticleEmitter::addParticle(const Point3F& pos, const Point3F& axis, const
pNew->acc.set(0, 0, 0);
pNew->currentAge = age_offset;
pNew->t_last = 0.0f;
// ribbon particles only use the first particle
if(mDataBlock->ribbonParticles)
{
mDataBlock->particleDataBlocks[0]->initializeParticle(pNew, vel);
}
else
{
U32 dBlockIndex = gRandGen.randI() % mDataBlock->particleDataBlocks.size();
mDataBlock->particleDataBlocks[dBlockIndex]->initializeParticle(pNew, vel);
}
updateKeyData( pNew );
}
@ -1745,6 +1817,7 @@ void ParticleEmitter::updateKeyData( Particle *part )
//-----------------------------------------------------------------------------
// Update particles
//-----------------------------------------------------------------------------
// AFX CODE BLOCK (enhanced-emitter) <<
void ParticleEmitter::update( U32 ms )
{
F32 t = F32(ms)/1000.0f; // AFX -- moved outside loop, no need to recalculate this for every particle
@ -1828,7 +1901,52 @@ void ParticleEmitter::copyToVB( const Point3F &camPos, const LinearColorF &ambie
tempBuff.reserve( n_parts*4 + 64); // make sure tempBuff is big enough
ParticleVertexType *buffPtr = tempBuff.address(); // use direct pointer (faster)
if (mDataBlock->orientParticles)
if (mDataBlock->ribbonParticles)
{
PROFILE_START(ParticleEmitter_copyToVB_Ribbon);
if (mDataBlock->reverseOrder)
{
buffPtr += 4 * (n_parts - 1);
// do sorted-oriented particles
if (mDataBlock->sortParticles)
{
SortParticle* partPtr = orderedVector.address();
for (U32 i = 0; i < n_parts - 1; i++, partPtr++, buffPtr -= 4)
setupRibbon(partPtr->p, partPtr++->p, partPtr--->p, camPos, ambientColor, buffPtr);
}
// do unsorted-oriented particles
else
{
Particle* oldPtr = NULL;
for (Particle* partPtr = part_list_head.next; partPtr != NULL; partPtr = partPtr->next, buffPtr -= 4) {
setupRibbon(partPtr, partPtr->next, oldPtr, camPos, ambientColor, buffPtr);
oldPtr = partPtr;
}
}
}
else
{
// do sorted-oriented particles
if (mDataBlock->sortParticles)
{
SortParticle* partPtr = orderedVector.address();
for (U32 i = 0; i < n_parts - 1; i++, partPtr++, buffPtr += 4)
setupRibbon(partPtr->p, partPtr++->p, partPtr--->p, camPos, ambientColor, buffPtr);
}
// do unsorted-oriented particles
else
{
Particle* oldPtr = NULL;
for (Particle* partPtr = part_list_head.next; partPtr != NULL; partPtr = partPtr->next, buffPtr += 4) {
setupRibbon(partPtr, partPtr->next, oldPtr, camPos, ambientColor, buffPtr);
oldPtr = partPtr;
}
}
}
PROFILE_END();
}
else if (mDataBlock->orientParticles)
{
PROFILE_START(ParticleEmitter_copyToVB_Orient);
@ -2265,6 +2383,199 @@ void ParticleEmitter::setupAligned( const Particle *part,
}
}
void ParticleEmitter::setupRibbon(Particle *part,
Particle *next,
Particle *prev,
const Point3F &camPos,
const LinearColorF &ambientColor,
ParticleVertexType *lVerts)
{
Point3F dir, dirFromCam;
Point3F crossDir, crossDirNext;
Point3F start, end;
LinearColorF prevCol;
static Point3F crossDirPrev;
static int position;
static F32 alphaMod, alphaModEnd;
const F32 ambientLerp = mClampF(mDataBlock->ambientFactor, 0.0f, 1.0f);
LinearColorF partCol = mLerp(part->color, (part->color * ambientColor), ambientLerp);
if (part->currentAge > part->totalLifetime)
{
F32 alphaDeath = (part->currentAge - part->totalLifetime) / 200.0f;
if (alphaDeath > 1.0f)
alphaDeath = 1.0f;
alphaDeath = 1.0f - alphaDeath;
partCol.alpha *= alphaDeath;
}
start = part->pos;
position++;
if (next == NULL && prev == NULL) {
// a ribbon of just one particle
position = 0;
if (part->vel.magnitudeSafe() == 0.0)
dir = part->orientDir;
else
dir = part->vel;
dir.normalize();
dirFromCam = part->pos - camPos;
mCross(dirFromCam, dir, &crossDir);
crossDir.normalize();
crossDir = crossDir * part->size * 0.5;
crossDirPrev = crossDir;
partCol.alpha = 0.0f;
prevCol = partCol;
end = part->pos;
}
else if (next == NULL && prev != NULL)
{
// last link in the chain, also the oldest
dir = part->pos - prev->pos;
dir.normalize();
dirFromCam = part->pos - camPos;
mCross(dirFromCam, dir, &crossDir);
crossDir.normalize();
crossDir = crossDir * part->size * 0.5;
end = prev->pos;
partCol.alpha = 0.0f;
prevCol = mLerp(prev->color, (prev->color * ambientColor), ambientLerp);
prevCol.alpha *= alphaModEnd;
}
else if (next != NULL && prev == NULL)
{
// first link in chain, newest particle
// since we draw from current to previous, this one isn't drawn
position = 0;
dir = next->pos - part->pos;
dir.normalize();
dirFromCam = part->pos - camPos;
mCross(dirFromCam, dir, &crossDir);
crossDir.normalize();
crossDir = crossDir * part->size * 0.5f;
crossDirPrev = crossDir;
partCol.alpha = 0.0f;
prevCol = partCol;
alphaModEnd = 0.0f;
end = part->pos;
}
else
{
// middle of chain
dir = next->pos - prev->pos;
dir.normalize();
dirFromCam = part->pos - camPos;
mCross(dirFromCam, dir, &crossDir);
crossDir.normalize();
crossDir = crossDir * part->size * 0.5;
prevCol = mLerp(prev->color, (prev->color * ambientColor), ambientLerp);
if (position == 1)
{
// the second particle has a few tweaks for alpha, to smoothly match the first particle
// we only want to do this once when the particle first fades in, and avoid a strobing effect
alphaMod = (float(part->currentAge) / float(part->currentAge - prev->currentAge)) - 1.0f;
if (alphaMod > 1.0f)
alphaMod = 1.0f;
partCol.alpha *= alphaMod;
prevCol.alpha = 0.0f;
if (next->next == NULL)
alphaModEnd = alphaMod;
//Con::printf("alphaMod: %f", alphaMod );
}
else if (position == 2)
{
prevCol.alpha *= alphaMod;
alphaMod = 0.0f;
}
if (next->next == NULL && position > 1)
{
// next to last particle, start the fade out
alphaModEnd = (float(next->totalLifetime - next->currentAge)) / (float(part->totalLifetime - part->currentAge));
alphaModEnd *= 2.0f;
if (alphaModEnd > 1.0f)
alphaModEnd = 1.0f;
partCol.alpha *= alphaModEnd;
//Con::printf("alphaMod: %f Lifetime: %d Age: %d", alphaMod, part->totalLifetime, part->currentAge );
}
end = prev->pos;
}
ColorI pCol = partCol.toColorI();
// Here we deal with UVs for animated particle (oriented)
if (part->dataBlock->animateTexture)
{
// Let particle compute the UV indices for current frame
S32 fm = (S32)(part->currentAge*(1.0f / 1000.0f)*part->dataBlock->framesPerSec);
U8 fm_tile = part->dataBlock->animTexFrames[fm % part->dataBlock->numFrames];
S32 uv[4];
uv[0] = fm_tile + fm_tile / part->dataBlock->animTexTiling.x;
uv[1] = uv[0] + (part->dataBlock->animTexTiling.x + 1);
uv[2] = uv[1] + 1;
uv[3] = uv[0] + 1;
lVerts->point = start + crossDir;
lVerts->color = pCol;
// Here and below, we copy UVs from particle datablock's current frame's UVs (oriented)
lVerts->texCoord = part->dataBlock->animTexUVs[uv[0]];
++lVerts;
lVerts->point = start - crossDir;
lVerts->color = pCol;
lVerts->texCoord = part->dataBlock->animTexUVs[uv[1]];
++lVerts;
lVerts->point = end - crossDirPrev;
lVerts->color = pCol;
lVerts->texCoord = part->dataBlock->animTexUVs[uv[2]];
++lVerts;
lVerts->point = end + crossDirPrev;
lVerts->color = pCol;
lVerts->texCoord = part->dataBlock->animTexUVs[uv[3]];
++lVerts;
crossDirPrev = crossDir;
return;
}
lVerts->point = start + crossDir;
lVerts->color = pCol;
// Here and below, we copy UVs from particle datablock's texCoords (oriented)
lVerts->texCoord = part->dataBlock->texCoords[0];
++lVerts;
lVerts->point = start - crossDir;
lVerts->color = pCol;
lVerts->texCoord = part->dataBlock->texCoords[1];
++lVerts;
lVerts->point = end - crossDirPrev;
lVerts->color = pCol;
lVerts->texCoord = part->dataBlock->texCoords[2];
++lVerts;
lVerts->point = end + crossDirPrev;
lVerts->color = pCol;
lVerts->texCoord = part->dataBlock->texCoords[3];
++lVerts;
crossDirPrev = crossDir;
}
bool ParticleEmitterData::reload()
{
// Clear out current particle data.

View file

@ -85,6 +85,7 @@ class ParticleEmitterData : public GameBaseData
F32 ejectionOffsetVariance; ///< Z offset Variance from emitter point to eject
F32 thetaMin; ///< Minimum angle, from the horizontal plane, to eject from
F32 thetaMax; ///< Maximum angle, from the horizontal plane, to eject from
F32 thetaVariance; ///< Angle, from the previous particle, to eject from
F32 phiReferenceVel; ///< Reference angle, from the verticle plane, to eject from
F32 phiVariance; ///< Varience from the reference angle, from 0 to n
@ -102,6 +103,7 @@ class ParticleEmitterData : public GameBaseData
bool overrideAdvance; ///<
bool orientParticles; ///< Particles always face the screen
bool orientOnVelocity; ///< Particles face the screen at the start
bool ribbonParticles; ///< Particles are rendered as a continous ribbon
bool useEmitterSizes; ///< Use emitter specified sizes instead of datablock sizes
bool useEmitterColors; ///< Use emitter specified colors instead of datablock colors
bool alignParticles; ///< Particles always face along a particular axis
@ -244,6 +246,13 @@ class ParticleEmitter : public GameBase
const LinearColorF &ambientColor,
ParticleVertexType *lVerts );
inline void setupRibbon( Particle *part,
Particle *next,
Particle *prev,
const Point3F &camPos,
const LinearColorF &ambientColor,
ParticleVertexType *lVerts);
/// Updates the bounding box for the particle system
void updateBBox();
@ -285,6 +294,9 @@ protected:
U32 mNextParticleTime;
F32 mThetaOld;
F32 mPhiOld;
Point3F mLastPosition;
bool mHasLastPosition;
private:

View file

@ -786,6 +786,32 @@ $PE_guielement_ext_colorpicker = "18 18";
altCommand = "PE_EmitterEditor.updateEmitter( \"alignDirection\", $ThisControl.getText());";
};
};
new GuiControl(){ // Emitter ribbon
isContainer = "1";
HorizSizing = "width";
VertSizing = "bottom";
Position = $PE_guielement_pos_single_container ;
Extent = $PE_guielement_ext_single_container ;
new GuiTextCtrl() {
Profile = "ToolsGuiTextProfile";
HorizSizing = "width";
VertSizing = "bottom";
position = $PE_guielement_pos_name;
Extent = $PE_guielement_ext_checkbox_name;
text = "Render as a Ribbon";
};
new GuiCheckBoxCtrl() {
internalName = "PEE_ribbonParticles";
Profile = "ToolsGuiCheckBoxProfile";
HorizSizing = "left";
VertSizing = "bottom";
position = $PE_guielement_pos_checkbox;
Extent = $PE_guielement_ext_checkbox;
text = "";
command = "PE_EmitterEditor.updateEmitter( \"ribbonParticles\", $ThisControl.getValue());";
};
};
}; // end stack
}; // end "motion" rollout
new GuiRolloutCtrl() {