2919 lines
84 KiB
C++
2919 lines
84 KiB
C++
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
// Copyright (C) 2010 Winch Gate Property Limited
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#include "std3d.h"
|
|
|
|
#include "nel/3d/ps_emitter.h"
|
|
#include "nel/3d/material.h"
|
|
#include "nel/misc/line.h"
|
|
#include "nel/3d/dru.h"
|
|
#include "nel/3d/particle_system.h"
|
|
|
|
namespace NL3D {
|
|
|
|
|
|
|
|
|
|
static const uint EMITTER_BUFF_SIZE = 512; // number of emitter to be processed at once
|
|
static const float EMIT_PERIOD_THRESHOLD = 1.f / 75.f; // assuming the same behaviour than with a 75 hz rendering
|
|
bool CPSEmitter::_BypassEmitOnDeath = false;
|
|
|
|
|
|
//////////////////////
|
|
// STATIC FUNCTIONS //
|
|
//////////////////////
|
|
/** In an arrey of float, all value that are 0.f are replaced by EMIT_PERIOD_THRESHOLD
|
|
* A period of 0 is allowed for emitter and means "emit at each frame"
|
|
* This is deprecated now, and this helps to avoid that behaviour
|
|
*/
|
|
static void replaceNullPeriodsByThreshold(float *tab, uint numElem)
|
|
{
|
|
NL_PS_FUNC(replaceNullPeriodsByThreshold)
|
|
const float *endTab = tab + numElem;
|
|
while (tab != endTab)
|
|
{
|
|
if (*tab == 0.f) *tab = EMIT_PERIOD_THRESHOLD;
|
|
++ tab;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////
|
|
// CPSEmitter implementation //
|
|
///////////////////////////////
|
|
CPSEmitter::CPSEmitter() : _EmittedType(NULL),
|
|
_SpeedInheritanceFactor(0.f),
|
|
_EmissionType(regular),
|
|
_Period(0.02f),
|
|
_PeriodScheme(NULL),
|
|
_GenNb(1),
|
|
_GenNbScheme(NULL),
|
|
_EmitDelay(0),
|
|
_MaxEmissionCount(0),
|
|
_SpeedBasisEmission(false),
|
|
_ConsistentEmission(true),
|
|
_BypassAutoLOD(false),
|
|
_UserMatrixModeForEmissionDirection(false),
|
|
_EmitTrigger(false),
|
|
_UserDirectionMatrixMode(PSFXWorldMatrix)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_CPSEmitter)
|
|
}
|
|
|
|
|
|
///==========================================================================
|
|
CPSEmitter::~CPSEmitter()
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_CPSEmitterDtor)
|
|
delete _PeriodScheme;
|
|
delete _GenNbScheme;
|
|
// if a located is emitted, unregister us as an observer
|
|
if (_EmittedType)
|
|
{
|
|
_EmittedType->unregisterDtorObserver(this);
|
|
}
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::releaseRefTo(const CParticleSystemProcess *other)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_releaseRefTo)
|
|
if (_EmittedType == other)
|
|
{
|
|
setEmittedType(NULL);
|
|
}
|
|
}
|
|
|
|
void CPSEmitter::releaseAllRef()
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_releaseAllRef)
|
|
setEmittedType(NULL);
|
|
}
|
|
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::setOwner(CPSLocated *psl)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_setOwner)
|
|
CPSLocatedBindable::setOwner(psl);
|
|
updateMaxCountVect();
|
|
}
|
|
|
|
|
|
///==========================================================================
|
|
inline void CPSEmitter::processEmit(uint32 index, sint nbToGenerate)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_processEmit)
|
|
NLMISC::CVector speed, pos;
|
|
nlassert(_Owner);
|
|
if (!_SpeedBasisEmission)
|
|
{
|
|
if (_SpeedInheritanceFactor == 0.f)
|
|
{
|
|
if (!_UserMatrixModeForEmissionDirection)
|
|
{
|
|
while (nbToGenerate > 0)
|
|
{
|
|
nbToGenerate --;
|
|
emit(_Owner->getPos()[index], index, pos, speed);
|
|
_EmittedType->postNewElement(pos, speed, *this->_Owner, index, _Owner->getMatrixMode(), 0.f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (nbToGenerate > 0)
|
|
{
|
|
nbToGenerate --;
|
|
emit(_Owner->getPos()[index], index, pos, speed);
|
|
_EmittedType->postNewElement(pos, speed, *this->_Owner, index, _UserDirectionMatrixMode, 0.f);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (nbToGenerate --)
|
|
{
|
|
emit(_Owner->getPos()[index], index, pos, speed);
|
|
_EmittedType->postNewElement(pos, speed + _SpeedInheritanceFactor * _Owner->getSpeed()[index], *this->_Owner, 0, _Owner->getMatrixMode(), 0.f);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NLMISC::CMatrix m;
|
|
CPSUtil::buildSchmidtBasis(_Owner->getSpeed()[index], m);
|
|
if (_SpeedInheritanceFactor == 0.f)
|
|
{
|
|
while (nbToGenerate > 0)
|
|
{
|
|
nbToGenerate --;
|
|
emit(_Owner->getPos()[index], index, pos, speed);
|
|
_EmittedType->postNewElement(pos, m * speed, *this->_Owner, index, _Owner->getMatrixMode(), 0.f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (nbToGenerate --)
|
|
{
|
|
emit(_Owner->getPos()[index], index, pos, speed);
|
|
_EmittedType->postNewElement(pos, m * speed + _SpeedInheritanceFactor * _Owner->getSpeed()[index], *this->_Owner, index, _Owner->getMatrixMode(), 0.f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::processEmitOutsideSimLoop(uint32 index,sint nbToGenerate)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_processEmitOutsideSimLoop)
|
|
NLMISC::CVector speed, pos;
|
|
nlassert(_Owner);
|
|
if (!_SpeedBasisEmission)
|
|
{
|
|
if (_SpeedInheritanceFactor == 0.f)
|
|
{
|
|
if (!_UserMatrixModeForEmissionDirection)
|
|
{
|
|
while (nbToGenerate > 0)
|
|
{
|
|
nbToGenerate --;
|
|
emit(_Owner->getPos()[index], index, pos, speed);
|
|
_EmittedType->newElement(pos, speed, this->_Owner, index, _Owner->getMatrixMode(), true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (nbToGenerate > 0)
|
|
{
|
|
nbToGenerate --;
|
|
emit(_Owner->getPos()[index], index, pos, speed);
|
|
_EmittedType->newElement(pos, speed, this->_Owner, index, _UserDirectionMatrixMode);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (nbToGenerate --)
|
|
{
|
|
emit(_Owner->getPos()[index], index, pos, speed);
|
|
_EmittedType->newElement(pos, speed + _SpeedInheritanceFactor * _Owner->getSpeed()[index], this->_Owner, 0, _Owner->getMatrixMode(), true);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NLMISC::CMatrix m;
|
|
CPSUtil::buildSchmidtBasis(_Owner->getSpeed()[index], m);
|
|
if (_SpeedInheritanceFactor == 0.f)
|
|
{
|
|
while (nbToGenerate > 0)
|
|
{
|
|
nbToGenerate --;
|
|
emit(_Owner->getPos()[index], index, pos, speed);
|
|
_EmittedType->newElement(pos, m * speed, this->_Owner, index, _Owner->getMatrixMode(), true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (nbToGenerate --)
|
|
{
|
|
emit(_Owner->getPos()[index], index, pos, speed);
|
|
_EmittedType->newElement(pos, m * speed + _SpeedInheritanceFactor * _Owner->getSpeed()[index], this->_Owner, index, _Owner->getMatrixMode(), true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
///==========================================================================
|
|
inline void CPSEmitter::processEmitConsistent(const NLMISC::CVector &emitterPos,
|
|
uint32 index,
|
|
sint nbToGenerate,
|
|
TAnimationTime deltaT)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_processEmitConsistent)
|
|
static NLMISC::CVector speed, pos; /// speed and pos of emittee
|
|
nlassert(_Owner);
|
|
if (!_SpeedBasisEmission)
|
|
{
|
|
if (_SpeedInheritanceFactor == 0.f)
|
|
{
|
|
if (!_UserMatrixModeForEmissionDirection)
|
|
{
|
|
while (nbToGenerate > 0)
|
|
{
|
|
nbToGenerate --;
|
|
emit(emitterPos, index, pos, speed);
|
|
_EmittedType->postNewElement(pos, speed, *this->_Owner, index, _Owner->getMatrixMode(), deltaT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (nbToGenerate > 0)
|
|
{
|
|
nbToGenerate --;
|
|
emit(emitterPos, index, pos, speed);
|
|
_EmittedType->postNewElement(pos, speed, *this->_Owner, index, _UserDirectionMatrixMode, deltaT);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (nbToGenerate --)
|
|
{
|
|
emit(emitterPos, index, pos, speed);
|
|
_EmittedType->postNewElement(pos, speed + _SpeedInheritanceFactor * _Owner->getSpeed()[index], *this->_Owner, index, _Owner->getMatrixMode(), deltaT);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NLMISC::CMatrix m;
|
|
CPSUtil::buildSchmidtBasis(_Owner->getSpeed()[index], m);
|
|
if (_SpeedInheritanceFactor == 0.f)
|
|
{
|
|
while (nbToGenerate > 0)
|
|
{
|
|
nbToGenerate --;
|
|
emit(emitterPos, index, pos, speed);
|
|
_EmittedType->postNewElement(pos, m * speed, *this->_Owner, index, _Owner->getMatrixMode(), deltaT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (nbToGenerate --)
|
|
{
|
|
emit(emitterPos, index, pos, speed);
|
|
_EmittedType->postNewElement(pos, m * speed + _SpeedInheritanceFactor * _Owner->getSpeed()[index], *this->_Owner, index, _Owner->getMatrixMode(), deltaT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///==========================================================================
|
|
bool CPSEmitter::setEmissionType(TEmissionType freqType)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_setEmissionType)
|
|
if (_Owner && _Owner->getOwner())
|
|
{
|
|
CParticleSystem *ps = _Owner->getOwner();
|
|
if (ps->getBypassMaxNumIntegrationSteps())
|
|
{
|
|
if (!_Owner)
|
|
{
|
|
nlwarning("<CPSEmitter::setEmissionType> The emitter should be inserted in a CPSLocated instance");
|
|
nlassert(0);
|
|
}
|
|
// check if the new value is valid
|
|
TEmissionType oldType = _EmissionType;
|
|
_EmissionType = freqType;
|
|
if (testEmitForever() == true)
|
|
{
|
|
_EmissionType = oldType;
|
|
std::string mess = "<CPSEmitter::setEmissionType> can't set emission type to '" +
|
|
NLMISC::toString(freqType) +
|
|
"' with the current configuration : the system has been flagged with \
|
|
'BypassMaxNumIntegrationSteps', and should have a finite duration. \
|
|
The flag is not set";
|
|
nlwarning(mess.c_str());
|
|
return false;
|
|
|
|
}
|
|
}
|
|
ps->systemDurationChanged();
|
|
}
|
|
_EmissionType = freqType;
|
|
return true;
|
|
}
|
|
|
|
///==========================================================================
|
|
bool CPSEmitter::setEmittedType(CPSLocated *et)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_setEmittedType)
|
|
if (_EmittedType)
|
|
{
|
|
_EmittedType->unregisterDtorObserver(this);
|
|
}
|
|
if (et)
|
|
{
|
|
et->registerDtorObserver(this);
|
|
}
|
|
CPSLocated *oldType = _EmittedType;
|
|
_EmittedType = et;
|
|
if (_Owner && _Owner->getOwner())
|
|
{
|
|
CParticleSystem *ps = _Owner->getOwner();
|
|
if (_EmittedType)
|
|
{
|
|
bool ok = true;
|
|
if (ps->getBypassMaxNumIntegrationSteps())
|
|
{
|
|
ok = ps->canFinish();
|
|
}
|
|
else
|
|
{
|
|
ok = !ps->hasLoop();
|
|
}
|
|
if (!ok)
|
|
{
|
|
setEmittedType(oldType);
|
|
nlwarning("<CPSLocated::setEmittedType> Can't set new emitted type : this causes the system to last forever, and it has been flagged with 'BypassMaxNumIntegrationSteps'. New emitted type is not set");
|
|
return false;
|
|
}
|
|
}
|
|
ps->systemDurationChanged();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::notifyTargetRemoved(CPSLocated *ptr)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_notifyTargetRemoved)
|
|
nlassert(ptr == _EmittedType && _EmittedType);
|
|
setEmittedType(NULL);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::setPeriod(float period)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_setPeriod)
|
|
if (_PeriodScheme)
|
|
{
|
|
delete _PeriodScheme;
|
|
_PeriodScheme = NULL;
|
|
}
|
|
_Period = period;
|
|
if (_Owner && _Owner->getOwner())
|
|
{
|
|
_Owner->getOwner()->systemDurationChanged();
|
|
}
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::setPeriodScheme(CPSAttribMaker<float> *scheme)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_setPeriodScheme)
|
|
delete _PeriodScheme;
|
|
_PeriodScheme = scheme;
|
|
if (_Owner && scheme->hasMemory()) scheme->resize(_Owner->getMaxSize(), _Owner->getSize());
|
|
if (_Owner && _Owner->getOwner())
|
|
{
|
|
_Owner->getOwner()->systemDurationChanged();
|
|
}
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::setGenNb(uint32 genNb)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_setGenNb)
|
|
if (_GenNbScheme)
|
|
{
|
|
delete _GenNbScheme;
|
|
_GenNbScheme = NULL;
|
|
}
|
|
_GenNb = genNb;
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::setGenNbScheme(CPSAttribMaker<uint32> *scheme)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_setGenNbScheme)
|
|
delete _GenNbScheme;
|
|
_GenNbScheme = scheme;
|
|
if (_Owner && scheme->hasMemory()) scheme->resize(_Owner->getMaxSize(), _Owner->getSize());
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::showTool(void)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_showTool)
|
|
uint32 size = _Owner->getSize();
|
|
if (!size) return;
|
|
setupDriverModelMatrix();
|
|
|
|
const CVector I = computeI();
|
|
const CVector K = computeK();
|
|
|
|
// ugly slow code, but not for runtime
|
|
for (uint k = 0; k < size; ++k)
|
|
{
|
|
// center of the current particle
|
|
const CVector p = _Owner->getPos()[k];
|
|
const float sSize =0.1f;
|
|
std::vector<NLMISC::CLine> lines;
|
|
NLMISC::CLine l;
|
|
l.V0 = p - sSize * I; l.V1 = p + sSize * I; lines.push_back(l);
|
|
l.V0 = p - sSize * K; l.V1 = p + sSize * K; lines.push_back(l);
|
|
l.V0 = p - sSize * (I + K); l.V1 = p + sSize * (I + K); lines.push_back(l);
|
|
l.V0 = p - sSize * (I - K); l.V1 = p + sSize * (I - K); lines.push_back(l);
|
|
|
|
CMaterial mat;
|
|
mat.setBlendFunc(CMaterial::one, CMaterial::one);
|
|
mat.setZWrite(false);
|
|
mat.setLighting(false);
|
|
mat.setBlend(true);
|
|
mat.setZFunc(CMaterial::less);
|
|
|
|
|
|
CPSLocated *loc;
|
|
uint32 index;
|
|
CPSLocatedBindable *lb;
|
|
_Owner->getOwner()->getCurrentEditedElement(loc, index, lb);
|
|
|
|
mat.setColor((lb == NULL || this == lb) && loc == _Owner && index == k ? CRGBA::Red : CRGBA(127, 127, 127));
|
|
|
|
|
|
CDRU::drawLinesUnlit(lines, mat, *getDriver() );
|
|
}
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::singleEmit(uint32 index, uint quantity)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_singleEmit)
|
|
nlassert(_Owner);
|
|
const uint32 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner,0) : _GenNb;
|
|
processEmitOutsideSimLoop(index, quantity * nbToGenerate);
|
|
}
|
|
|
|
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::processRegularEmissionWithNoLOD(uint firstInstanceIndex)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_processRegularEmissionWithNoLOD)
|
|
nlassert(_Owner);
|
|
nlassert(_Owner->getOwner());
|
|
//
|
|
const bool emitThreshold = _Owner->getOwner()->isEmitThresholdEnabled();
|
|
//
|
|
const uint size = _Owner->getSize();
|
|
nlassert(firstInstanceIndex < size);
|
|
uint leftToDo = size - firstInstanceIndex, toProcess;
|
|
float emitPeriod[EMITTER_BUFF_SIZE];
|
|
const float *currEmitPeriod;
|
|
uint currEmitPeriodPtrInc = _PeriodScheme ? 1 : 0;
|
|
sint32 nbToGenerate;
|
|
|
|
TPSAttribTime::iterator phaseIt = _Phase.begin() + firstInstanceIndex, endPhaseIt;
|
|
TPSAttribUInt8::iterator numEmitIt = _NumEmission.begin() + firstInstanceIndex;
|
|
|
|
// we don't use an iterator here
|
|
// because it could be invalidated if size change (a located could generate itself)
|
|
do
|
|
{
|
|
toProcess = leftToDo < EMITTER_BUFF_SIZE ? leftToDo : EMITTER_BUFF_SIZE;
|
|
|
|
|
|
if (_PeriodScheme)
|
|
{
|
|
// compute period
|
|
// NB : we ask to clamp entry because life counter of emitter are incremented, then spawn is called, and only after that dead emitters are removed
|
|
// so we may have a life counter that is > to 1
|
|
currEmitPeriod = (float *) (_PeriodScheme->make(_Owner, size - leftToDo, emitPeriod, sizeof(float), toProcess, true, 1 << 16, true));
|
|
if (emitThreshold)
|
|
{
|
|
|
|
/** Test if 'make' filled our buffer. If this is not the case, we assume that values where precomputed, and that
|
|
* all null period have already been replaced by the threshold
|
|
*/
|
|
if (currEmitPeriod == emitPeriod)
|
|
{
|
|
// if there possibility to have 0 in the scheme ?
|
|
if (_PeriodScheme->getMinValue() <= 0.f && _PeriodScheme->getMaxValue() >= 0.f)
|
|
{
|
|
replaceNullPeriodsByThreshold(emitPeriod, toProcess);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_Period != 0.f || !emitThreshold)
|
|
{
|
|
currEmitPeriod = &_Period;
|
|
}
|
|
else
|
|
{
|
|
currEmitPeriod = &EMIT_PERIOD_THRESHOLD;
|
|
}
|
|
}
|
|
|
|
endPhaseIt = phaseIt + toProcess;
|
|
|
|
if (_MaxEmissionCount == 0) // no emission count limit
|
|
{
|
|
/// is there an emission delay ?
|
|
if (_EmitDelay == 0.f) // no emission delay
|
|
{
|
|
do
|
|
{
|
|
*phaseIt += CParticleSystem::EllapsedTime;
|
|
if ( *phaseIt >= *currEmitPeriod) // phase is greater than period -> must emit
|
|
{
|
|
if (*currEmitPeriod != 0)
|
|
{
|
|
*phaseIt -= ::floorf(*phaseIt / *currEmitPeriod) * *currEmitPeriod;
|
|
}
|
|
const uint32 k = (uint32)(phaseIt - _Phase.begin());
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, k) : _GenNb;
|
|
processEmit(k, nbToGenerate);
|
|
}
|
|
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
}
|
|
while (phaseIt != endPhaseIt);
|
|
}
|
|
else // there's an emission delay
|
|
{
|
|
do
|
|
{
|
|
*phaseIt += CParticleSystem::EllapsedTime;
|
|
if ( *phaseIt >= *currEmitPeriod + _EmitDelay) // phase is greater than period -> must emit
|
|
{
|
|
if (*currEmitPeriod != 0)
|
|
{
|
|
*phaseIt -= ::floorf((*phaseIt - _EmitDelay) / *currEmitPeriod) * *currEmitPeriod;
|
|
}
|
|
const uint32 k = (uint32)(phaseIt - _Phase.begin());
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, k) : _GenNb;
|
|
processEmit(k, nbToGenerate);
|
|
}
|
|
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
}
|
|
while (phaseIt != endPhaseIt);
|
|
}
|
|
}
|
|
else // there's an emission count limit
|
|
{
|
|
/// is there an emission delay ?
|
|
if (_EmitDelay == 0.f) // no emission delay
|
|
{
|
|
do
|
|
{
|
|
if (*numEmitIt < _MaxEmissionCount)
|
|
{
|
|
*phaseIt += CParticleSystem::EllapsedTime;
|
|
if ( *phaseIt >= *currEmitPeriod) // phase is greater than period -> must emit
|
|
{
|
|
if (*currEmitPeriod != 0)
|
|
{
|
|
*phaseIt -= ::floorf(*phaseIt / *currEmitPeriod) * *currEmitPeriod;
|
|
}
|
|
const uint32 k = (uint32)(phaseIt - _Phase.begin());
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, k) : _GenNb;
|
|
processEmit(k, nbToGenerate);
|
|
++*numEmitIt;
|
|
}
|
|
}
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
++ numEmitIt;
|
|
}
|
|
while (phaseIt != endPhaseIt);
|
|
}
|
|
else // there's an emission delay
|
|
{
|
|
do
|
|
{
|
|
if (*numEmitIt < _MaxEmissionCount)
|
|
{
|
|
*phaseIt += CParticleSystem::EllapsedTime;
|
|
if ( *phaseIt >= *currEmitPeriod + _EmitDelay) // phase is greater than period -> must emit
|
|
{
|
|
if (*currEmitPeriod != 0)
|
|
{
|
|
*phaseIt -= ::floorf((*phaseIt - _EmitDelay) / *currEmitPeriod) * *currEmitPeriod;
|
|
}
|
|
const uint32 k = (uint32)(phaseIt - _Phase.begin());
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, k) : _GenNb;
|
|
processEmit(k, nbToGenerate);
|
|
++*numEmitIt;
|
|
}
|
|
}
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
++numEmitIt;
|
|
}
|
|
while (phaseIt != endPhaseIt);
|
|
}
|
|
}
|
|
|
|
leftToDo -= toProcess;
|
|
}
|
|
while (leftToDo);
|
|
}
|
|
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::processRegularEmission(uint firstInstanceIndex, float emitLOD)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_processRegularEmission)
|
|
nlassert(_Owner);
|
|
nlassert(_Owner->getOwner());
|
|
//
|
|
const bool emitThreshold = _Owner->getOwner()->isEmitThresholdEnabled();
|
|
//
|
|
const uint size = _Owner->getSize();
|
|
nlassert(firstInstanceIndex < size);
|
|
uint leftToDo = size - firstInstanceIndex, toProcess;
|
|
float emitPeriod[EMITTER_BUFF_SIZE];
|
|
const float *currEmitPeriod;
|
|
uint currEmitPeriodPtrInc = _PeriodScheme ? 1 : 0;
|
|
sint32 nbToGenerate;
|
|
|
|
TPSAttribTime::iterator phaseIt = _Phase.begin() + firstInstanceIndex, endPhaseIt;
|
|
TPSAttribUInt8::iterator numEmitIt = _NumEmission.begin() + firstInstanceIndex;
|
|
|
|
float ellapsedTimeLOD = emitLOD * CParticleSystem::EllapsedTime;
|
|
uint maxEmissionCountLOD = (uint8) (_MaxEmissionCount * emitLOD);
|
|
maxEmissionCountLOD = std::max(1u, maxEmissionCountLOD);
|
|
|
|
// we don't use an iterator here
|
|
// because it could be invalidated if size change (a located could generate itself)
|
|
do
|
|
{
|
|
toProcess = leftToDo < EMITTER_BUFF_SIZE ? leftToDo : EMITTER_BUFF_SIZE;
|
|
|
|
|
|
if (_PeriodScheme)
|
|
{
|
|
// compute period
|
|
// NB : we ask to clamp entry because life counter of emitter are incremented, then spawn is called, and only after that dead emitters are removed
|
|
// so we may have a life counter that is > to 1
|
|
currEmitPeriod = (float *) (_PeriodScheme->make(_Owner, size - leftToDo, emitPeriod, sizeof(float), toProcess, true, 1 << 16, true));
|
|
if (emitThreshold)
|
|
{
|
|
|
|
/** Test if 'make' filled our buffer. If this is not the case, we assume that values where precomputed, and that
|
|
* all null period have already been replaced by the threshold
|
|
*/
|
|
if (currEmitPeriod == emitPeriod)
|
|
{
|
|
// if there possibility to have 0 in the scheme ?
|
|
if (_PeriodScheme->getMinValue() <= 0.f && _PeriodScheme->getMaxValue() >= 0.f)
|
|
{
|
|
replaceNullPeriodsByThreshold(emitPeriod, toProcess);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_Period != 0.f || !emitThreshold)
|
|
{
|
|
currEmitPeriod = &_Period;
|
|
}
|
|
else
|
|
{
|
|
currEmitPeriod = &EMIT_PERIOD_THRESHOLD;
|
|
}
|
|
}
|
|
|
|
endPhaseIt = phaseIt + toProcess;
|
|
|
|
if (_MaxEmissionCount == 0) // no emission count limit
|
|
{
|
|
/// is there an emission delay ?
|
|
if (_EmitDelay == 0.f) // no emission delay
|
|
{
|
|
do
|
|
{
|
|
*phaseIt += ellapsedTimeLOD;
|
|
if ( *phaseIt >= *currEmitPeriod) // phase is greater than period -> must emit
|
|
{
|
|
if (*currEmitPeriod != 0)
|
|
{
|
|
*phaseIt -= ::floorf(*phaseIt / *currEmitPeriod) * *currEmitPeriod;
|
|
}
|
|
const uint32 k = (uint32)(phaseIt - _Phase.begin());
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, k) : _GenNb;
|
|
if (nbToGenerate)
|
|
{
|
|
nbToGenerate = (sint32) (emitLOD * nbToGenerate);
|
|
if (!nbToGenerate) nbToGenerate = 1;
|
|
processEmit(k, nbToGenerate);
|
|
}
|
|
}
|
|
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
}
|
|
while (phaseIt != endPhaseIt);
|
|
}
|
|
else // there's an emission delay
|
|
{
|
|
do
|
|
{
|
|
if (*phaseIt < _EmitDelay)
|
|
{
|
|
*phaseIt += CParticleSystem::EllapsedTime;
|
|
if (*phaseIt < _EmitDelay)
|
|
{
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
*phaseIt = (*phaseIt - _EmitDelay) * emitLOD + _EmitDelay;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*phaseIt += ellapsedTimeLOD;
|
|
}
|
|
|
|
if ( *phaseIt >= *currEmitPeriod + _EmitDelay) // phase is greater than period -> must emit
|
|
{
|
|
if (*currEmitPeriod != 0)
|
|
{
|
|
*phaseIt -= ::floorf((*phaseIt - _EmitDelay) / *currEmitPeriod) * *currEmitPeriod;
|
|
}
|
|
const uint32 k = (uint32)(phaseIt - _Phase.begin());
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, k) : _GenNb;
|
|
if (nbToGenerate)
|
|
{
|
|
nbToGenerate = (sint32) (emitLOD * nbToGenerate);
|
|
if (!nbToGenerate) nbToGenerate = 1;
|
|
processEmit(k, nbToGenerate);
|
|
}
|
|
}
|
|
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
}
|
|
while (phaseIt != endPhaseIt);
|
|
}
|
|
}
|
|
else // there's an emission count limit
|
|
{
|
|
/// is there an emission delay ?
|
|
if (_EmitDelay == 0.f) // no emission delay
|
|
{
|
|
do
|
|
{
|
|
if (*numEmitIt < maxEmissionCountLOD)
|
|
{
|
|
*phaseIt += ellapsedTimeLOD;
|
|
if ( *phaseIt >= *currEmitPeriod) // phase is greater than period -> must emit
|
|
{
|
|
if (*currEmitPeriod != 0)
|
|
{
|
|
*phaseIt -= ::floorf(*phaseIt / *currEmitPeriod) * *currEmitPeriod;
|
|
}
|
|
const uint32 k = (uint32)(phaseIt - _Phase.begin());
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, k) : _GenNb;
|
|
if (nbToGenerate)
|
|
{
|
|
nbToGenerate = (sint32) (emitLOD * nbToGenerate);
|
|
if (!nbToGenerate) nbToGenerate = 1;
|
|
processEmit(k, nbToGenerate);
|
|
}
|
|
++*numEmitIt;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*numEmitIt = _MaxEmissionCount;
|
|
}
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
++ numEmitIt;
|
|
}
|
|
while (phaseIt != endPhaseIt);
|
|
}
|
|
else // there's an emission delay
|
|
{
|
|
do
|
|
{
|
|
if (*numEmitIt < maxEmissionCountLOD)
|
|
{
|
|
if (*phaseIt < _EmitDelay)
|
|
{
|
|
*phaseIt += CParticleSystem::EllapsedTime;
|
|
if (*phaseIt < _EmitDelay)
|
|
{
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
++numEmitIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
*phaseIt = (*phaseIt - _EmitDelay) * emitLOD + _EmitDelay;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*phaseIt += ellapsedTimeLOD;
|
|
}
|
|
|
|
if ( *phaseIt >= *currEmitPeriod + _EmitDelay) // phase is greater than period -> must emit
|
|
{
|
|
if (*currEmitPeriod != 0)
|
|
{
|
|
*phaseIt -= ::floorf((*phaseIt - _EmitDelay) / *currEmitPeriod) * *currEmitPeriod;
|
|
}
|
|
const uint32 k = (uint32)(phaseIt - _Phase.begin());
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, k) : _GenNb;
|
|
if (nbToGenerate)
|
|
{
|
|
nbToGenerate = (sint32) (nbToGenerate * emitLOD);
|
|
if (!nbToGenerate) nbToGenerate = 1;
|
|
processEmit(k, nbToGenerate);
|
|
}
|
|
++*numEmitIt;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*numEmitIt = _MaxEmissionCount;
|
|
}
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
++numEmitIt;
|
|
}
|
|
while (phaseIt != endPhaseIt);
|
|
}
|
|
}
|
|
|
|
leftToDo -= toProcess;
|
|
}
|
|
while (leftToDo);
|
|
}
|
|
|
|
/// private : generate the various position of an emitter in the given tab for the given slice of time,
|
|
// depending on whether its motion is parametric or incremental. This is used to create emittees at the right position
|
|
|
|
static
|
|
#ifndef NL_DEBUG
|
|
inline
|
|
#endif
|
|
uint GenEmitterPositions(CPSLocated *emitter,
|
|
CPSLocated *emittee,
|
|
uint emitterIndex,
|
|
uint numStep,
|
|
TAnimationTime deltaT, /* fraction of time needed to reach the first emission */
|
|
TAnimationTime step,
|
|
std::vector<NLMISC::CVector> &dest
|
|
)
|
|
{
|
|
NL_PS_FUNC(GenEmitterPositions)
|
|
const uint toProcess = std::max(1U, std::min(numStep, (uint) emittee->getMaxSize()));
|
|
dest.resize(toProcess);
|
|
|
|
|
|
if (!emitter->isParametricMotionEnabled()) // standard case : take current pos and integrate
|
|
{
|
|
if (toProcess == 1) // only one emission -> takes current pos
|
|
{
|
|
dest[0] = emitter->getPos()[emitterIndex] - deltaT * emitter->getSpeed()[emitterIndex];
|
|
}
|
|
else
|
|
{
|
|
std::vector<NLMISC::CVector>::iterator outIt = dest.end();
|
|
std::vector<NLMISC::CVector>::iterator endIt = dest.begin();
|
|
NLMISC::CVector pos = emitter->getPos()[emitterIndex] - deltaT * emitter->getSpeed()[emitterIndex];
|
|
NLMISC::CVector speed = step * emitter->getSpeed()[emitterIndex];
|
|
do
|
|
{
|
|
-- outIt;
|
|
*outIt = pos;
|
|
pos -= speed;
|
|
}
|
|
while (outIt != endIt);
|
|
}
|
|
}
|
|
else // compute parametric trajectory
|
|
{
|
|
emitter->integrateSingle(emitter->getOwner()->getSystemDate() + CParticleSystem::RealEllapsedTime - deltaT,
|
|
-step,
|
|
toProcess,
|
|
emitterIndex,
|
|
&dest[0]
|
|
);
|
|
}
|
|
|
|
return toProcess;
|
|
}
|
|
|
|
|
|
/** The same as GenEmitterPositions, but with LOD taken in account.
|
|
*/
|
|
static inline uint GenEmitterPositionsWithLOD(CPSLocated *emitter,
|
|
CPSLocated *emittee,
|
|
uint emitterIndex,
|
|
uint numStep,
|
|
TAnimationTime deltaT, /* fraction of time needed to reach the first emission */
|
|
TAnimationTime step,
|
|
float invLODRatio,
|
|
std::vector<NLMISC::CVector> &dest
|
|
)
|
|
{
|
|
NL_PS_FUNC(GenEmitterPositionsWithLOD)
|
|
const uint toProcess = std::max(1U, std::min(numStep, (uint) emittee->getMaxSize()));
|
|
dest.resize(toProcess);
|
|
|
|
|
|
if (!emitter->isParametricMotionEnabled()) // standard case : take current pos and integrate
|
|
{
|
|
if (toProcess == 1) // only one emission -> takes current pos
|
|
{
|
|
dest[0] = emitter->getPos()[emitterIndex] - deltaT * emitter->getSpeed()[emitterIndex];
|
|
}
|
|
else
|
|
{
|
|
std::vector<NLMISC::CVector>::iterator outIt = dest.end();
|
|
std::vector<NLMISC::CVector>::iterator endIt = dest.begin();
|
|
NLMISC::CVector pos = emitter->getPos()[emitterIndex] - deltaT * emitter->getSpeed()[emitterIndex];
|
|
NLMISC::CVector speed = step * invLODRatio * emitter->getSpeed()[emitterIndex];
|
|
do
|
|
{
|
|
-- outIt;
|
|
*outIt = pos;
|
|
pos -= speed;
|
|
}
|
|
while (outIt != endIt);
|
|
}
|
|
}
|
|
else // compute parametric trajectory
|
|
{
|
|
emitter->integrateSingle(emitter->getOwner()->getSystemDate() - deltaT - step * toProcess,
|
|
step,
|
|
toProcess,
|
|
emitterIndex,
|
|
&dest[0]
|
|
);
|
|
}
|
|
|
|
return toProcess;
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::processRegularEmissionConsistent(uint firstInstanceIndex, float emitLOD, float inverseEmitLOD)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_processRegularEmissionConsistent)
|
|
/// hmm some code factorisation would do no harm, but we want to keep tests outside the loops as much as possible...
|
|
|
|
nlassert(_Owner);
|
|
nlassert(_Owner->getOwner());
|
|
//
|
|
const bool emitThreshold = _Owner->getOwner()->isEmitThresholdEnabled();
|
|
//
|
|
|
|
|
|
static std::vector<NLMISC::CVector> emitterPositions;
|
|
// Positions for the emitter. They are computed by using a parametric trajectory or by using integration
|
|
|
|
const uint size = _Owner->getSize();
|
|
nlassert(firstInstanceIndex < size);
|
|
uint leftToDo = size - firstInstanceIndex, toProcess;
|
|
float emitPeriod[EMITTER_BUFF_SIZE];
|
|
const float *currEmitPeriod;
|
|
uint currEmitPeriodPtrInc = _PeriodScheme ? 1 : 0;
|
|
sint32 nbToGenerate;
|
|
|
|
|
|
TPSAttribTime::iterator phaseIt = _Phase.begin() + firstInstanceIndex, endPhaseIt;
|
|
TPSAttribUInt8::iterator numEmitIt;
|
|
if(firstInstanceIndex < _NumEmission.getSize())
|
|
{
|
|
numEmitIt = _NumEmission.begin() + firstInstanceIndex;
|
|
}
|
|
else
|
|
{
|
|
numEmitIt = _NumEmission.end();
|
|
}
|
|
|
|
|
|
float ellapsedTimeLOD = CParticleSystem::EllapsedTime * emitLOD;
|
|
uint maxEmissionCountLOD = (uint8) (_MaxEmissionCount * emitLOD);
|
|
maxEmissionCountLOD = std::max(1u, maxEmissionCountLOD);
|
|
// we don't use an iterator here
|
|
// because it could be invalidated if size change (a located could generate itself)
|
|
do
|
|
{
|
|
toProcess = leftToDo < EMITTER_BUFF_SIZE ? leftToDo : EMITTER_BUFF_SIZE;
|
|
|
|
|
|
if (_PeriodScheme)
|
|
{
|
|
// compute period
|
|
// NB : we ask to clamp entry because life counter of emitter are incremented, then spawn is called, and only after that dead emitters are removed
|
|
// so we may have a life counter that is > to 1
|
|
currEmitPeriod = (float *) (_PeriodScheme->make(_Owner, size - leftToDo, emitPeriod, sizeof(float), toProcess, true, 1 << 16, true));
|
|
if (emitThreshold)
|
|
{
|
|
|
|
/** Test if 'make' filled our buffer. If this is not the case, we assume that values where precomputed, and that
|
|
* all null period have already been replaced by the threshold
|
|
*/
|
|
if (currEmitPeriod == emitPeriod)
|
|
{
|
|
// if there possibility to have 0 in the scheme ?
|
|
if (_PeriodScheme->getMinValue() <= 0.f && _PeriodScheme->getMaxValue() >= 0.f)
|
|
{
|
|
replaceNullPeriodsByThreshold(emitPeriod, toProcess);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_Period != 0.f || !emitThreshold)
|
|
{
|
|
currEmitPeriod = &_Period;
|
|
}
|
|
else
|
|
{
|
|
currEmitPeriod = &EMIT_PERIOD_THRESHOLD;
|
|
}
|
|
}
|
|
|
|
endPhaseIt = phaseIt + toProcess;
|
|
|
|
if (_MaxEmissionCount == 0) // no emission count limit
|
|
{
|
|
/// is there an emission delay ?
|
|
if (_EmitDelay == 0.f) // no emission delay
|
|
{
|
|
do
|
|
{
|
|
*phaseIt += ellapsedTimeLOD;
|
|
if ( *phaseIt >= *currEmitPeriod) // phase is greater than period -> must emit
|
|
{
|
|
if (*currEmitPeriod != 0)
|
|
{
|
|
/** Must ensure phase is valid if period decrease over time
|
|
*/
|
|
*phaseIt = std::min(*phaseIt, *currEmitPeriod + ellapsedTimeLOD);
|
|
//
|
|
/// compute the number of emissions
|
|
uint numEmissions = (uint) ::floorf(*phaseIt / *currEmitPeriod);
|
|
*phaseIt -= *currEmitPeriod * numEmissions;
|
|
|
|
uint emitterIndex = (uint)(phaseIt - _Phase.begin());
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
|
|
if (nbToGenerate)
|
|
{
|
|
float deltaT = std::max(0.f, *phaseIt);
|
|
|
|
/// compute the position of the emitter for the needed dates
|
|
numEmissions = GenEmitterPositionsWithLOD(_Owner,
|
|
_EmittedType,
|
|
emitterIndex,
|
|
numEmissions,
|
|
deltaT,
|
|
*currEmitPeriod,
|
|
inverseEmitLOD,
|
|
emitterPositions
|
|
);
|
|
|
|
/// process each emission at the right pos at the right date
|
|
nbToGenerate = (sint32) (emitLOD * nbToGenerate);
|
|
if (!nbToGenerate) nbToGenerate = 1;
|
|
uint k = numEmissions;
|
|
float deltaTInc = *currEmitPeriod * inverseEmitLOD;
|
|
do
|
|
{
|
|
--k;
|
|
processEmitConsistent(emitterPositions[k],
|
|
emitterIndex,
|
|
nbToGenerate,
|
|
deltaT
|
|
);
|
|
deltaT += deltaTInc;
|
|
}
|
|
while (k);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const uint32 emitterIndex = (uint32)(phaseIt - _Phase.begin());
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
|
|
if (nbToGenerate)
|
|
{
|
|
nbToGenerate = (sint32) (emitLOD * nbToGenerate);
|
|
if (!nbToGenerate) nbToGenerate = 1;
|
|
processEmit(emitterIndex, nbToGenerate);
|
|
}
|
|
}
|
|
}
|
|
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
}
|
|
while (phaseIt != endPhaseIt);
|
|
}
|
|
else // thhere's an emission delay
|
|
{
|
|
do
|
|
{
|
|
if (*phaseIt < _EmitDelay)
|
|
{
|
|
*phaseIt += CParticleSystem::EllapsedTime;
|
|
if (*phaseIt < _EmitDelay)
|
|
{
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
*phaseIt = (*phaseIt - _EmitDelay) * emitLOD + _EmitDelay;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*phaseIt += ellapsedTimeLOD;
|
|
}
|
|
|
|
if ( *phaseIt >= *currEmitPeriod + _EmitDelay) // phase is greater than period -> must emit
|
|
{
|
|
if (*currEmitPeriod != 0)
|
|
{
|
|
/** Must ensure phase is valid if period decrease over time
|
|
*/
|
|
*phaseIt = std::min(*phaseIt, *currEmitPeriod + ellapsedTimeLOD + _EmitDelay);
|
|
//
|
|
uint numEmissions = (uint) ::floorf((*phaseIt - _EmitDelay) / *currEmitPeriod);
|
|
*phaseIt -= *currEmitPeriod * numEmissions;
|
|
float deltaT = std::max(*phaseIt - _EmitDelay, 0.f);
|
|
//nlassert(deltaT >= 0.f);
|
|
/// process each emission at the right pos at the right date
|
|
uint emitterIndex = (uint)(phaseIt - _Phase.begin());
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
|
|
if (nbToGenerate)
|
|
{
|
|
|
|
/// compute the position of the emitter for the needed date
|
|
numEmissions = GenEmitterPositionsWithLOD( _Owner,
|
|
_EmittedType,
|
|
emitterIndex,
|
|
numEmissions,
|
|
deltaT,
|
|
*currEmitPeriod,
|
|
inverseEmitLOD,
|
|
emitterPositions
|
|
);
|
|
|
|
nbToGenerate = (sint32) (emitLOD * nbToGenerate);
|
|
if (!nbToGenerate) nbToGenerate = 1;
|
|
uint k = numEmissions;
|
|
float deltaTInc = *currEmitPeriod * inverseEmitLOD;
|
|
do
|
|
{
|
|
--k;
|
|
processEmitConsistent(emitterPositions[k],
|
|
emitterIndex,
|
|
nbToGenerate,
|
|
deltaT);
|
|
deltaT += deltaTInc;
|
|
}
|
|
while (k);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const uint32 emitterIndex = (uint32)(phaseIt - _Phase.begin());
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
|
|
if (nbToGenerate)
|
|
{
|
|
nbToGenerate = (sint32) (emitLOD * nbToGenerate);
|
|
if (!nbToGenerate) nbToGenerate = 1;
|
|
processEmit(emitterIndex, nbToGenerate);
|
|
}
|
|
}
|
|
}
|
|
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
}
|
|
while (phaseIt != endPhaseIt);
|
|
}
|
|
}
|
|
else // there's an emission count limit
|
|
{
|
|
/// is there an emission delay ?
|
|
if (_EmitDelay == 0.f) // no emission delay
|
|
{
|
|
do
|
|
{
|
|
if (*numEmitIt < maxEmissionCountLOD)
|
|
{
|
|
*phaseIt += ellapsedTimeLOD;
|
|
if ( *phaseIt >= *currEmitPeriod) // phase is greater than period -> must emit
|
|
{
|
|
if (*currEmitPeriod != 0)
|
|
{
|
|
/** Must ensure phase is valid if period decrease over time
|
|
*/
|
|
*phaseIt = std::min(*phaseIt, *currEmitPeriod + ellapsedTimeLOD);
|
|
//
|
|
uint numEmissions = (uint) ::floorf(*phaseIt / *currEmitPeriod);
|
|
*numEmitIt += numEmissions;
|
|
*phaseIt -= *currEmitPeriod * numEmissions;
|
|
float deltaT = std::max(*phaseIt, 0.f);
|
|
//nlassert(deltaT >= 0.f);
|
|
uint emitterIndex = (uint)(phaseIt - _Phase.begin());
|
|
if (*numEmitIt > _MaxEmissionCount) // make sure we don't go over the emission limit
|
|
{
|
|
numEmissions -= *numEmitIt - _MaxEmissionCount;
|
|
*numEmitIt = _MaxEmissionCount;
|
|
}
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
|
|
if (nbToGenerate)
|
|
{
|
|
/// compute the position of the emitter for the needed date
|
|
numEmissions = GenEmitterPositionsWithLOD(_Owner,
|
|
_EmittedType,
|
|
emitterIndex,
|
|
numEmissions,
|
|
deltaT,
|
|
*currEmitPeriod,
|
|
inverseEmitLOD,
|
|
emitterPositions
|
|
);
|
|
uint k = numEmissions;
|
|
/// process each emission at the right pos at the right date
|
|
nbToGenerate = (sint32) (emitLOD * nbToGenerate);
|
|
if (!nbToGenerate) nbToGenerate = 1;
|
|
float deltaTInc = *currEmitPeriod * inverseEmitLOD;
|
|
do
|
|
{
|
|
--k;
|
|
processEmitConsistent(emitterPositions[k],
|
|
emitterIndex,
|
|
nbToGenerate,
|
|
deltaT
|
|
);
|
|
deltaT += deltaTInc;
|
|
}
|
|
while (k);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const uint32 emitterIndex = (uint32)(phaseIt - _Phase.begin());
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
|
|
if (nbToGenerate)
|
|
{
|
|
nbToGenerate = (sint32) (emitLOD * nbToGenerate);
|
|
if (!nbToGenerate) nbToGenerate = 1;
|
|
processEmit(emitterIndex, nbToGenerate);
|
|
++*numEmitIt;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*numEmitIt = _MaxEmissionCount; // if the lod change, must ensure that the
|
|
}
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
++ numEmitIt;
|
|
}
|
|
while (phaseIt != endPhaseIt);
|
|
}
|
|
else // there's an emission delay
|
|
{
|
|
do
|
|
{
|
|
if (*numEmitIt < maxEmissionCountLOD)
|
|
{
|
|
if (*phaseIt < _EmitDelay)
|
|
{
|
|
*phaseIt += CParticleSystem::EllapsedTime;
|
|
if (*phaseIt < _EmitDelay)
|
|
{
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
++numEmitIt;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
*phaseIt = (*phaseIt - _EmitDelay) * emitLOD + _EmitDelay;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*phaseIt += ellapsedTimeLOD;
|
|
}
|
|
//
|
|
if ( *phaseIt >= *currEmitPeriod + _EmitDelay) // phase is greater than period -> must emit
|
|
{
|
|
if (*currEmitPeriod != 0)
|
|
{
|
|
/** Must ensure phase is valid if period decrease over time
|
|
*/
|
|
*phaseIt = std::min(*phaseIt, *currEmitPeriod + ellapsedTimeLOD + _EmitDelay);
|
|
//
|
|
uint numEmissions = (uint) ::floorf((*phaseIt - _EmitDelay) / *currEmitPeriod);
|
|
*numEmitIt += numEmissions;
|
|
*phaseIt -= *currEmitPeriod * numEmissions;
|
|
float deltaT = std::max(*phaseIt - _EmitDelay, 0.f);
|
|
//nlassert(deltaT >= 0.f);
|
|
uint emitterIndex = (uint)(phaseIt - _Phase.begin());
|
|
if (*numEmitIt > _MaxEmissionCount) // make sure we don't go over the emission limit
|
|
{
|
|
numEmissions -= *numEmitIt - _MaxEmissionCount;
|
|
*numEmitIt = _MaxEmissionCount;
|
|
}
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
|
|
if (nbToGenerate)
|
|
{
|
|
/// compute the position of the emitter for the needed date
|
|
numEmissions = GenEmitterPositionsWithLOD(_Owner,
|
|
_EmittedType,
|
|
emitterIndex,
|
|
numEmissions,
|
|
deltaT,
|
|
*currEmitPeriod,
|
|
inverseEmitLOD,
|
|
emitterPositions
|
|
);
|
|
uint k = numEmissions;
|
|
/// process each emission at the right pos at the right date
|
|
nbToGenerate = (sint32) (emitLOD * nbToGenerate);
|
|
if (!nbToGenerate) nbToGenerate = 1;
|
|
float deltaTInc = *currEmitPeriod * inverseEmitLOD;
|
|
do
|
|
{
|
|
--k;
|
|
processEmitConsistent(emitterPositions[k],
|
|
emitterIndex,
|
|
nbToGenerate,
|
|
deltaT);
|
|
deltaT += deltaTInc;
|
|
}
|
|
while (k);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const uint32 emitterIndex = (uint32)(phaseIt - _Phase.begin());
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
|
|
if (nbToGenerate)
|
|
{
|
|
nbToGenerate = (sint32) (emitLOD * nbToGenerate);
|
|
if (!nbToGenerate) nbToGenerate = 1;
|
|
processEmit(emitterIndex, nbToGenerate);
|
|
++*numEmitIt;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*numEmitIt = _MaxEmissionCount; // if the lod change, must ensure that the
|
|
}
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
++numEmitIt;
|
|
}
|
|
while (phaseIt != endPhaseIt);
|
|
}
|
|
}
|
|
|
|
leftToDo -= toProcess;
|
|
}
|
|
while (leftToDo);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::processRegularEmissionConsistentWithNoLOD(uint firstInstanceIndex)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_processRegularEmissionConsistentWithNoLOD)
|
|
/// hum, some code factorization would do no harm, but we want to keep tests outside the loops as much as possible...
|
|
|
|
nlassert(_Owner);
|
|
nlassert(_Owner->getOwner());
|
|
//
|
|
const bool emitThreshold = _Owner->getOwner()->isEmitThresholdEnabled();
|
|
//
|
|
|
|
|
|
static std::vector<NLMISC::CVector> emitterPositions;
|
|
// Positions for the emitter. They are computed by using a parametric trajectory or by using integration
|
|
|
|
const uint size = _Owner->getSize();
|
|
nlassert(firstInstanceIndex < size);
|
|
uint leftToDo = size - firstInstanceIndex, toProcess;
|
|
float emitPeriod[EMITTER_BUFF_SIZE];
|
|
const float *currEmitPeriod;
|
|
uint currEmitPeriodPtrInc = _PeriodScheme ? 1 : 0;
|
|
sint32 nbToGenerate;
|
|
|
|
|
|
TPSAttribTime::iterator phaseIt = _Phase.begin() + firstInstanceIndex, endPhaseIt;
|
|
TPSAttribUInt8::iterator numEmitIt;
|
|
if (firstInstanceIndex < _NumEmission.getSize())
|
|
numEmitIt = _NumEmission.begin() + firstInstanceIndex;
|
|
else
|
|
numEmitIt = _NumEmission.end();
|
|
do
|
|
{
|
|
toProcess = leftToDo < EMITTER_BUFF_SIZE ? leftToDo : EMITTER_BUFF_SIZE;
|
|
|
|
|
|
if (_PeriodScheme)
|
|
{
|
|
// compute period
|
|
// NB : we ask to clamp entry because life counter of emitter are incremented, then spawn is called, and only after that dead emitters are removed
|
|
// so we may have a life counter that is > to 1
|
|
currEmitPeriod = (float *) (_PeriodScheme->make(_Owner, size - leftToDo, emitPeriod, sizeof(float), toProcess, true, 1 << 16, true));
|
|
if (emitThreshold)
|
|
{
|
|
|
|
/** Test if 'make' filled our buffer. If this is not the case, we assume that values where precomputed, and that
|
|
* all null period have already been replaced by the threshold
|
|
*/
|
|
if (currEmitPeriod == emitPeriod)
|
|
{
|
|
// if there possibility to have 0 in the scheme ?
|
|
if (_PeriodScheme->getMinValue() <= 0.f && _PeriodScheme->getMaxValue() >= 0.f)
|
|
{
|
|
replaceNullPeriodsByThreshold(emitPeriod, toProcess);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_Period != 0.f || !emitThreshold)
|
|
{
|
|
currEmitPeriod = &_Period;
|
|
}
|
|
else
|
|
{
|
|
currEmitPeriod = &EMIT_PERIOD_THRESHOLD;
|
|
}
|
|
}
|
|
|
|
endPhaseIt = phaseIt + toProcess;
|
|
|
|
if (_MaxEmissionCount == 0) // no emission count limit
|
|
{
|
|
/// is there an emission delay ?
|
|
if (_EmitDelay == 0.f) // no emission delay
|
|
{
|
|
do
|
|
{
|
|
*phaseIt += CParticleSystem::EllapsedTime;
|
|
if ( *phaseIt >= *currEmitPeriod) // phase is greater than period -> must emit
|
|
{
|
|
if (*currEmitPeriod != 0)
|
|
{
|
|
/** Must ensure phase is valid if period decrease over time
|
|
*/
|
|
*phaseIt = std::min(*phaseIt, *currEmitPeriod + CParticleSystem::EllapsedTime);
|
|
//
|
|
/// compute the number of emissions
|
|
uint numEmissions = (uint) ::floorf(*phaseIt / *currEmitPeriod);
|
|
*phaseIt -= *currEmitPeriod * numEmissions;
|
|
float deltaT = std::max(0.f, *phaseIt);
|
|
//nlassert(deltaT >= 0.f);
|
|
uint emitterIndex = (uint)(phaseIt - _Phase.begin());
|
|
|
|
/// compute the position of the emitter for the needed dates
|
|
numEmissions = GenEmitterPositions(_Owner,
|
|
_EmittedType,
|
|
emitterIndex,
|
|
numEmissions,
|
|
deltaT,
|
|
*currEmitPeriod,
|
|
emitterPositions
|
|
);
|
|
|
|
/// process each emission at the right pos at the right date
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
|
|
uint k = numEmissions;
|
|
do
|
|
{
|
|
--k;
|
|
processEmitConsistent(emitterPositions[k],
|
|
emitterIndex,
|
|
nbToGenerate,
|
|
deltaT);
|
|
deltaT += *currEmitPeriod;
|
|
}
|
|
while (k);
|
|
}
|
|
else
|
|
{
|
|
const uint32 emitterIndex = (uint32)(phaseIt - _Phase.begin());
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
|
|
processEmit(emitterIndex, nbToGenerate);
|
|
}
|
|
}
|
|
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
}
|
|
while (phaseIt != endPhaseIt);
|
|
}
|
|
else // thhere's an emission delay
|
|
{
|
|
do
|
|
{
|
|
*phaseIt += CParticleSystem::EllapsedTime;
|
|
if ( *phaseIt >= *currEmitPeriod + _EmitDelay) // phase is greater than period -> must emit
|
|
{
|
|
if (*currEmitPeriod != 0)
|
|
{
|
|
/** Must ensure phase is valid if period decrease over time
|
|
*/
|
|
*phaseIt = std::min(*phaseIt, *currEmitPeriod + CParticleSystem::EllapsedTime + _EmitDelay);
|
|
//
|
|
uint numEmissions = (uint) ::floorf((*phaseIt - _EmitDelay) / *currEmitPeriod);
|
|
*phaseIt -= *currEmitPeriod * numEmissions;
|
|
float deltaT = std::max(*phaseIt - _EmitDelay, 0.f);
|
|
//nlassert(deltaT >= 0.f);
|
|
|
|
uint emitterIndex = (uint)(phaseIt - _Phase.begin());
|
|
/// compute the position of the emitter for the needed date
|
|
numEmissions = GenEmitterPositions(_Owner,
|
|
_EmittedType,
|
|
emitterIndex,
|
|
numEmissions,
|
|
deltaT,
|
|
*currEmitPeriod,
|
|
emitterPositions
|
|
);
|
|
/// process each emission at the right pos at the right date
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
|
|
uint k = numEmissions;
|
|
do
|
|
{
|
|
--k;
|
|
processEmitConsistent(emitterPositions[k],
|
|
emitterIndex,
|
|
nbToGenerate,
|
|
deltaT);
|
|
deltaT += *currEmitPeriod;
|
|
}
|
|
while (k);
|
|
}
|
|
else
|
|
{
|
|
const uint32 emitterIndex = (uint32)(phaseIt - _Phase.begin());
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
|
|
processEmit(emitterIndex, nbToGenerate);
|
|
}
|
|
}
|
|
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
}
|
|
while (phaseIt != endPhaseIt);
|
|
}
|
|
}
|
|
else // there's an emission count limit
|
|
{
|
|
/// is there an emission delay ?
|
|
if (_EmitDelay == 0.f) // no emission delay
|
|
{
|
|
do
|
|
{
|
|
if (*numEmitIt < _MaxEmissionCount)
|
|
{
|
|
*phaseIt += CParticleSystem::EllapsedTime;
|
|
if ( *phaseIt >= *currEmitPeriod) // phase is greater than period -> must emit
|
|
{
|
|
if (*currEmitPeriod != 0)
|
|
{
|
|
/** Must ensure phase is valid if period decrease over time
|
|
*/
|
|
*phaseIt = std::min(*phaseIt, *currEmitPeriod + CParticleSystem::EllapsedTime);
|
|
//
|
|
uint numEmissions = (uint) ::floorf(*phaseIt / *currEmitPeriod);
|
|
*numEmitIt += numEmissions;
|
|
*phaseIt -= *currEmitPeriod * numEmissions;
|
|
float deltaT = std::max(*phaseIt, 0.f);
|
|
//nlassert(deltaT >= 0.f);
|
|
uint emitterIndex = (uint)(phaseIt - _Phase.begin());
|
|
if (*numEmitIt > _MaxEmissionCount) // make sure we don't go over the emission limit
|
|
{
|
|
numEmissions -= *numEmitIt - _MaxEmissionCount;
|
|
*numEmitIt = _MaxEmissionCount;
|
|
}
|
|
/// compute the position of the emitter for the needed date
|
|
numEmissions = GenEmitterPositions(_Owner,
|
|
_EmittedType,
|
|
emitterIndex,
|
|
numEmissions,
|
|
deltaT,
|
|
*currEmitPeriod,
|
|
emitterPositions
|
|
);
|
|
uint k = numEmissions;
|
|
/// process each emission at the right pos at the right date
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
|
|
do
|
|
{
|
|
--k;
|
|
processEmitConsistent(emitterPositions[k],
|
|
emitterIndex,
|
|
nbToGenerate,
|
|
deltaT);
|
|
deltaT += *currEmitPeriod;
|
|
}
|
|
while (k);
|
|
}
|
|
else
|
|
{
|
|
const uint32 emitterIndex = (uint32)(phaseIt - _Phase.begin());
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
|
|
processEmit(emitterIndex, nbToGenerate);
|
|
++*numEmitIt;
|
|
}
|
|
}
|
|
}
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
++ numEmitIt;
|
|
}
|
|
while (phaseIt != endPhaseIt);
|
|
}
|
|
else // there's an emission delay
|
|
{
|
|
do
|
|
{
|
|
if (*numEmitIt < _MaxEmissionCount)
|
|
{
|
|
*phaseIt += CParticleSystem::EllapsedTime;
|
|
if ( *phaseIt >= *currEmitPeriod + _EmitDelay) // phase is greater than period -> must emit
|
|
{
|
|
if (*currEmitPeriod != 0)
|
|
{
|
|
/** Must ensure phase is valid if period decrease over time
|
|
*/
|
|
*phaseIt = std::min(*phaseIt, *currEmitPeriod + CParticleSystem::EllapsedTime + _EmitDelay);
|
|
//
|
|
uint numEmissions = (uint) ::floorf((*phaseIt - _EmitDelay) / *currEmitPeriod);
|
|
*numEmitIt += numEmissions;
|
|
*phaseIt -= *currEmitPeriod * numEmissions;
|
|
float deltaT = std::max(*phaseIt - _EmitDelay, 0.f);
|
|
//nlassert(deltaT >= 0.f);
|
|
uint emitterIndex = (uint)(phaseIt - _Phase.begin());
|
|
if (*numEmitIt > _MaxEmissionCount) // make sure we don't go over the emission limit
|
|
{
|
|
numEmissions -= *numEmitIt - _MaxEmissionCount;
|
|
*numEmitIt = _MaxEmissionCount;
|
|
}
|
|
/// compute the position of the emitter for the needed date
|
|
numEmissions = GenEmitterPositions(_Owner,
|
|
_EmittedType,
|
|
emitterIndex,
|
|
numEmissions,
|
|
deltaT,
|
|
*currEmitPeriod,
|
|
emitterPositions
|
|
);
|
|
uint k = numEmissions;
|
|
/// process each emission at the right pos at the right date
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
|
|
do
|
|
{
|
|
--k;
|
|
processEmitConsistent(emitterPositions[k],
|
|
emitterIndex,
|
|
nbToGenerate,
|
|
deltaT);
|
|
deltaT += *currEmitPeriod;
|
|
}
|
|
while (k);
|
|
}
|
|
else
|
|
{
|
|
const uint32 emitterIndex = (uint32)(phaseIt - _Phase.begin());
|
|
nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, emitterIndex) : _GenNb;
|
|
processEmit(emitterIndex, nbToGenerate);
|
|
++*numEmitIt;
|
|
}
|
|
}
|
|
}
|
|
++phaseIt;
|
|
currEmitPeriod += currEmitPeriodPtrInc;
|
|
++numEmitIt;
|
|
}
|
|
while (phaseIt != endPhaseIt);
|
|
}
|
|
}
|
|
|
|
leftToDo -= toProcess;
|
|
}
|
|
while (leftToDo);
|
|
}
|
|
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::step(TPSProcessPass pass)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_step)
|
|
if (pass == PSToolRender)
|
|
{
|
|
showTool();
|
|
}
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::computeSpawns(uint firstInstanceIndex)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_computeSpawns)
|
|
if (!_EmittedType) return;
|
|
nlassert(CParticleSystem::InsideSimLoop);
|
|
const uint32 size = _Owner->getSize();
|
|
if (!size) return;
|
|
if (CParticleSystem::EllapsedTime == 0.f) return; // do nothing when paused
|
|
CParticleSystem *ps = _Owner->getOwner();
|
|
nlassert(ps);
|
|
float emitLOD;
|
|
if (ps->isAutoLODEnabled() && !ps->isSharingEnabled() && !_BypassAutoLOD)
|
|
{
|
|
// temp test for auto lod
|
|
emitLOD = ps->getAutoLODEmitRatio();
|
|
}
|
|
else
|
|
{
|
|
emitLOD = 1.f;
|
|
}
|
|
nlassert(_EmissionType == CPSEmitter::regular);
|
|
if (!_ConsistentEmission)
|
|
{
|
|
if (emitLOD != 1.f)
|
|
{
|
|
processRegularEmission(firstInstanceIndex, emitLOD);
|
|
}
|
|
else
|
|
{
|
|
processRegularEmissionWithNoLOD(firstInstanceIndex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (emitLOD != 1.f)
|
|
{
|
|
if (emitLOD != 0.f)
|
|
{
|
|
processRegularEmissionConsistent(firstInstanceIndex, emitLOD, 1.f / emitLOD);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
processRegularEmissionConsistentWithNoLOD(firstInstanceIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::newElement(const CPSEmitterInfo &info)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_newElement)
|
|
nlassert(_Phase.getSize() != _Phase.getMaxSize());
|
|
|
|
_Phase.insert(0.f);
|
|
if (_MaxEmissionCount != 0)
|
|
{
|
|
_NumEmission.insert(0);
|
|
}
|
|
if (_PeriodScheme && _PeriodScheme->hasMemory()) _PeriodScheme->newElement(info);
|
|
if (_GenNbScheme && _GenNbScheme->hasMemory()) _GenNbScheme->newElement(info);
|
|
|
|
}
|
|
|
|
///==========================================================================
|
|
inline void CPSEmitter::deleteElementBase(uint32 index)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_deleteElementBase)
|
|
if (_PeriodScheme && _PeriodScheme->hasMemory()) _PeriodScheme->deleteElement(index);
|
|
if (_GenNbScheme && _GenNbScheme->hasMemory()) _GenNbScheme->deleteElement(index);
|
|
_Phase.remove(index);
|
|
if (_MaxEmissionCount != 0)
|
|
{
|
|
_NumEmission.remove(index);
|
|
}
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::deleteElement(uint32 index)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_deleteElement)
|
|
|
|
if (_EmissionType == CPSEmitter::onDeath && _EmittedType && _Active)
|
|
{
|
|
if (!_BypassEmitOnDeath)
|
|
{
|
|
const uint32 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, index) : _GenNb;
|
|
processEmitOutsideSimLoop(index, nbToGenerate);
|
|
}
|
|
}
|
|
deleteElementBase(index);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::deleteElement(uint32 index, TAnimationTime timeUntilNextSimStep)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_deleteElement)
|
|
if (_EmissionType == CPSEmitter::onDeath && _EmittedType && _Active)
|
|
{
|
|
const uint32 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, index) : _GenNb;
|
|
processEmitConsistent(_Owner->getPos()[index], index, nbToGenerate, timeUntilNextSimStep);
|
|
}
|
|
deleteElementBase(index);
|
|
}
|
|
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::resize(uint32 size)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_resize)
|
|
nlassert(size < (1 << 16));
|
|
if (_PeriodScheme && _PeriodScheme->hasMemory()) _PeriodScheme->resize(size, _Owner->getSize());
|
|
if (_GenNbScheme && _GenNbScheme->hasMemory()) _GenNbScheme->resize(size, _Owner->getSize());
|
|
_Phase.resize(size);
|
|
if (_MaxEmissionCount != 0)
|
|
{
|
|
_NumEmission.resize(size);
|
|
}
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::bounceOccured(uint32 index, TAnimationTime timeToNextSimStep)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_bounceOccured)
|
|
// TODO : avoid duplication with deleteElement
|
|
if (_EmittedType && _EmissionType == CPSEmitter::onBounce)
|
|
{
|
|
const uint32 nbToGenerate = _GenNbScheme ? _GenNbScheme->get(_Owner, index) : _GenNb;
|
|
processEmitConsistent(_Owner->getPos()[index], index, nbToGenerate, timeToNextSimStep);
|
|
}
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_serial)
|
|
/// version 6 : the flag _EmitDirBasis no longer exist, it has been replaced by _UserMatrixModeForEmissionDirection
|
|
//
|
|
/// version 5 : added _BypassAutoLOD
|
|
/// version 4 : added consistent emissions
|
|
sint ver = f.serialVersion(6);
|
|
CPSLocatedBindable::serial(f);
|
|
|
|
f.serialPolyPtr(_EmittedType);
|
|
f.serial(_Phase);
|
|
f.serial(_SpeedInheritanceFactor);
|
|
|
|
bool speedBasisEmission = _SpeedBasisEmission; // tmp copy because of bitfield serialization scheme
|
|
f.serial(speedBasisEmission);
|
|
_SpeedBasisEmission = speedBasisEmission;
|
|
|
|
f.serialEnum(_EmissionType);
|
|
|
|
// this is for use with serial
|
|
bool trueB = true, falseB = false;
|
|
|
|
if (!f.isReading())
|
|
{
|
|
switch (_EmissionType)
|
|
{
|
|
case CPSEmitter::regular:
|
|
if (_PeriodScheme)
|
|
{
|
|
f.serial(trueB);
|
|
f.serialPolyPtr(_PeriodScheme);
|
|
}
|
|
else
|
|
{
|
|
f.serial(falseB);
|
|
f.serial(_Period);
|
|
}
|
|
if (ver >= 3)
|
|
{
|
|
f.serial(_EmitDelay, _MaxEmissionCount);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (_GenNbScheme)
|
|
{
|
|
f.serial(trueB);
|
|
f.serialPolyPtr(_GenNbScheme);
|
|
}
|
|
else
|
|
{
|
|
f.serial(falseB);
|
|
f.serial(_GenNb);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool useScheme;
|
|
switch (_EmissionType)
|
|
{
|
|
case CPSEmitter::regular:
|
|
{
|
|
f.serial(useScheme);
|
|
if (useScheme)
|
|
{
|
|
delete _PeriodScheme;
|
|
f.serialPolyPtr(_PeriodScheme);
|
|
}
|
|
else
|
|
{
|
|
f.serial(_Period);
|
|
}
|
|
if (ver >= 3)
|
|
{
|
|
f.serial(_EmitDelay, _MaxEmissionCount);
|
|
updateMaxCountVect();
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
f.serial(useScheme);
|
|
if (useScheme)
|
|
{
|
|
delete _GenNbScheme;
|
|
f.serialPolyPtr(_GenNbScheme);
|
|
}
|
|
else
|
|
{
|
|
f.serial(_GenNb);
|
|
}
|
|
}
|
|
if (ver > 1 && ver < 6)
|
|
{
|
|
nlassert(f.isReading());
|
|
bool emitDirBasis;
|
|
f.serial(emitDirBasis);
|
|
if (emitDirBasis)
|
|
{
|
|
_UserMatrixModeForEmissionDirection = false;
|
|
_UserDirectionMatrixMode = PSFXWorldMatrix;
|
|
}
|
|
else
|
|
{
|
|
_UserMatrixModeForEmissionDirection = true;
|
|
if (_Owner)
|
|
{
|
|
_UserDirectionMatrixMode = _Owner->getMatrixMode() == PSFXWorldMatrix ? PSIdentityMatrix : PSFXWorldMatrix;
|
|
}
|
|
else
|
|
{
|
|
_UserDirectionMatrixMode = PSFXWorldMatrix;
|
|
}
|
|
}
|
|
}
|
|
if (ver >= 4)
|
|
{
|
|
bool consistentEmission = _ConsistentEmission; // tmp copy because of bitfield serialization scheme
|
|
f.serial(consistentEmission);
|
|
_ConsistentEmission = consistentEmission;
|
|
}
|
|
if (ver >= 5)
|
|
{
|
|
bool byassAutoLOD = _BypassAutoLOD; // tmp copy because of bitfield serialization scheme
|
|
f.serial(byassAutoLOD);
|
|
_BypassAutoLOD = byassAutoLOD;
|
|
}
|
|
if (ver >= 6)
|
|
{
|
|
bool userMatrixModeForEmissionDirection = _UserMatrixModeForEmissionDirection; // tmp copy because of bitfield serialization scheme
|
|
f.serial(userMatrixModeForEmissionDirection);
|
|
_UserMatrixModeForEmissionDirection = userMatrixModeForEmissionDirection;
|
|
f.serialEnum(_UserDirectionMatrixMode);
|
|
}
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::updateMaxCountVect()
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_updateMaxCountVect)
|
|
if (!_MaxEmissionCount || !_Owner)
|
|
{
|
|
_NumEmission.resize(0);
|
|
}
|
|
else
|
|
{
|
|
_NumEmission.resize(_Owner->getMaxSize());
|
|
while (_NumEmission.getSize() != 0)
|
|
{
|
|
_NumEmission.remove(0);
|
|
}
|
|
while (_NumEmission.getSize() != _Owner->getSize())
|
|
{
|
|
_NumEmission.insert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::setEmitDelay(float delay)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_setEmitDelay)
|
|
_EmitDelay = delay;
|
|
if (_Owner && _Owner->getOwner())
|
|
{
|
|
_Owner->getOwner()->systemDurationChanged();
|
|
}
|
|
}
|
|
|
|
///==========================================================================
|
|
bool CPSEmitter::setMaxEmissionCount(uint8 count)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_setMaxEmissionCount)
|
|
if (count == _MaxEmissionCount) return true;
|
|
nlassert(_Owner && _Owner->getOwner());
|
|
CParticleSystem *ps = _Owner->getOwner();
|
|
if (ps->getBypassMaxNumIntegrationSteps())
|
|
{
|
|
uint8 oldEmissiontCount = _MaxEmissionCount;
|
|
// should check that the new value is valid
|
|
_MaxEmissionCount = count;
|
|
if (testEmitForever())
|
|
{
|
|
_MaxEmissionCount = oldEmissiontCount;
|
|
nlwarning("<CPSEmitter::setMaxEmissionCount> can't set max emission count to %d \
|
|
with the current configuration : the system has been flagged with \
|
|
'BypassMaxNumIntegrationSteps', and should have a finite duration. \
|
|
The new value is not set", (int) count);
|
|
return false;
|
|
}
|
|
}
|
|
ps->systemDurationChanged();
|
|
_MaxEmissionCount = count;
|
|
updateMaxCountVect();
|
|
return true;
|
|
}
|
|
|
|
///==========================================================================
|
|
bool CPSEmitter::checkLoop() const
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_checkLoop)
|
|
nlassert(_Owner);
|
|
nlassert(_Owner->getOwner());
|
|
if (!_EmittedType) return false;
|
|
std::set<const CPSLocated *> seenLocated; // the located we've already seen
|
|
std::vector<const CPSLocated *> leftLoc(1); // the located that are left to see
|
|
leftLoc[0] = _EmittedType;
|
|
do
|
|
{
|
|
const CPSLocated *curr = leftLoc.back();
|
|
if (curr == this->_Owner) return true;
|
|
leftLoc.pop_back();
|
|
seenLocated.insert(curr);
|
|
for(uint32 k = 0; k < curr->getNbBoundObjects(); ++k)
|
|
{
|
|
const CPSEmitter *emitter = dynamic_cast<const CPSEmitter *>(curr->getBoundObject(k));
|
|
if (emitter && emitter->_EmittedType)
|
|
{
|
|
if (seenLocated.find(emitter->_EmittedType) == seenLocated.end()) // not already seen this one ?
|
|
{
|
|
leftLoc.push_back(emitter->_EmittedType);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (!leftLoc.empty());
|
|
return false;
|
|
}
|
|
|
|
///==========================================================================
|
|
bool CPSEmitter::testEmitForever() const
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_testEmitForever)
|
|
if (!_Owner)
|
|
{
|
|
nlwarning("<CPSEmitter::testEmitForever> The emitter should be inserted in a CPSLocated instance for this call to work.");
|
|
nlassert(0);
|
|
return true;
|
|
}
|
|
if (!_Owner->getLastForever()) return false;
|
|
switch(getEmissionType())
|
|
{
|
|
case CPSEmitter::onBounce:
|
|
case CPSEmitter::externEmit:
|
|
case CPSEmitter::regular:
|
|
// it is ok only if a limited number of located is emitted
|
|
if (getMaxEmissionCount() == 0) return true;
|
|
break;
|
|
case CPSEmitter::onDeath: return true; // the emitter never dies, so ..
|
|
case CPSEmitter::once: return false;
|
|
break;
|
|
default:
|
|
nlassert(0); // not a known type
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////
|
|
// implementation of CPSModulatedEmitter //
|
|
////////////////////////////////////////////
|
|
|
|
void CPSModulatedEmitter::serialEmitteeSpeedScheme(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
NL_PS_FUNC(CPSModulatedEmitter_IStream )
|
|
bool useScheme;
|
|
if (!f.isReading())
|
|
{
|
|
useScheme = useEmitteeSpeedScheme();
|
|
}
|
|
f.serial(useScheme);
|
|
if (useScheme)
|
|
{
|
|
f.serialPolyPtr(_EmitteeSpeedScheme);
|
|
}
|
|
else
|
|
{
|
|
f.serial(_EmitteeSpeed);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////
|
|
// implementation of CPSEmitterOmni //
|
|
////////////////////////////////////////////
|
|
|
|
///==========================================================================
|
|
void CPSEmitterOmni::emit(const NLMISC::CVector &srcPos, uint32 index, CVector &pos, CVector &speed)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterOmni_emit)
|
|
// TODO : verifier que ca marche si une particule s'emet elle-mem
|
|
nlassert(_EmittedType);
|
|
|
|
CVector v( ((rand() % 1000) - 500) / 500.0f
|
|
, ((rand() % 1000) - 500) / 500.0f
|
|
, ((rand() % 1000) - 500) / 500.0f);
|
|
v.normalize();
|
|
v *= _EmitteeSpeedScheme ? _EmitteeSpeedScheme->get(_Owner, index) : _EmitteeSpeed;
|
|
|
|
pos = srcPos;
|
|
speed = v;
|
|
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterOmni::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterOmni_serial)
|
|
f.serialVersion(1);
|
|
CPSEmitter::serial(f);
|
|
CPSModulatedEmitter::serialEmitteeSpeedScheme(f);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterOmni::newElement(const CPSEmitterInfo &info)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterOmni_newElement)
|
|
CPSEmitter::newElement(info);
|
|
newEmitteeSpeedElement(info);
|
|
}
|
|
|
|
///==========================================================================
|
|
inline void CPSEmitterOmni::deleteElementBase(uint32 index)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterOmni_deleteElementBase)
|
|
deleteEmitteeSpeedElement(index);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterOmni::deleteElement(uint32 index, TAnimationTime timeUntilNextSimStep)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterOmni_deleteElement)
|
|
CPSEmitter::deleteElement(index, timeUntilNextSimStep);
|
|
deleteElementBase(index);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterOmni::deleteElement(uint32 index)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterOmni_deleteElement)
|
|
CPSEmitter::deleteElement(index);
|
|
deleteElementBase(index);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterOmni::resize(uint32 capacity)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterOmni_resize)
|
|
nlassert(capacity < (1 << 16));
|
|
CPSEmitter::resize(capacity);
|
|
resizeEmitteeSpeed(capacity);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterDirectionnal::emit(const NLMISC::CVector &srcPos, uint32 index, CVector &pos, CVector &speed)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterDirectionnal_emit)
|
|
// TODO : verifier que ca marche si une particule s'emet elle-mem
|
|
nlassert(_EmittedType);
|
|
|
|
|
|
speed = (_EmitteeSpeedScheme ? _EmitteeSpeedScheme->get(_Owner, index) : _EmitteeSpeed) * _Dir;
|
|
pos = srcPos;
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterDirectionnal::newElement(const CPSEmitterInfo &info)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterDirectionnal_newElement)
|
|
CPSEmitter::newElement(info);
|
|
newEmitteeSpeedElement(info);
|
|
}
|
|
|
|
|
|
///==========================================================================
|
|
inline void CPSEmitterDirectionnal::deleteElementBase(uint32 index)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterDirectionnal_deleteElementBase)
|
|
deleteEmitteeSpeedElement(index);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterDirectionnal::deleteElement(uint32 index, TAnimationTime timeUntilNextSimStep)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterDirectionnal_deleteElement)
|
|
CPSEmitter::deleteElement(index, timeUntilNextSimStep);
|
|
deleteElementBase(index);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterDirectionnal::deleteElement(uint32 index)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterDirectionnal_deleteElement)
|
|
CPSEmitter::deleteElement(index);
|
|
deleteElementBase(index);
|
|
}
|
|
|
|
|
|
|
|
///==========================================================================
|
|
void CPSEmitterDirectionnal::resize(uint32 capacity)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterDirectionnal_resize)
|
|
nlassert(capacity < (1 << 16));
|
|
CPSEmitter::resize(capacity);
|
|
resizeEmitteeSpeed(capacity);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterDirectionnal::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterDirectionnal_IStream )
|
|
f.serialVersion(1);
|
|
CPSEmitter::serial(f);
|
|
CPSModulatedEmitter::serialEmitteeSpeedScheme(f);
|
|
f.serial(_Dir);
|
|
}
|
|
|
|
////////////////////////////////////////////
|
|
// implementation of CPSEmitterRectangle //
|
|
////////////////////////////////////////////
|
|
|
|
///==========================================================================
|
|
void CPSEmitterRectangle::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterRectangle_IStream )
|
|
f.serialVersion(1);
|
|
CPSEmitter::serial(f);
|
|
CPSModulatedEmitter::serialEmitteeSpeedScheme(f);
|
|
f.serial(_Basis);
|
|
f.serial(_Width);
|
|
f.serial(_Height);
|
|
f.serial(_Dir);
|
|
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterRectangle::emit(const NLMISC::CVector &srcPos, uint32 index, CVector &pos, CVector &speed)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterRectangle_emit)
|
|
CVector N = _Basis[index].X ^ _Basis[index].Y;
|
|
pos = srcPos + ((rand() % 32000) * (1.f / 16000) - 1.f) * _Width[index] * _Basis[index].X
|
|
+ ((rand() % 32000) * (1.f / 16000) - 1.f) * _Height[index] * _Basis[index].Y;
|
|
speed = (_EmitteeSpeedScheme ? _EmitteeSpeedScheme->get(_Owner, index) : _EmitteeSpeed)
|
|
* (_Dir.x * _Basis[index].X+ _Dir.y * _Basis[index].Y + _Dir.z * N);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterRectangle::setMatrix(uint32 index, const CMatrix &m)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterRectangle_setMatrix)
|
|
_Owner->getPos()[index] = m.getPos();
|
|
|
|
|
|
_Basis[index].X = m.getI();
|
|
_Basis[index].Y = m.getJ();
|
|
}
|
|
|
|
///==========================================================================
|
|
CMatrix CPSEmitterRectangle::getMatrix(uint32 index) const
|
|
{
|
|
NL_PS_FUNC(CPSEmitterRectangle_getMatrix)
|
|
CMatrix m;
|
|
m.setPos(_Owner->getPos()[index]);
|
|
m.setRot(_Basis[index].X, _Basis[index].Y, _Basis[index].X ^ _Basis[index].Y, true);
|
|
return m;
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterRectangle::setScale(uint32 index, float scale)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterRectangle_setScale)
|
|
_Width[index] = scale;
|
|
_Height[index] = scale;
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterRectangle::setScale(uint32 index, const CVector &s)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterRectangle_setScale)
|
|
_Width[index] = s.x;
|
|
_Height[index] = s.y;
|
|
}
|
|
|
|
///==========================================================================
|
|
CVector CPSEmitterRectangle::getScale(uint32 index) const
|
|
{
|
|
NL_PS_FUNC(CPSEmitterRectangle_getScale)
|
|
return CVector(_Width[index], _Height[index], 1.f);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterRectangle::newElement(const CPSEmitterInfo &info)
|
|
{
|
|
NL_PS_FUNC( CPSEmitterRectangle_newElement)
|
|
CPSEmitter::newElement(info);
|
|
newEmitteeSpeedElement(info);
|
|
_Basis.insert(CPlaneBasis(CVector::K));
|
|
_Width.insert(1.f);
|
|
_Height.insert(1.f);
|
|
}
|
|
|
|
///==========================================================================
|
|
inline void CPSEmitterRectangle::deleteElementBase(uint32 index)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterRectangle_deleteElementBase)
|
|
deleteEmitteeSpeedElement(index);
|
|
_Basis.remove(index);
|
|
_Width.remove(index);
|
|
_Height.remove(index);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterRectangle::deleteElement(uint32 index)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterRectangle_deleteElement)
|
|
CPSEmitter::deleteElement(index);
|
|
deleteElementBase(index);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterRectangle::deleteElement(uint32 index, TAnimationTime timeUntilNextSimStep)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterRectangle_deleteElement)
|
|
CPSEmitter::deleteElement(index, timeUntilNextSimStep);
|
|
deleteElementBase(index);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterRectangle::resize(uint32 size)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterRectangle_resize)
|
|
nlassert(size < (1 << 16));
|
|
CPSEmitter::resize(size);
|
|
resizeEmitteeSpeed(size);
|
|
_Basis.resize(size);
|
|
_Width.resize(size);
|
|
_Height.resize(size);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterRectangle::showTool(void)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterRectangle_showTool)
|
|
nlassert(_Owner);
|
|
const uint size = _Owner->getSize();
|
|
if (!size) return;
|
|
setupDriverModelMatrix();
|
|
CMatrix mat;
|
|
|
|
CPSLocated *loc;
|
|
uint32 index;
|
|
CPSLocatedBindable *lb;
|
|
_Owner->getOwner()->getCurrentEditedElement(loc, index, lb);
|
|
|
|
for (uint k = 0; k < size; ++k)
|
|
{
|
|
const CVector &I = _Basis[k].X;
|
|
const CVector &J = _Basis[k].Y;
|
|
mat.setRot(I, J , I ^J);
|
|
mat.setPos(_Owner->getPos()[k]);
|
|
CPSUtil::displayBasis(getDriver() ,getLocalToWorldMatrix(), mat, 1.f, *getFontGenerator(), *getFontManager());
|
|
setupDriverModelMatrix();
|
|
|
|
const CRGBA col = ((lb == NULL || this == lb) && loc == _Owner && index == k ? CRGBA::Red : CRGBA(127, 127, 127));
|
|
|
|
|
|
|
|
const CVector &pos = _Owner->getPos()[k];
|
|
CPSUtil::display3DQuad(*getDriver(), pos + I * _Width[k] + J * _Height[k]
|
|
, pos + I * _Width[k] - J * _Height[k]
|
|
, pos - I * _Width[k] - J * _Height[k]
|
|
, pos - I * _Width[k] + J * _Height[k], col);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////
|
|
// CPSEmitterconic implementation //
|
|
////////////////////////////////////
|
|
|
|
///==========================================================================
|
|
void CPSEmitterConic::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterConic_serial)
|
|
f.serialVersion(1);
|
|
CPSEmitterDirectionnal::serial(f);
|
|
f.serial(_Radius);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterConic::setDir(const CVector &v)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterConic_setDir)
|
|
CPSEmitterDirectionnal::setDir(v);
|
|
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitterConic::emit(const NLMISC::CVector &srcPos, uint32 index, CVector &pos, CVector &speed)
|
|
{
|
|
NL_PS_FUNC(CPSEmitterConic_emit)
|
|
// TODO : optimize that
|
|
nlassert(_EmittedType);
|
|
|
|
// we choose a custom direction like with omnidirectionnal emitter
|
|
// then we force the direction vect to have the unit size
|
|
|
|
static const double divRand = (2.0 / RAND_MAX);
|
|
|
|
CVector dir((float) (rand() * divRand - 1)
|
|
, (float) (rand() * divRand - 1)
|
|
, (float) (rand() * divRand - 1) );
|
|
|
|
const float n =dir.norm();
|
|
|
|
dir *= _Radius / n;
|
|
|
|
dir -= (_Dir * dir) * _Dir;
|
|
dir += _Dir;
|
|
dir.normalize();
|
|
|
|
|
|
speed = (_EmitteeSpeedScheme ? _EmitteeSpeedScheme->get(_Owner, index) : _EmitteeSpeed)
|
|
* dir;
|
|
pos = srcPos;
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
// CPSSphericalEmitter implementation //
|
|
////////////////////////////////////////
|
|
|
|
///==========================================================================
|
|
void CPSSphericalEmitter::emit(const NLMISC::CVector &srcPos, uint32 index, CVector &pos, CVector &speed)
|
|
{
|
|
NL_PS_FUNC(CPSSphericalEmitter_emit)
|
|
static const double divRand = (2.0 / RAND_MAX);
|
|
CVector dir((float) (rand() * divRand - 1), (float) (rand() * divRand - 1) , (float) (rand() * divRand - 1) );
|
|
dir.normalize();
|
|
pos = srcPos + _Radius[index] * dir;
|
|
speed = (_EmitteeSpeedScheme ? _EmitteeSpeedScheme->get(_Owner, index) : _EmitteeSpeed) * dir;
|
|
}
|
|
|
|
|
|
///==========================================================================
|
|
void CPSSphericalEmitter::showTool(void)
|
|
{
|
|
NL_PS_FUNC(CPSSphericalEmitter_showTool)
|
|
CPSLocated *loc;
|
|
uint32 index;
|
|
CPSLocatedBindable *lb;
|
|
_Owner->getOwner()->getCurrentEditedElement(loc, index, lb);
|
|
|
|
|
|
TPSAttribFloat::const_iterator radiusIt = _Radius.begin();
|
|
TPSAttribVector::const_iterator posIt = _Owner->getPos().begin(), endPosIt = _Owner->getPos().end();
|
|
setupDriverModelMatrix();
|
|
for (uint k = 0; posIt != endPosIt; ++posIt, ++radiusIt, ++k)
|
|
{
|
|
const CRGBA col = ((lb == NULL || this == lb) && loc == _Owner && index == k ? CRGBA::Red : CRGBA(127, 127, 127));
|
|
CPSUtil::displaySphere(*getDriver(), *radiusIt, *posIt, 5, col);
|
|
}
|
|
}
|
|
|
|
|
|
///==========================================================================
|
|
void CPSSphericalEmitter::setMatrix(uint32 index, const CMatrix &m)
|
|
{
|
|
NL_PS_FUNC(CPSSphericalEmitter_setMatrix)
|
|
nlassert(index < _Radius.getSize());
|
|
// compute new pos
|
|
_Owner->getPos()[index] = m.getPos();
|
|
|
|
}
|
|
|
|
///==========================================================================
|
|
CMatrix CPSSphericalEmitter::getMatrix(uint32 index) const
|
|
{
|
|
NL_PS_FUNC(CPSSphericalEmitter_getMatrix)
|
|
nlassert(index < _Radius.getSize());
|
|
CMatrix m;
|
|
m.identity();
|
|
m.translate(_Owner->getPos()[index]);
|
|
return m;
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSSphericalEmitter::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
NL_PS_FUNC(CPSSphericalEmitter_serial)
|
|
f.serialVersion(1);
|
|
CPSEmitter::serial(f);
|
|
CPSModulatedEmitter::serialEmitteeSpeedScheme(f);
|
|
f.serial(_Radius);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSSphericalEmitter::newElement(const CPSEmitterInfo &info)
|
|
{
|
|
NL_PS_FUNC(CPSSphericalEmitter_newElement)
|
|
CPSEmitter::newElement(info);
|
|
newEmitteeSpeedElement(info);
|
|
_Radius.insert(1.f);
|
|
}
|
|
|
|
///==========================================================================
|
|
inline void CPSSphericalEmitter::deleteElementBase(uint32 index)
|
|
{
|
|
NL_PS_FUNC(CPSSphericalEmitter_deleteElementBase)
|
|
deleteEmitteeSpeedElement(index);
|
|
_Radius.remove(index);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSSphericalEmitter::deleteElement(uint32 index)
|
|
{
|
|
NL_PS_FUNC(CPSSphericalEmitter_deleteElement)
|
|
CPSEmitter::deleteElement(index);
|
|
deleteElementBase(index);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSSphericalEmitter::deleteElement(uint32 index, TAnimationTime timeUntilNextSimStep)
|
|
{
|
|
NL_PS_FUNC(CPSSphericalEmitter_deleteElement)
|
|
CPSEmitter::deleteElement(index, timeUntilNextSimStep);
|
|
deleteElementBase(index);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSSphericalEmitter::resize(uint32 size)
|
|
{
|
|
NL_PS_FUNC(CPSSphericalEmitter_resize)
|
|
nlassert(size < (1 << 16));
|
|
CPSEmitter::resize(size);
|
|
resizeEmitteeSpeed(size);
|
|
_Radius.resize(size);
|
|
}
|
|
|
|
/////////////////////////////////////
|
|
// CPSRadialEmitter implementation //
|
|
/////////////////////////////////////
|
|
|
|
///==========================================================================
|
|
void CPSRadialEmitter::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
|
|
{
|
|
NL_PS_FUNC(CPSRadialEmitter_serial)
|
|
f.serialVersion(1);
|
|
CPSEmitterDirectionnal::serial(f);
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSRadialEmitter::emit(const NLMISC::CVector &srcPos, uint32 index, NLMISC::CVector &pos, NLMISC::CVector &speed)
|
|
{
|
|
NL_PS_FUNC(CPSRadialEmitter_emit)
|
|
// TODO : verify if it works when a particle emits itself
|
|
nlassert(_EmittedType);
|
|
|
|
static const double divRand = (2.0 / RAND_MAX);
|
|
CVector dir((float) (rand() * divRand - 1),
|
|
(float) (rand() * divRand - 1),
|
|
(float) (rand() * divRand - 1) );
|
|
dir -= (dir * _Dir) * _Dir; //keep tangential direction
|
|
dir.normalize();
|
|
speed = (_EmitteeSpeedScheme ? _EmitteeSpeedScheme->get(_Owner, index) : _EmitteeSpeed) * dir;
|
|
pos = srcPos;
|
|
}
|
|
|
|
|
|
///===============================================================================
|
|
void CPSEmitter::enableSpeedBasisEmission(bool enabled /*=true*/)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_enableSpeedBasisEmission)
|
|
bool wasUserMatNeeded = isUserMatrixUsed();
|
|
_SpeedBasisEmission = enabled;
|
|
updatePSRefCountForUserMatrixUsage(isUserMatrixUsed(), wasUserMatNeeded);
|
|
}
|
|
|
|
///===============================================================================
|
|
void CPSEmitter::enableUserMatrixModeForEmissionDirection(bool enable /*=true*/)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_enableUserMatrixModeForEmissionDirection)
|
|
bool wasUserMatNeeded = isUserMatrixUsed();
|
|
_UserMatrixModeForEmissionDirection = enable;
|
|
updatePSRefCountForUserMatrixUsage(isUserMatrixUsed(), wasUserMatNeeded);
|
|
}
|
|
|
|
///===============================================================================
|
|
void CPSEmitter::setUserMatrixModeForEmissionDirection(TPSMatrixMode matrixMode)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_setUserMatrixModeForEmissionDirection)
|
|
bool wasUserMatNeeded = isUserMatrixUsed();
|
|
_UserDirectionMatrixMode = matrixMode;
|
|
updatePSRefCountForUserMatrixUsage(isUserMatrixUsed(), wasUserMatNeeded);
|
|
}
|
|
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::updatePSRefCountForUserMatrixUsage(bool matrixIsNeededNow, bool matrixWasNeededBefore)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_updatePSRefCountForUserMatrixUsage)
|
|
if (_Owner && _Owner->getOwner())
|
|
{
|
|
if (matrixIsNeededNow && !matrixWasNeededBefore)
|
|
{
|
|
_Owner->getOwner()->addRefForUserSysCoordInfo();
|
|
}
|
|
else if (!matrixIsNeededNow && matrixWasNeededBefore)
|
|
{
|
|
_Owner->getOwner()->releaseRefForUserSysCoordInfo();
|
|
}
|
|
}
|
|
}
|
|
|
|
///==========================================================================
|
|
bool CPSEmitter::isUserMatrixUsed() const
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_isUserMatrixUsed)
|
|
return !_SpeedBasisEmission && _UserMatrixModeForEmissionDirection && _UserDirectionMatrixMode == PSUserMatrix;
|
|
}
|
|
|
|
///==========================================================================
|
|
bool CPSEmitter::getUserMatrixUsageCount() const
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_getUserMatrixUsageCount)
|
|
return isUserMatrixUsed() ? 1 : 0;
|
|
}
|
|
|
|
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::doEmitOnce(uint firstInstanceIndex)
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_doEmitOnce)
|
|
if (!_EmittedType) return;
|
|
if (!_GenNbScheme && _GenNb == 0) return;
|
|
nlassert(_Owner);
|
|
nlassert(CParticleSystem::InsideSimLoop); // should only be called by the sim loop
|
|
float emitLOD;
|
|
nlassert(_Owner);
|
|
nlassert(_Owner->getOwner());
|
|
const CParticleSystem *ps = _Owner->getOwner();
|
|
if (ps->isAutoLODEnabled() && !ps->isSharingEnabled() && !_BypassAutoLOD)
|
|
{
|
|
// temp test for auto lod
|
|
emitLOD = ps->getAutoLODEmitRatio();
|
|
}
|
|
else
|
|
{
|
|
emitLOD = 1.f;
|
|
}
|
|
nlassert(emitLOD >= 0.f);
|
|
if (_GenNbScheme)
|
|
{
|
|
const uint BATCH_SIZE = 1024;
|
|
uint32 numToEmit[BATCH_SIZE];
|
|
uint k = firstInstanceIndex;
|
|
nlassert(firstInstanceIndex < _Owner->getSize());
|
|
uint leftToDo = _Owner->getSize() - firstInstanceIndex;
|
|
|
|
while (leftToDo)
|
|
{
|
|
uint toProcess = std::min((uint) BATCH_SIZE, leftToDo);
|
|
uint32 *numToEmitPtr = (uint32 *) _GenNbScheme->make(_Owner, k, numToEmit, sizeof(uint32), true);
|
|
leftToDo -= toProcess;
|
|
while (toProcess)
|
|
{
|
|
CVector startPos;
|
|
if (!_Owner->isParametricMotionEnabled())
|
|
{
|
|
startPos = _Owner->getPos()[k] - _Owner->getSpeed()[k] * CParticleSystem::EllapsedTime;
|
|
}
|
|
else
|
|
{
|
|
startPos = _Owner->getParametricInfos()[k].Pos;
|
|
}
|
|
float currTime = _Owner->getTime()[k];
|
|
_Owner->getTime()[k] = 0.f; // when emit occured, time was 0
|
|
sint32 nbToGenerate = (sint32) (emitLOD * *numToEmitPtr);
|
|
if (nbToGenerate > 0)
|
|
{
|
|
nbToGenerate = std::min(nbToGenerate, (sint32) _EmittedType->getMaxSize());
|
|
processEmitConsistent(startPos, k, nbToGenerate, _Owner->getAgeInSeconds(k) / CParticleSystem::RealEllapsedTimeRatio);
|
|
}
|
|
// restore time & pos
|
|
_Owner->getTime()[k] = currTime;
|
|
++ k;
|
|
++ numToEmitPtr;
|
|
-- toProcess;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sint nbToGenerate = (sint) (emitLOD * _GenNb);
|
|
if (nbToGenerate <= 0) nbToGenerate = 1;
|
|
nbToGenerate = std::min(nbToGenerate, (sint) _EmittedType->getMaxSize());
|
|
for(uint k = firstInstanceIndex; k < _Owner->getSize(); ++k)
|
|
{
|
|
// retrieve previous position (because motion step is done before spawn step)
|
|
CVector startPos;
|
|
if (!_Owner->isParametricMotionEnabled())
|
|
{
|
|
startPos = _Owner->getPos()[k] - _Owner->getSpeed()[k] * CParticleSystem::EllapsedTime;
|
|
}
|
|
else
|
|
{
|
|
startPos = _Owner->getParametricInfos()[k].Pos;
|
|
}
|
|
float currTime = _Owner->getTime()[k];
|
|
_Owner->getTime()[k] = 0.f; // when emit occured, time was 0
|
|
processEmitConsistent(startPos, k, nbToGenerate, _Owner->getAgeInSeconds(k) / CParticleSystem::RealEllapsedTimeRatio);
|
|
// restore time & pos
|
|
_Owner->getTime()[k] = currTime;
|
|
}
|
|
}
|
|
}
|
|
|
|
///==========================================================================
|
|
void CPSEmitter::updateEmitTrigger()
|
|
{
|
|
NL_PS_FUNC(CPSEmitter_updateEmitTrigger)
|
|
if (!_EmitTrigger) return;
|
|
nlassert(_Owner);
|
|
nlassert(_Owner->getOwner());
|
|
const CParticleSystem *ps = _Owner->getOwner();
|
|
float emitLOD;
|
|
if (ps->isAutoLODEnabled() && !ps->isSharingEnabled() && !_BypassAutoLOD)
|
|
{
|
|
// temp test for auto lod
|
|
emitLOD = ps->getAutoLODEmitRatio();
|
|
}
|
|
else
|
|
{
|
|
emitLOD = 1.f;
|
|
}
|
|
if (_GenNbScheme)
|
|
{
|
|
const uint BATCH_SIZE = 1024;
|
|
uint32 numToEmit[BATCH_SIZE];
|
|
uint k = 0;
|
|
uint leftToDo = _Owner->getSize();
|
|
while (leftToDo)
|
|
{
|
|
uint toProcess = std::min(BATCH_SIZE, leftToDo);
|
|
uint32 *numToEmitPtr = (uint32 *) _GenNbScheme->make(_Owner, k, numToEmit, sizeof(uint32), true);
|
|
while (toProcess)
|
|
{
|
|
uint32 nbToGenerate = (sint32) (emitLOD * *numToEmitPtr);
|
|
if (!nbToGenerate) nbToGenerate = 1;
|
|
processEmit(k, nbToGenerate);
|
|
++ k;
|
|
++ numToEmitPtr;
|
|
}
|
|
|
|
leftToDo -= toProcess;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint nbToGenerate = (sint32) (emitLOD * _GenNb);
|
|
if (!nbToGenerate) nbToGenerate = 1;
|
|
for(uint k = 0; k < _Owner->getSize(); ++k)
|
|
{
|
|
processEmit(k, nbToGenerate);
|
|
}
|
|
}
|
|
_EmitTrigger = false;
|
|
}
|
|
|
|
|
|
|
|
} // NL3D
|
|
|
|
namespace NLMISC
|
|
{
|
|
|
|
std::string toString(NL3D::CPSEmitter::TEmissionType type)
|
|
{
|
|
NL_PS_FUNC(toString_CPSEmitter_TEmissionType)
|
|
nlctassert(NL3D::CPSEmitter::numEmissionType == 5); // If this ct assertion is raised, the content of TEmissionType has changed, so should change this function !
|
|
switch (type)
|
|
{
|
|
case NL3D::CPSEmitter::regular: return "regular";
|
|
case NL3D::CPSEmitter::onDeath: return "onDeath";
|
|
case NL3D::CPSEmitter::once: return "once";
|
|
case NL3D::CPSEmitter::onBounce: return "onBounce";
|
|
case NL3D::CPSEmitter::externEmit: return "externEmit";
|
|
default:
|
|
nlassert(0);
|
|
return "";
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
} // NLMISC
|