khanat-opennel-code/code/nel/src/3d/ps_located.cpp
2015-12-18 13:02:31 +01:00

3149 lines
90 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 <algorithm>
#include "nel/misc/aabbox.h"
#include "nel/misc/matrix.h"
#include "nel/misc/common.h"
#include "nel/3d/ps_util.h"
#include "nel/3d/particle_system.h"
#include "nel/3d/ps_zone.h"
#include "nel/3d/driver.h"
#include "nel/3d/material.h"
#include "nel/3d/dru.h"
#include "nel/3d/ps_located.h"
#include "nel/3d/ps_particle.h"
#include "nel/3d/ps_force.h"
#include "nel/3d/ps_emitter.h"
#include "nel/3d/ps_misc.h"
#include "nel/misc/line.h"
#include "nel/misc/system_info.h"
#include "nel/misc/common.h"
//
#include "nel/3d/particle_system_model.h"
#ifdef NL_DEBUG
#define CHECK_PS_INTEGRITY checkIntegrity();
#else
#define CHECK_PS_INTEGRITY
#endif
namespace NL3D {
std::vector<CPSCollisionInfo> CPSLocated::_Collisions;
CPSCollisionInfo *CPSLocated::_FirstCollision = NULL;
/// ***************************************************************************************
/**
* Constructor
*/
CPSLocated::CPSLocated() : /*_MaxNumFaces(0),*/
_Size(0),
_MaxSize(DefaultMaxLocatedInstance),
_CollisionInfoNbRef(0),
_CollisionNextPos(NULL),
_InitialLife(1.f),
_LifeScheme(NULL),
_InitialMass(1.f),
_MassScheme(NULL),
_LODDegradation(false),
_ParametricMotion(false),
_TriggerOnDeath(false),
_LastForever(true),
_TriggerID(NELID("NONE")),
_NonIntegrableForceNbRefs(0),
_NumIntegrableForceWithDifferentBasis(0)
{
NL_PS_FUNC(CPSLocated_CPSLocated)
}
// *****************************************************************************************************
const NLMISC::CMatrix &CPSLocated::getLocalToWorldMatrix() const
{
NL_PS_FUNC(CPSLocated_getLocalToWorldMatrix)
nlassert(_Owner);
switch(getMatrixMode())
{
case PSFXWorldMatrix: return _Owner->getSysMat();
case PSIdentityMatrix: return NLMISC::CMatrix::Identity;
case PSUserMatrix: return _Owner->getUserMatrix();
default:
nlassert(0);
}
nlassert(0);
return NLMISC::CMatrix::Identity;
}
// *****************************************************************************************************
const NLMISC::CMatrix &CPSLocated::getWorldToLocalMatrix() const
{
NL_PS_FUNC(CPSLocated_getWorldToLocalMatrix)
nlassert(_Owner);
switch(getMatrixMode())
{
case PSFXWorldMatrix: return _Owner->getInvertedSysMat();
case PSIdentityMatrix: return NLMISC::CMatrix::Identity;
case PSUserMatrix: return _Owner->getInvertedUserMatrix();
default:
nlassert(0);
}
nlassert(0);
return NLMISC::CMatrix::Identity;
}
/// ***************************************************************************************
float CPSLocated::evalMaxDuration() const
{
NL_PS_FUNC(CPSLocated_evalMaxDuration)
if (_LastForever) return -1.f;
return _LifeScheme ? _LifeScheme->getMaxValue() : _InitialLife;
}
/// ***************************************************************************************
void CPSLocated::checkIntegrity() const
{
NL_PS_FUNC(CPSLocated_checkIntegrity)
nlassert(_InvMass.getMaxSize() == _Pos.getMaxSize());
nlassert(_Pos.getMaxSize() == _Speed.getMaxSize());
nlassert(_Speed.getMaxSize() == _Time.getMaxSize());
nlassert(_Time.getMaxSize() == _TimeIncrement.getMaxSize());
//
nlassert(_InvMass.getSize() == _Pos.getSize());
nlassert(_Pos.getSize() == _Speed.getSize());
nlassert(_Speed.getSize() == _Time.getSize());
nlassert(_Time.getSize() == _TimeIncrement.getSize());
//
if (hasCollisionInfos())
{
nlassert(_CollisionNextPos->getSize() == _Pos.getSize());
nlassert(_CollisionNextPos->getMaxSize() == _Pos.getMaxSize());
}
//
}
/// ***************************************************************************************
bool CPSLocated::setLastForever()
{
NL_PS_FUNC(CPSLocated_setLastForever)
CHECK_PS_INTEGRITY
_LastForever = true;
if (_Owner && _Owner->getBypassMaxNumIntegrationSteps())
{
// Should test that the system is still valid.
if (!_Owner->canFinish())
{
_LastForever = false;
nlwarning("<CPSLocated::setLastForever> Can't set flag : this causes the system to last forever, and it has been flagged with 'BypassMaxNumIntegrationSteps'. Flag is not set");
return false;
}
}
CHECK_PS_INTEGRITY
return true;
}
/// ***************************************************************************************
void CPSLocated::systemDateChanged()
{
NL_PS_FUNC(CPSLocated_systemDateChanged)
CHECK_PS_INTEGRITY
for(TLocatedBoundCont::iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
(*it)->systemDateChanged();
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::releaseRefTo(const CParticleSystemProcess *other)
{
NL_PS_FUNC(CPSLocated_releaseRefTo)
CHECK_PS_INTEGRITY
// located bindables
{
for(TLocatedBoundCont::iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
(*it)->releaseRefTo(other);
}
}
// dtor observers
{
for(TDtorObserversVect::iterator it = _DtorObserversVect.begin(); it != _DtorObserversVect.end(); ++it)
{
if ((*it)->getOwner() == other)
{
CPSLocatedBindable *refMaker = *it;
refMaker->notifyTargetRemoved(this);
break;
}
}
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::releaseAllRef()
{
NL_PS_FUNC(CPSLocated_releaseAllRef)
CHECK_PS_INTEGRITY
// located bindables
{
for(TLocatedBoundCont::iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
(*it)->releaseAllRef();
}
}
// we must do a copy, because the subsequent call can modify this vector
TDtorObserversVect copyVect(_DtorObserversVect.begin(), _DtorObserversVect.end());
// call all the dtor observers
for (TDtorObserversVect::iterator it = copyVect.begin(); it != copyVect.end(); ++it)
{
(*it)->notifyTargetRemoved(this);
}
_DtorObserversVect.clear();
nlassert(_CollisionInfoNbRef == 0); //If this is not = 0, then someone didnt call releaseCollisionInfo
// If this happen, you can register with the registerDTorObserver
// (observer pattern)
// and override notifyTargetRemove to call releaseCollisionInfo
nlassert(_IntegrableForces.size() == 0);
nlassert(_NonIntegrableForceNbRefs == 0);
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::notifyMotionTypeChanged(void)
{
NL_PS_FUNC(CPSLocated_notifyMotionTypeChanged)
CHECK_PS_INTEGRITY
for (TLocatedBoundCont::const_iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
(*it)->motionTypeChanged(_ParametricMotion);
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::integrateSingle(float startDate, float deltaT, uint numStep,
uint32 indexInLocated,
NLMISC::CVector *destPos,
uint stride) const
{
NL_PS_FUNC(CPSLocated_integrateSingle)
CHECK_PS_INTEGRITY
nlassert(supportParametricMotion() && _ParametricMotion);
if (_IntegrableForces.size() != 0)
{
bool accumulate = false;
for (TForceVect::const_iterator it = _IntegrableForces.begin(); it != _IntegrableForces.end(); ++it)
{
nlassert((*it)->isIntegrable());
(*it)->integrateSingle(startDate, deltaT, numStep, this, indexInLocated, destPos, accumulate, stride);
accumulate = true;
}
}
else // no forces applied, just deduce position from date, initial pos and speed
{
#ifdef NL_DEBUG
NLMISC::CVector *endPos = (NLMISC::CVector *) ( (uint8 *) destPos + stride * numStep);
#endif
const CPSLocated::CParametricInfo &pi = _PInfo[indexInLocated];
destPos = FillBufUsingSubdiv(pi.Pos, pi.Date, startDate, deltaT, numStep, destPos, stride);
if (numStep != 0)
{
float currDate = startDate - pi.Date;
nlassert(currDate >= 0);
do
{
#ifdef NL_DEBUG
nlassert(destPos < endPos);
#endif
destPos->x = pi.Pos.x + currDate * pi.Speed.x;
destPos->y = pi.Pos.y + currDate * pi.Speed.y;
destPos->z = pi.Pos.z + currDate * pi.Speed.z;
currDate += deltaT;
destPos = (NLMISC::CVector *) ( (uint8 *) destPos + stride);
}
while (--numStep);
}
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::performParametricMotion(TAnimationTime date)
{
NL_PS_FUNC(CPSLocated_performParametricMotion)
CHECK_PS_INTEGRITY
if (!_Size) return;
nlassert(supportParametricMotion() && _ParametricMotion);
if (_IntegrableForces.size() != 0)
{
bool accumulate = false;
for (TForceVect::iterator it = _IntegrableForces.begin(); it != _IntegrableForces.end(); ++it)
{
nlassert((*it)->isIntegrable());
(*it)->integrate(date, this, 0, _Size, &_Pos[0], &_Speed[0], accumulate);
accumulate = true;
}
}
else
{
CPSLocated::TPSAttribParametricInfo::const_iterator it = _PInfo.begin(),
endIt = _PInfo.end();
TPSAttribVector::iterator posIt = _Pos.begin();
float deltaT;
do
{
deltaT = date - it->Date;
posIt->x = it->Pos.x + deltaT * it->Speed.x;
posIt->y = it->Pos.y + deltaT * it->Speed.y;
posIt->z = it->Pos.z + deltaT * it->Speed.z;
++posIt;
++it;
}
while (it != endIt);
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
/// allocate parametric infos
void CPSLocated::allocateParametricInfos(void)
{
NL_PS_FUNC(CPSLocated_allocateParametricInfos)
CHECK_PS_INTEGRITY
if (_ParametricMotion) return;
nlassert(supportParametricMotion());
nlassert(_Owner);
const float date = _Owner->getSystemDate();
_PInfo.resize(_MaxSize);
// copy back infos from current position and speeds
TPSAttribVector::const_iterator posIt = _Pos.begin(), endPosIt = _Pos.end();
TPSAttribVector::const_iterator speedIt = _Speed.begin();
while (posIt != endPosIt)
{
_PInfo.insert( CParametricInfo(*posIt, *speedIt, date) );
++posIt;
}
_ParametricMotion = true;
notifyMotionTypeChanged();
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
/// release parametric infos
void CPSLocated::releaseParametricInfos(void)
{
NL_PS_FUNC(CPSLocated_releaseParametricInfos)
CHECK_PS_INTEGRITY
if (!_ParametricMotion) return;
NLMISC::contReset(_PInfo);
_ParametricMotion = false;
notifyMotionTypeChanged();
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
/// Test whether this located support parametric motion
bool CPSLocated::supportParametricMotion(void) const
{
NL_PS_FUNC(CPSLocated_supportParametricMotion)
return _NonIntegrableForceNbRefs == 0 && _NumIntegrableForceWithDifferentBasis == 0;
}
/// ***************************************************************************************
/** When set to true, this tells the system to use parametric motion. This is needed in a few case only,
* and can only work if all the forces that apply to the system are integrable
*/
void CPSLocated::enableParametricMotion(bool enable /*= true*/)
{
NL_PS_FUNC(CPSLocated_enableParametricMotion)
CHECK_PS_INTEGRITY
nlassert(supportParametricMotion());
if (enable)
{
allocateParametricInfos();
}
else
{
releaseParametricInfos();
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::setMatrixMode(TPSMatrixMode matrixMode)
{
NL_PS_FUNC(CPSLocated_setMatrixMode)
CHECK_PS_INTEGRITY
if (matrixMode != getMatrixMode())
{
for (TLocatedBoundCont::const_iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
(*it)->basisChanged(matrixMode);
}
CParticleSystemProcess::setMatrixMode(matrixMode);
for (TForceVect::iterator fIt = _IntegrableForces.begin(); fIt != _IntegrableForces.end(); ++fIt)
{
integrableForceBasisChanged( (*fIt)->getOwner()->getMatrixMode() );
}
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
/*
void CPSLocated::notifyMaxNumFacesChanged(void)
{
CHECK_PS_INTEGRITY
if (!_Owner) return;
// we examine whether we have particle attached to us, and ask for the max number of faces they may want
_MaxNumFaces = 0;
for (TLocatedBoundCont::const_iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
if ((*it)->getType() == PSParticle)
{
uint maxNumFaces = ((CPSParticle *) (*it))->getMaxNumFaces();
///nlassertex(maxNumFaces < ((1 << 16) - 1), ("%s", (*it)->getClassName().c_str()));
_MaxNumFaces += maxNumFaces;
}
}
CHECK_PS_INTEGRITY
}
*/
/// ***************************************************************************************
uint CPSLocated::getNumWantedTris() const
{
NL_PS_FUNC(CPSLocated_getNumWantedTris)
CHECK_PS_INTEGRITY
if (!_Owner) return 0;
uint numWantedTris = 0;
for (TLocatedBoundCont::const_iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
if ((*it)->getType() == PSParticle)
{
numWantedTris += NLMISC::safe_cast<CPSParticle *>(*it)->getNumWantedTris();
}
}
CHECK_PS_INTEGRITY
return numWantedTris;
}
/*
/// ***************************************************************************************
uint CPSLocated::querryMaxWantedNumFaces(void)
{
return _MaxNumFaces;
}
*/
/// ***************************************************************************************
/// tells whether there are alive entities / particles in the system
bool CPSLocated::hasParticles(void) const
{
NL_PS_FUNC(CPSLocated_hasParticles)
CHECK_PS_INTEGRITY
for (TLocatedBoundCont::const_iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
if ((*it)->getType() == PSParticle && (*it)->hasParticles()) return true;
}
CHECK_PS_INTEGRITY
return false;
}
/// ***************************************************************************************
/// tells whether there are alive emitters
bool CPSLocated::hasEmitters(void) const
{
NL_PS_FUNC(CPSLocated_hasEmitters)
CHECK_PS_INTEGRITY
for (TLocatedBoundCont::const_iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
if ((*it)->getType() == PSEmitter && (*it)->hasEmitters()) return true;
}
CHECK_PS_INTEGRITY
return false;
}
/// ***************************************************************************************
void CPSLocated::getLODVect(NLMISC::CVector &v, float &offset, TPSMatrixMode matrixMode)
{
NL_PS_FUNC(CPSLocated_getLODVect)
nlassert(_Owner);
CHECK_PS_INTEGRITY
_Owner->getLODVect(v, offset, matrixMode);
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
float CPSLocated::getUserParam(uint numParam) const
{
NL_PS_FUNC(CPSLocated_getUserParam)
nlassert(_Owner);
CHECK_PS_INTEGRITY
return _Owner->getUserParam(numParam);
}
/// ***************************************************************************************
CScene *CPSLocated::getScene(void)
{
NL_PS_FUNC(CPSLocated_getScene)
nlassert(_Owner);
CHECK_PS_INTEGRITY
return _Owner->getScene();
}
/// ***************************************************************************************
void CPSLocated::incrementNbDrawnParticles(uint num)
{
NL_PS_FUNC(CPSLocated_incrementNbDrawnParticles)
CHECK_PS_INTEGRITY
CParticleSystem::NbParticlesDrawn += num; // for benchmark purpose
}
/// ***************************************************************************************
void CPSLocated::setInitialLife(TAnimationTime lifeTime)
{
NL_PS_FUNC(CPSLocated_setInitialLife)
CHECK_PS_INTEGRITY
_LastForever = false;
_InitialLife = lifeTime;
delete _LifeScheme;
_LifeScheme = NULL;
/** Reset all particles current date to 0. This is needed because we do not check
* if particle life is over when the date of the system has not gone beyond the life duration of particles
*/
for (uint k = 0; k < _Size; ++k)
{
_Time[k] = 0.f;
}
//
if (_Owner)
{
_Owner->systemDurationChanged();
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::setLifeScheme(CPSAttribMaker<float> *scheme)
{
NL_PS_FUNC(CPSLocated_setLifeScheme)
CHECK_PS_INTEGRITY
nlassert(scheme);
nlassert(!scheme->hasMemory()); // scheme with memory is invalid there !!
_LastForever = false;
delete _LifeScheme;
_LifeScheme = scheme;
//
if (_Owner)
{
_Owner->systemDurationChanged();
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::setInitialMass(float mass)
{
NL_PS_FUNC(CPSLocated_setInitialMass)
CHECK_PS_INTEGRITY
_InitialMass = mass;
delete _MassScheme;
_MassScheme = NULL;
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::setMassScheme(CPSAttribMaker<float> *scheme)
{
NL_PS_FUNC(CPSLocated_setMassScheme)
CHECK_PS_INTEGRITY
nlassert(scheme);
nlassert(!scheme->hasMemory()); // scheme with memory is invalid there !!
delete _MassScheme;
_MassScheme = scheme;
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
/// get a matrix that helps to express located B coordinate in located A basis
const NLMISC::CMatrix &CPSLocated::getConversionMatrix(const CParticleSystem &ps, TPSMatrixMode destMode, TPSMatrixMode srcMode)
{
NL_PS_FUNC(CPSLocated_getConversionMatrix)
switch(destMode)
{
case PSFXWorldMatrix:
switch(srcMode)
{
case PSFXWorldMatrix: return NLMISC::CMatrix::Identity;
case PSIdentityMatrix: return ps.getInvertedSysMat();
case PSUserMatrix: return ps.getUserToFXMatrix();
default:
nlassert(0);
}
break;
case PSIdentityMatrix:
switch(srcMode)
{
case PSFXWorldMatrix: return ps.getSysMat();
case PSIdentityMatrix: return NLMISC::CMatrix::Identity;
case PSUserMatrix: return ps.getUserMatrix();
default:
nlassert(0);
}
break;
case PSUserMatrix:
switch(srcMode)
{
case PSFXWorldMatrix: return ps.getFXToUserMatrix();
case PSIdentityMatrix: return ps.getInvertedUserMatrix();
case PSUserMatrix: return NLMISC::CMatrix::Identity;
default:
nlassert(0);
}
break;
default:
nlassert(0);
}
nlassert(0);
return NLMISC::CMatrix::Identity;
}
/// ***************************************************************************************
NLMISC::CVector CPSLocated::computeI(void) const
{
NL_PS_FUNC(CPSLocated_computeI)
CHECK_PS_INTEGRITY
const NLMISC::CMatrix &sysMat = _Owner->getSysMat();
if (getMatrixMode() == PSIdentityMatrix)
{
if (!sysMat.hasScalePart())
{
return _Owner->getInvertedViewMat().getI();
}
else
{
return sysMat.getScaleUniform() * _Owner->getInvertedViewMat().getI();
}
}
else
{
if (!sysMat.hasScalePart())
{
// we must express the I vector in the system basis, so we need to multiply it by the inverted matrix of the system
return getWorldToLocalMatrix().mulVector(_Owner->getInvertedViewMat().getI());
}
else
{
return sysMat.getScaleUniform() * getWorldToLocalMatrix().mulVector(_Owner->getInvertedViewMat().getI());
}
}
}
/// ***************************************************************************************
NLMISC::CVector CPSLocated::computeIWithZAxisAligned(void) const
{
NL_PS_FUNC(CPSLocated_computeIWithZAxisAligned)
CHECK_PS_INTEGRITY
const NLMISC::CMatrix &sysMat = _Owner->getSysMat();
const CVector &camI = _Owner->getInvertedViewMat().getI();
CVector I(camI.x, camI.y, 0.f);
I.normalize();
if (getMatrixMode() == PSIdentityMatrix)
{
if (!sysMat.hasScalePart())
{
return I;
}
else
{
return sysMat.getScaleUniform() * I;
}
}
else
{
if (!sysMat.hasScalePart())
{
// we must express the I vector in the system basis, so we need to multiply it by the inverted matrix of the system
return getWorldToLocalMatrix().mulVector(I);
}
else
{
return sysMat.getScaleUniform() * getWorldToLocalMatrix().mulVector(I);
}
}
}
/// ***************************************************************************************
NLMISC::CVector CPSLocated::computeJ(void) const
{
NL_PS_FUNC(CPSLocated_computeJ)
CHECK_PS_INTEGRITY
const NLMISC::CMatrix &sysMat = _Owner->getSysMat();
if (getMatrixMode() == PSIdentityMatrix)
{
if (!sysMat.hasScalePart())
{
return _Owner->getInvertedViewMat().getJ();
}
else
{
return sysMat.getScaleUniform() * _Owner->getInvertedViewMat().getJ();
}
}
else
{
if (!sysMat.hasScalePart())
{
// we must express the J vector in the system basis, so we need to multiply it by the inverted matrix of the system
return getWorldToLocalMatrix().mulVector(_Owner->getInvertedViewMat().getJ());
}
else
{
return sysMat.getScaleUniform() * getWorldToLocalMatrix().mulVector(_Owner->getInvertedViewMat().getJ());
}
}
}
/// ***************************************************************************************
NLMISC::CVector CPSLocated::computeK(void) const
{
NL_PS_FUNC(CPSLocated_computeK)
CHECK_PS_INTEGRITY
const NLMISC::CMatrix &sysMat = _Owner->getSysMat();
if (getMatrixMode() == PSIdentityMatrix)
{
if (!sysMat.hasScalePart())
{
return _Owner->getInvertedViewMat().getK();
}
else
{
return sysMat.getScaleUniform() * _Owner->getInvertedViewMat().getK();
}
}
else
{
if (!sysMat.hasScalePart())
{
// we must express the K vector in the system basis, so we need to multiply it by the inverted matrix of the system
return getWorldToLocalMatrix().mulVector(_Owner->getInvertedViewMat().getK());
}
else
{
return sysMat.getScaleUniform() * getWorldToLocalMatrix().mulVector(_Owner->getInvertedViewMat().getK());
}
}
}
/// ***************************************************************************************
NLMISC::CVector CPSLocated::computeKWithZAxisAligned(void) const
{
NL_PS_FUNC(CPSLocated_computeKWithZAxisAligned)
CHECK_PS_INTEGRITY
const NLMISC::CMatrix &sysMat = _Owner->getSysMat();
if (getMatrixMode() == PSIdentityMatrix)
{
if (!sysMat.hasScalePart())
{
return CVector::K;
}
else
{
return CVector(0.f, 0.f, sysMat.getScaleUniform());
}
}
else
{
if (!sysMat.hasScalePart())
{
// we must express the K vector in the system basis, so we need to multiply it by the inverted matrix of the system
return getWorldToLocalMatrix().mulVector(CVector::K);
}
else
{
return getWorldToLocalMatrix().mulVector(CVector(0.f, 0.f, sysMat.getScaleUniform()));
}
}
}
/// ***************************************************************************************
IDriver *CPSLocated::getDriver() const
{
NL_PS_FUNC(CPSLocated_getDriver)
CHECK_PS_INTEGRITY
nlassert(_Owner);
nlassert (_Owner->getDriver() ); // you haven't called setDriver on the system
return _Owner->getDriver();
}
/// ***************************************************************************************
/// dtor
CPSLocated::~CPSLocated()
{
NL_PS_FUNC(CPSLocated_CPSLocated)
CHECK_PS_INTEGRITY
// we must do a copy, because the subsequent call can modify this vector
TDtorObserversVect copyVect(_DtorObserversVect.begin(), _DtorObserversVect.end());
// call all the dtor observers
for (TDtorObserversVect::iterator it = copyVect.begin(); it != copyVect.end(); ++it)
{
(*it)->notifyTargetRemoved(this);
}
nlassert(_CollisionInfoNbRef == 0); //If this is not = 0, then someone didnt call releaseCollisionInfo
// If this happen, you can register with the registerDTorObserver
// (observer pattern)
// and override notifyTargetRemove to call releaseCollisionInfo
nlassert(_IntegrableForces.size() == 0);
nlassert(_NonIntegrableForceNbRefs == 0);
// delete all bindable
for (TLocatedBoundCont::iterator it2 = _LocatedBoundCont.begin(); it2 != _LocatedBoundCont.end(); ++it2)
{
(*it2)->finalize();
delete *it2;
}
_LocatedBoundCont.clear();
delete _LifeScheme;
delete _MassScheme;
delete _CollisionNextPos;
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
/**
* sorted insertion (by decreasing priority order) of a bindable (particle e.g an aspect, emitter) in a located
*/
bool CPSLocated::bind(CPSLocatedBindable *lb)
{
NL_PS_FUNC(CPSLocated_bind)
CHECK_PS_INTEGRITY
nlassert(std::find(_LocatedBoundCont.begin(), _LocatedBoundCont.end(), lb) == _LocatedBoundCont.end());
TLocatedBoundCont::iterator it = _LocatedBoundCont.begin();
while (it != _LocatedBoundCont.end() && **it < *lb) // the "<" operator sort them correctly
{
++it;
}
_LocatedBoundCont.insert(it, lb);
lb->setOwner(this);
lb->resize(_MaxSize);
// any located bindable that is bound to us should have no element in it for now !!
// we resize it anyway...
uint32 initialSize = _Size;
CPSEmitterInfo ei;
ei.setDefaults();
for (uint k = 0; k < initialSize; ++k)
{
_Size = k;
lb->newElement(ei);
}
_Size = initialSize;
if (_ParametricMotion) lb->motionTypeChanged(true);
/// the max number of shapes may have changed
//notifyMaxNumFacesChanged();
if (_Owner)
{
CParticleSystem *ps = _Owner;
if (ps->getBypassMaxNumIntegrationSteps())
{
if (!ps->canFinish())
{
unbind(getIndexOf(lb));
nlwarning("<CPSLocated::bind> Can't bind the located : this causes the system to last forever, and it has been flagged with 'BypassMaxNumIntegrationSteps'. Located is not bound.");
return false;
}
}
// if there's an extern id, register in lb list
if (lb->getExternID() != 0)
{
// register in ID list
ps->registerLocatedBindableExternID(lb->getExternID(), lb);
}
_Owner->systemDurationChanged();
}
CHECK_PS_INTEGRITY
return true;
}
/// ***************************************************************************************
void CPSLocated::remove(const CPSLocatedBindable *p)
{
NL_PS_FUNC(CPSLocated_remove)
CHECK_PS_INTEGRITY
TLocatedBoundCont::iterator it = std::find(_LocatedBoundCont.begin(), _LocatedBoundCont.end(), p);
nlassert(it != _LocatedBoundCont.end());
(*it)->finalize();
delete *it;
_LocatedBoundCont.erase(it);
if (_Owner)
{
_Owner->systemDurationChanged();
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::registerDtorObserver(CPSLocatedBindable *anObserver)
{
NL_PS_FUNC(CPSLocated_registerDtorObserver)
CHECK_PS_INTEGRITY
// check whether the observer wasn't registered twice
nlassert(std::find(_DtorObserversVect.begin(), _DtorObserversVect.end(), anObserver) == _DtorObserversVect.end());
_DtorObserversVect.push_back(anObserver);
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::unregisterDtorObserver(CPSLocatedBindable *anObserver)
{
NL_PS_FUNC(CPSLocated_unregisterDtorObserver)
CHECK_PS_INTEGRITY
// check that it was registered
TDtorObserversVect::iterator it = std::find(_DtorObserversVect.begin(), _DtorObserversVect.end(), anObserver);
nlassert(it != _DtorObserversVect.end());
_DtorObserversVect.erase(it);
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::postNewElement(const NLMISC::CVector &pos,
const NLMISC::CVector &speed,
CPSLocated &emitterLocated,
uint32 indexInEmitter,
TPSMatrixMode speedCoordSystem,
TAnimationTime lifeTime)
{
NL_PS_FUNC(CPSLocated_postNewElement)
nlassert(CParticleSystem::InsideSimLoop); // should be called only inside the sim loop!
// In the event loop life of emitter is updated just after particles are spawned, so we must check there if the particle wasn't emitted when the
// emitter was already destroyed
// When postNewElement is called, the particle and the emitter that created it live at the same date, so EmitterLife - ParticleLife should be > 1.f
float emitterLife;
if (!emitterLocated.getLastForever())
{
if (emitterLocated._LifeScheme)
{
emitterLife = emitterLocated._Time[indexInEmitter] - lifeTime * CParticleSystem::RealEllapsedTimeRatio * emitterLocated._TimeIncrement[indexInEmitter];
if (emitterLife >= 1.f)
{
return; // emitter had finished its life
}
}
else
{
emitterLife = emitterLocated._Time[indexInEmitter] * emitterLocated._InitialLife - lifeTime * CParticleSystem::RealEllapsedTimeRatio;
if (emitterLife >= emitterLocated._InitialLife)
{
return; // emitter had finished its life
}
if (emitterLocated._InitialLife != 0.f)
{
emitterLife /= emitterLocated._InitialLife;
}
}
}
else
{
emitterLife = emitterLocated.getTime()[indexInEmitter];
}
// now check that the emitter didn't collide before it spawned a particle
if (emitterLocated.hasCollisionInfos())
{
const CPSCollisionInfo &ci = _Collisions[indexInEmitter];
if (ci.Dist != -1.f)
{
// a collision occured, check time from collision to next time step
if ((emitterLocated.getPos()[indexInEmitter] - ci.NewPos) * (pos - ci.NewPos) > 0.f) return; // discard emit that are farther than the collision
}
}
// create a request to create a new element
CParticleSystem::CSpawnVect &sp = *CParticleSystem::_Spawns[getIndex()];
if (!_Owner->getAutoCountFlag() && sp.MaxNumSpawns == sp.SpawnInfos.size()) return; // no more place to spawn
if (getMaxSize() >= ((1 << 16) - 1)) return;
sp.SpawnInfos.resize(sp.SpawnInfos.size() + 1);
CPSSpawnInfo &si = sp.SpawnInfos.back();
si.EmitterInfo.Pos = emitterLocated.getPos()[indexInEmitter];
si.EmitterInfo.Speed = emitterLocated.getSpeed()[indexInEmitter];
si.EmitterInfo.InvMass = emitterLocated.getInvMass()[indexInEmitter];
si.EmitterInfo.Life = emitterLife;
si.EmitterInfo.Loc = &emitterLocated;
si.SpawnPos = pos;
si.Speed = speed;
si.SpeedCoordSystem = speedCoordSystem;
si.LifeTime = lifeTime;
}
/// ***************************************************************************************
sint32 CPSLocated::newElement(const CPSSpawnInfo &si, bool doEmitOnce /* = false */, TAnimationTime ellapsedTime)
{
NL_PS_FUNC(CPSLocated_newElement)
CHECK_PS_INTEGRITY
sint32 creationIndex;
// get the convertion matrix from the emitter basis to the emittee basis
// if the emitter is null, we assume that the coordinate are given in the chosen basis for this particle type
if (_MaxSize == _Size)
{
if (_Owner && _Owner->getAutoCountFlag() && getMaxSize() < ((1 << 16) - 1) )
{
// we are probably in edition mode -> auto-count mode helps to compute ideal particle array size
// but at the expense of costly allocations
uint maxSize = getMaxSize();
resize((uint32) std::min((uint) NLMISC::raiseToNextPowerOf2(maxSize + 1), (uint) ((1 << 16) - 1))); // force a reserve with next power of 2 (no important in edition mode)
resize(maxSize + 1);
CParticleSystem::_SpawnPos.resize(maxSize + 1);
}
else
{
return -1;
}
}
// During creation, we interpolate the position of the system (by using the ellapsed time) if particle are created in world basis and if the emitter is in local basis.
// Example a fireball FX let particles in world basis, but the fireball is moving. If we dont interpolate position between 2 frames, emission will appear to be "sporadic".
// For now, we manage the local to world case. The world to local is possible, but not very useful
switch(si.EmitterInfo.Loc ? si.EmitterInfo.Loc->getMatrixMode() : this->getMatrixMode())
{
case PSFXWorldMatrix:
switch(this->getMatrixMode())
{
case PSFXWorldMatrix:
{
creationIndex =_Pos.insert(si.SpawnPos);
}
break;
case PSIdentityMatrix:
{
CVector fxPosDelta;
_Owner->interpolateFXPosDelta(fxPosDelta, si.LifeTime);
creationIndex =_Pos.insert(_Owner->getSysMat() * si.SpawnPos + fxPosDelta);
}
break;
case PSUserMatrix:
{
CVector fxPosDelta;
_Owner->interpolateFXPosDelta(fxPosDelta, si.LifeTime);
CVector userMatrixPosDelta;
_Owner->interpolateUserPosDelta(userMatrixPosDelta, si.LifeTime);
creationIndex =_Pos.insert(_Owner->getInvertedUserMatrix() * (_Owner->getSysMat() * si.SpawnPos + fxPosDelta - userMatrixPosDelta));
}
break;
default:
nlassert(0);
}
break;
case PSIdentityMatrix:
switch(this->getMatrixMode())
{
case PSFXWorldMatrix:
{
CVector fxPosDelta;
_Owner->interpolateFXPosDelta(fxPosDelta, si.LifeTime);
creationIndex =_Pos.insert(_Owner->getInvertedSysMat() * (si.SpawnPos - fxPosDelta));
}
break;
case PSIdentityMatrix:
{
creationIndex =_Pos.insert(si.SpawnPos);
}
break;
case PSUserMatrix:
{
CVector userMatrixPosDelta;
_Owner->interpolateUserPosDelta(userMatrixPosDelta, si.LifeTime);
creationIndex =_Pos.insert(_Owner->getInvertedUserMatrix() * (si.SpawnPos - userMatrixPosDelta));
}
break;
default:
nlassert(0);
}
break;
case PSUserMatrix:
switch(this->getMatrixMode())
{
case PSFXWorldMatrix:
{
CVector fxPosDelta;
_Owner->interpolateFXPosDelta(fxPosDelta, si.LifeTime);
CVector userMatrixPosDelta;
_Owner->interpolateUserPosDelta(userMatrixPosDelta, si.LifeTime);
creationIndex =_Pos.insert(_Owner->getInvertedSysMat() * (_Owner->getUserMatrix() * si.SpawnPos + userMatrixPosDelta- fxPosDelta));
}
break;
case PSIdentityMatrix:
{
CVector userMatrixPosDelta;
_Owner->interpolateUserPosDelta(userMatrixPosDelta, si.LifeTime);
creationIndex =_Pos.insert(_Owner->getUserMatrix() * si.SpawnPos + userMatrixPosDelta);
}
break;
case PSUserMatrix:
{
creationIndex =_Pos.insert(si.SpawnPos);
}
break;
default:
nlassert(0);
}
break;
default:
nlassert(0);
}
nlassert(creationIndex != -1); // all attributs must contains the same number of elements
if (si.SpeedCoordSystem == this->getMatrixMode()) // is speed vector expressed in the good basis ?
{
_Speed.insert(si.Speed);
}
else
{
// must do conversion of speed
nlassert(_Owner);
const NLMISC::CMatrix &convMat = getConversionMatrix(*_Owner, this->getMatrixMode(), si.SpeedCoordSystem);
_Speed.insert(convMat.mulVector(si.Speed));
}
_InvMass.insert(1.f / ((_MassScheme && si.EmitterInfo.Loc) ? _MassScheme->get(si.EmitterInfo) : _InitialMass ) );
if (CParticleSystem::InsideSimLoop)
{
CParticleSystem::_SpawnPos[creationIndex] = _Pos[creationIndex];
}
// compute age of particle when it has been created
if (getLastForever())
{
// age of particle is in seconds
_Time.insert(CParticleSystem::RealEllapsedTimeRatio * si.LifeTime);
_TimeIncrement.insert(_InitialLife != 0.f ? 1.f / _InitialLife : 1.f);
}
else
{
const float totalLifeTime = (_LifeScheme && si.EmitterInfo.Loc) ? _LifeScheme->get(si.EmitterInfo) : _InitialLife ;
float timeIncrement = totalLifeTime ? 1.f / totalLifeTime : 10E6f;
_TimeIncrement.insert(timeIncrement);
_Time.insert(CParticleSystem::RealEllapsedTimeRatio * si.LifeTime * timeIncrement);
}
// test whether parametric motion is used, and generate the infos that are needed then
if (_ParametricMotion)
{
_PInfo.insert( CParametricInfo(_Pos[creationIndex], _Speed[creationIndex], _Owner->getSystemDate() + CParticleSystem::RealEllapsedTime - CParticleSystem::RealEllapsedTimeRatio * si.LifeTime) );
}
else
{
_Pos[creationIndex] += si.LifeTime * _Speed[creationIndex];
}
///////////////////////////////////////////
// generate datas for all bound objects //
///////////////////////////////////////////
for (TLocatedBoundCont::iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
(*it)->newElement(si.EmitterInfo);
// if element is an emitter, then must bias the emission time counter because it will be updated of frameDT, but the particle has been alive for (frameDT - deltaT)
if ((*it)->getType() == PSEmitter)
{
CPSEmitter *pEmit = NLMISC::safe_cast<CPSEmitter *>(*it);
pEmit->_Phase[creationIndex] -= std::max(0.f, (ellapsedTime - si.LifeTime));
}
}
if (doEmitOnce && !CPSEmitter::getBypassEmitOnDeath())
{
// can be called only outside the sim loop (when the user triggers an emitters for example)
for (TLocatedBoundCont::iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
if ((*it)->getType() == PSEmitter)
{
CPSEmitter *pEmit = NLMISC::safe_cast<CPSEmitter *>(*it);
if (pEmit->getEmissionType() == CPSEmitter::once)
{
for(uint k = 0; k < getSize(); ++k)
{
pEmit->singleEmit(k, 1);
}
}
}
}
}
if (_CollisionNextPos)
{
_CollisionNextPos->insert();
}
++_Size; // if this is modified, you must also modify the getNewElementIndex in this class
// because that method give the index of the element being created for overrider of the newElement method
// of the CPSLocatedClass (which is called just above)
CHECK_PS_INTEGRITY
return creationIndex;
}
/// ***************************************************************************************
sint32 CPSLocated::newElement(const CVector &pos, const CVector &speed, CPSLocated *emitter, uint32 indexInEmitter,
TPSMatrixMode speedCoordSystem, bool doEmitOnce /* = false */)
{
NL_PS_FUNC(CPSLocated_newElement)
CPSSpawnInfo si;
si.EmitterInfo.Loc = emitter;
if (emitter)
{
si.EmitterInfo.Pos = emitter->getPos()[indexInEmitter];
si.EmitterInfo.Speed = emitter->getSpeed()[indexInEmitter];
si.EmitterInfo.InvMass = emitter->getInvMass()[indexInEmitter];
si.EmitterInfo.Life = emitter->getTime()[indexInEmitter];
}
else
{
si.EmitterInfo.Pos = NLMISC::CVector::Null;
si.EmitterInfo.Speed = NLMISC::CVector::Null;
si.EmitterInfo.InvMass = 1.f;
si.EmitterInfo.Life = 0.f;
}
si.SpawnPos = pos;
si.Speed = speed;
si.SpeedCoordSystem = speedCoordSystem;
si.LifeTime = 0.f;
return newElement(si, doEmitOnce, 0.f);
}
/// ***************************************************************************************
static inline uint32 IDToLittleEndian(uint32 input)
{
NL_PS_FUNC(IDToLittleEndian)
#ifdef NL_LITTLE_ENDIAN
return input;
#else
return ((input & (0xff<<24))>>24)
|| ((input & (0xff<<16))>>8)
|| ((input & (0xff<<8))<<8)
|| ((input & 0xff)<<24);
#endif
}
/// ***************************************************************************************
inline void CPSLocated::deleteElementBase(uint32 index)
{
NL_PS_FUNC(CPSLocated_deleteElementBase)
// remove common located's attributes
_InvMass.remove(index);
_Pos.remove(index);
_Speed.remove(index);
_Time.remove(index);
_TimeIncrement.remove(index);
if (_CollisionNextPos)
{
_CollisionNextPos->remove(index);
}
if (_ParametricMotion)
{
_PInfo.remove(index);
}
--_Size;
if (_TriggerOnDeath)
{
const uint32 id = IDToLittleEndian(_TriggerID);
nlassert(_Owner);
uint numLb = _Owner->getNumLocatedBindableByExternID(id);
for (uint k = 0; k < numLb; ++k)
{
CPSLocatedBindable *lb = _Owner->getLocatedBindableByExternID(id, k);
if (lb->getType() == PSEmitter)
{
CPSEmitter *e = NLMISC::safe_cast<CPSEmitter *>(lb);
e->setEmitTrigger();
}
}
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::deleteElement(uint32 index)
{
NL_PS_FUNC(CPSLocated_deleteElement)
#ifdef NL_DEBUG
if (CParticleSystem::InsideSimLoop)
{
nlassert(CParticleSystem::InsideRemoveLoop);
}
#endif
CHECK_PS_INTEGRITY
nlassert(index < _Size);
for (TLocatedBoundCont::iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
(*it)->deleteElement(index);
}
deleteElementBase(index);
}
/// ***************************************************************************************
void CPSLocated::deleteElement(uint32 index, TAnimationTime timeToNextSimStep)
{
NL_PS_FUNC(CPSLocated_deleteElement)
#ifdef NL_DEBUG
if (CParticleSystem::InsideSimLoop)
{
nlassert(CParticleSystem::InsideRemoveLoop);
}
#endif
nlassert(index < _Size);
for (TLocatedBoundCont::iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
(*it)->deleteElement(index, timeToNextSimStep);
}
deleteElementBase(index);
}
/// Resize the located container
/// ***************************************************************************************
void CPSLocated::resize(uint32 newSize)
{
NL_PS_FUNC(CPSLocated_resize)
CHECK_PS_INTEGRITY
nlassert(newSize < (1 << 16));
if (newSize < _Size)
{
for (uint32 k = _Size - 1; k >= newSize; --k)
{
deleteElement(k);
if (k == 0) break; // we're dealing with unsigned quantities
}
_Size = newSize;
}
_MaxSize = newSize;
_InvMass.resize(newSize);
_Pos.resize(newSize);
_Speed.resize(newSize);
_Time.resize(newSize);
_TimeIncrement.resize(newSize);
if (_ParametricMotion)
{
_PInfo.resize(newSize);
}
if (_CollisionNextPos)
{
_CollisionNextPos->resize(newSize);
}
// resize attributes for all bound objects
for (TLocatedBoundCont::iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
(*it)->resize(newSize);
}
/// compute the new max number of faces
//notifyMaxNumFacesChanged();
CHECK_PS_INTEGRITY
}
// dummy struct for serial of a field that has been removed
class CDummyCollision
{
public:
void serial(NLMISC::IStream &f) throw(NLMISC::EStream)
{
NL_PS_FUNC(CDummyCollision_serial)
f.serialVersion(1);
float dummyDist = 0.f;
NLMISC::CVector dummyNewPos, dummyNewSpeed;
f.serial(dummyDist, dummyNewPos, dummyNewSpeed);
};
};
/// ***************************************************************************************
void CPSLocated::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
{
NL_PS_FUNC(CPSLocated_serial)
// version 7 : - removed the field _NbFramesToSkip to get some space (it is never used)
// - removed the requestStack (system graph can't contains loops now)
// - removed _CollisonInfos because they are now static
// version 4 to version 5 : bugfix with reading of collisions
sint ver = f.serialVersion(7);
CParticleSystemProcess::serial(f);
if (f.isReading() && !CParticleSystem::getSerializeIdentifierFlag())
{
// just skip the name
sint32 len;
f.serial(len);
f.seek(len, NLMISC::IStream::current);
}
else
{
f.serial(_Name);
}
f.serial(_InvMass);
f.serial(_Pos);
f.serial(_Speed);
f.serial(_Time);
if (f.isReading())
{
// tmp fix : some fx were saved with a life that is != to 0
// this cause an assertion in CPSLocated::updateLife, because all particle are assumed to have an age of 0 when the system is started
// TODO : saving _Time is maybe unecessary... or find something better for CPSLocated::updateLife
uint timeSize = _Time.getSize();
if (timeSize != 0)
{
std::fill(&_Time[0], &_Time[0] + timeSize, 0.f);
}
}
f.serial(_TimeIncrement);
f.serial(_Size);
f.serial(_MaxSize);
bool lastForever = _LastForever;
f.serial(lastForever);
_LastForever = lastForever;
if (ver < 7)
{
nlassert(f.isReading());
// serial a dummy ptr (previous code did a serial ptr)
uint64 dummyPtr;
f.serial(dummyPtr);
if (dummyPtr)
{
#ifdef PS_FAST_ALLOC
extern NLMISC::CContiguousBlockAllocator *PSBlockAllocator;
NLMISC::CContiguousBlockAllocator *oldAlloc = PSBlockAllocator;
PSBlockAllocator = NULL;
#endif
static CPSAttrib<CDummyCollision> col;
col.clear();
f.serial(col);
#ifdef PS_FAST_ALLOC
PSBlockAllocator = oldAlloc;
#endif
}
}
f.serial(_CollisionInfoNbRef); // TODO avoid to serialize this ?
//
if (f.isReading())
{
if (_CollisionInfoNbRef)
{
_CollisionNextPos = new TPSAttribVector;
_CollisionNextPos->resize(_Pos.getMaxSize());
for(uint k = 0; k < _Size; ++k)
{
_CollisionNextPos->insert();
}
}
}
//CHECK_PS_INTEGRITY
if (f.isReading())
{
delete _LifeScheme;
delete _MassScheme;
bool useScheme;
f.serial(useScheme);
if (useScheme)
{
f.serialPolyPtr(_LifeScheme);
}
else
{
f.serial(_InitialLife);
_LifeScheme = NULL;
}
f.serial(useScheme);
if (useScheme)
{
f.serialPolyPtr(_MassScheme);
}
else
{
f.serial(_InitialMass);
nlassert(_InitialMass > 0);
_MassScheme = NULL;
}
}
else
{
bool bFalse = false, bTrue = true;
if (_LifeScheme)
{
f.serial(bTrue);
f.serialPolyPtr(_LifeScheme);
}
else
{
f.serial(bFalse);
f.serial(_InitialLife);
}
if (_MassScheme)
{
f.serial(bTrue);
f.serialPolyPtr(_MassScheme);
}
else
{
f.serial(bFalse);
nlassert(_InitialMass > 0);
f.serial(_InitialMass);
}
}
if (ver < 7)
{
uint32 dummy = 0; // was previously the field "_NbFramesToSkip"
f.serial(dummy);
}
f.serialContPolyPtr(_DtorObserversVect);
if (ver < 7)
{
nlassert(f.isReading());
// previously, there was a request stack serialized (because system permitted loops)
uint32 size;
f.serial(size);
nlassert(size == 0);
/*
for (uint32 k = 0; k < size; ++k)
{
TNewElementRequestStack::value_type t;
f.serial(t);
_RequestStack.push(t);
}
*/
}
if (ver < 7)
{
nlassert(f.isReading());
bool dummy;
f.serial(dummy); // was previously the flag "_UpdateLock"
}
f.serialContPolyPtr(_LocatedBoundCont);
// check that owners are good
#ifdef NL_DEBUG
for(uint k = 0; k < _LocatedBoundCont.size(); ++k)
{
nlassert(_LocatedBoundCont[k]->getOwner() == this);
}
#endif
if (ver > 1)
{
bool lodDegradation = _LODDegradation;
f.serial(lodDegradation);
_LODDegradation = lodDegradation;
}
if (ver > 2)
{
bool parametricMotion = _ParametricMotion;
f.serial(parametricMotion);
_ParametricMotion = parametricMotion;
}
if (f.isReading())
{
// evaluate our max number of faces
//notifyMaxNumFacesChanged();
if (_ParametricMotion)
{
_ParametricMotion = false;
allocateParametricInfos();
_ParametricMotion = true;
}
}
if (ver > 3)
{
bool triggerOnDeath = _TriggerOnDeath;
f.serial(triggerOnDeath);
_TriggerOnDeath = triggerOnDeath;
f.serial(_TriggerID);
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
// integrate speed of particles. Makes eventually use of SSE instructions when present
static void IntegrateSpeed(uint count, float *src1, const float *src2, float *dest, float ellapsedTime)
{
NL_PS_FUNC(IntegrateSpeed)
#if 0 // this works, but is not enabled for now. The precision is not that good...
/*
#ifdef NL_OS_WINDOWS
if (NLMISC::CCpuInfo::hasSSE()
&& ((uint) src1 & 15) == ((uint) src2 & 15)
&& ! ((uint) src1 & 3)
&& ! ((uint) src2 & 3)
) // must must be sure that memory alignment is identical
{
// compute first datas in order to align to 16 byte boudary
uint alignCount = ((uint) src1 >> 2) & 3; // number of float to process
while (alignCount --)
{
*src1++ += ellapsedTime * *src2 ++;
}
count -= alignCount;
if (count > 3)
{
float et[4] = { ellapsedTime, ellapsedTime, ellapsedTime, ellapsedTime};
// sse part of computation
__asm
{
mov ecx, count
shr ecx, 2
xor edx, edx
mov eax, src1
mov ebx, src2
movups xmm0, et[0]
myLoop:
movaps xmm2, [ebx + 8 * edx]
movaps xmm1, [eax + 8 * edx]
mulps xmm2, xmm0
addps xmm1, xmm2
movaps [eax + 8 * edx], xmm1
add edx, 2
dec ecx
jne myLoop
}
}
// proceed with left float
count &= 3;
if (count)
{
src1 += alignCount;
src2 += alignCount;
do
{
*src1 += ellapsedTime * *src2;
++src1;
++src2;
}
while (--count);
}
}
else
#endif
*/
#endif
{
// standard version
// standard version
uint countDiv8 = count>>3;
count &= 7; // takes count % 8
if (dest == src1)
{
while (countDiv8 --)
{
src1[0] += ellapsedTime * src2[0];
src1[1] += ellapsedTime * src2[1];
src1[2] += ellapsedTime * src2[2];
src1[3] += ellapsedTime * src2[3];
src1[4] += ellapsedTime * src2[4];
src1[5] += ellapsedTime * src2[5];
src1[6] += ellapsedTime * src2[6];
src1[7] += ellapsedTime * src2[7];
src2 += 8;
src1 += 8;
}
while (count--)
{
*src1++ += ellapsedTime * *src2++;
}
}
else
{
while (countDiv8 --)
{
dest[0] = src1[0] + ellapsedTime * src2[0];
dest[1] = src1[1] + ellapsedTime * src2[1];
dest[2] = src1[2] + ellapsedTime * src2[2];
dest[3] = src1[3] + ellapsedTime * src2[3];
dest[4] = src1[4] + ellapsedTime * src2[4];
dest[5] = src1[5] + ellapsedTime * src2[5];
dest[6] = src1[6] + ellapsedTime * src2[6];
dest[7] = src1[7] + ellapsedTime * src2[7];
src2 += 8;
src1 += 8;
dest += 8;
}
while (count--)
{
*dest++ = *src1++ + ellapsedTime * *src2++;
}
}
}
}
/// ***************************************************************************************
void CPSLocated::computeMotion()
{
NL_PS_FUNC(CPSLocated_computeMotion)
nlassert(_Size);
// there are 2 integration steps : with and without collisions
if (!_CollisionNextPos) // no collisionner are used
{
{
MINI_TIMER(PSMotion3)
if (_Size != 0) // avoid referencing _Pos[0] if there's no size, causes STL vectors to assert...
IntegrateSpeed(_Size * 3, &_Pos[0].x, &_Speed[0].x, &_Pos[0].x, CParticleSystem::EllapsedTime);
}
}
else
{
{
MINI_TIMER(PSMotion4)
// compute new position after the timeStep
IntegrateSpeed(_Size * 3, &_Pos[0].x, &_Speed[0].x, &(*_CollisionNextPos)[0].x, CParticleSystem::EllapsedTime);
nlassert(CPSLocated::_Collisions.size() >= _Size);
computeCollisions(0, &_Pos[0], &(*_CollisionNextPos)[0]);
// update new poositions by just swapping the 2 vectors
_CollisionNextPos->swap(_Pos);
}
}
}
/// ***************************************************************************************
void CPSLocated::computeNewParticleMotion(uint firstInstanceIndex)
{
NL_PS_FUNC(CPSLocated_computeNewParticleMotion)
nlassert(_CollisionNextPos);
resetCollisions(_Size);
computeCollisions(firstInstanceIndex, &CParticleSystem::_SpawnPos[0], &_Pos[0]);
}
/// ***************************************************************************************
void CPSLocated::resetCollisions(uint numInstances)
{
NL_PS_FUNC(CPSLocated_resetCollisions)
CPSCollisionInfo *currCollision = _FirstCollision;
while (currCollision)
{
currCollision->Dist = -1.f;
currCollision = currCollision->Next;
}
_FirstCollision = NULL;
if (numInstances > _Collisions.size())
{
uint oldSize = (uint) _Collisions.size();
_Collisions.resize(numInstances);
for(uint k = oldSize; k < numInstances; ++k)
{
_Collisions[k].Index = k;
}
}
}
/// ***************************************************************************************
void CPSLocated::updateCollisions()
{
NL_PS_FUNC(CPSLocated_updateCollisions)
CPSCollisionInfo *currCollision = _FirstCollision;
if (getLastForever())
{
while (currCollision)
{
_Pos[currCollision->Index] = currCollision->NewPos;
std::swap(_Speed[currCollision->Index], currCollision->NewSpeed); // keep speed because may be needed when removing particles
// notify each located bindable that a bounce occured ...
for (TLocatedBoundCont::iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
(*it)->bounceOccured(currCollision->Index, computeDateFromCollisionToNextSimStep(currCollision->Index, getAgeInSeconds(currCollision->Index)));
}
if (currCollision->CollisionZone->getCollisionBehaviour() == CPSZone::destroy)
{
#ifdef NL_DEBUG
nlassert(CParticleSystem::_ParticleRemoveListIndex[currCollision->Index] == -1);
#endif
CParticleSystem::_ParticleToRemove.push_back(currCollision->Index);
#ifdef NL_DEBUG
nlassert(CParticleSystem::_ParticleToRemove.size() <= _Size);
#endif
CParticleSystem::_ParticleRemoveListIndex[currCollision->Index] = (sint)CParticleSystem::_ParticleToRemove.size() - 1;
}
currCollision = currCollision->Next;
}
}
else
{
while (currCollision)
{
if (_Time[currCollision->Index] >= 1.f)
{
// check whether particles died before the collision. If so, just continue (particle has already been inserted in the remove list), and cancel the collision
float timeToCollision = currCollision->Dist / _Speed[currCollision->Index].norm();
if (_Time[currCollision->Index] / _TimeIncrement[currCollision->Index] - timeToCollision * CParticleSystem::RealEllapsedTimeRatio >= 1.f)
{
// says that collision did not occurs
currCollision->Dist = -1.f;
currCollision = currCollision->Next;
continue;
}
}
// if particle is too old, check whether it died before the collision
_Pos[currCollision->Index] = currCollision->NewPos;
std::swap(_Speed[currCollision->Index], currCollision->NewSpeed);
// notify each located bindable that a bounce occured ...
if (!_LocatedBoundCont.empty())
{
TAnimationTime timeFromcollisionToNextSimStep = computeDateFromCollisionToNextSimStep(currCollision->Index, getAgeInSeconds(currCollision->Index));
for (TLocatedBoundCont::iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
(*it)->bounceOccured(currCollision->Index, timeFromcollisionToNextSimStep);
}
}
if (currCollision->CollisionZone->getCollisionBehaviour() == CPSZone::destroy)
{
if (_Time[currCollision->Index] < 1.f)
{
// insert particle only if not already dead
#ifdef NL_DEBUG
nlassert(CParticleSystem::_ParticleRemoveListIndex[currCollision->Index] == -1);
#endif
CParticleSystem::_ParticleToRemove.push_back(currCollision->Index);
#ifdef NL_DEBUG
nlassert(CParticleSystem::_ParticleToRemove.size() <= _Size);
#endif
CParticleSystem::_ParticleRemoveListIndex[currCollision->Index] = (sint)CParticleSystem::_ParticleToRemove.size() - 1;
}
}
currCollision = currCollision->Next;
}
}
}
/// ***************************************************************************************
void CPSLocated::doLODDegradation()
{
NL_PS_FUNC(CPSLocated_doLODDegradation)
nlassert(CParticleSystem::InsideSimLoop);
nlassert(!CParticleSystem::InsideRemoveLoop);
CParticleSystem::InsideRemoveLoop = true;
if (CParticleSystem::EllapsedTime > 0)
{
nlassert(_Owner);
// compute the number of particles to show
const uint maxToHave = (uint) (_MaxSize * _Owner->getOneMinusCurrentLODRatio());
if (_Size > maxToHave) // too much instances ?
{
// choose a random element to start at, and a random step
// this will avoid a pulse effect when the system is far away
uint pos = maxToHave ? rand() % maxToHave : 0;
uint step = maxToHave ? rand() % maxToHave : 0;
do
{
deleteElement(pos);
pos += step;
if (pos >= maxToHave) pos -= maxToHave;
}
while (_Size !=maxToHave);
}
}
CParticleSystem::InsideRemoveLoop = false;
}
/// ***************************************************************************************
void CPSLocated::step(TPSProcessPass pass)
{
NL_PS_FUNC(CPSLocated_step)
CHECK_PS_INTEGRITY
if (!_Size) return;
if (pass != PSMotion)
{
{
/*
uint64 *target;
switch(pass)
{
case PSEmit: target = &PSStatEmit; break;
case PSCollision: target = &PSStatCollision; break;
default:
target = &PSStatRender;
break;
}
MINI_TIMER(*target)
*/
// apply the pass to all bound objects
for (TLocatedBoundCont::iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
if ((*it)->isActive())
{
if ((*it)->getLOD() == PSLod1n2 || _Owner->getLOD() == (*it)->getLOD()) // has this object the right LOD ?
{
(*it)->step(pass);
}
}
}
}
}
else
{
for (TLocatedBoundCont::iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
if ((*it)->isActive())
{
(*it)->step(pass);
}
}
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::updateLife()
{
NL_PS_FUNC(CPSLocated_updateLife)
CHECK_PS_INTEGRITY
if (!_Size) return;
if (! _LastForever)
{
if (_LifeScheme != NULL)
{
TPSAttribTime::iterator itTime = _Time.begin(), itTimeInc = _TimeIncrement.begin();
for (uint32 k = 0; k < _Size; ++k)
{
*itTime += CParticleSystem::RealEllapsedTime * *itTimeInc;
if (*itTime >= 1.0f)
{
#ifdef NL_DEBUG
nlassert(CParticleSystem::_ParticleRemoveListIndex[k] == -1);
#endif
CParticleSystem::_ParticleToRemove.push_back(k);
#ifdef NL_DEBUG
nlassert(CParticleSystem::_ParticleToRemove.size() <= _Size);
#endif
CParticleSystem::_ParticleRemoveListIndex[k] = (sint)CParticleSystem::_ParticleToRemove.size() - 1;
}
++itTime;
++itTimeInc;
}
}
else /// all particles have the same lifetime
{
if (_InitialLife != 0)
{
nlassert(_Owner);
float timeInc = CParticleSystem::RealEllapsedTime / _InitialLife;
if (_Owner->getSystemDate() + 0.1f + 2.f * timeInc >= (_InitialLife - CParticleSystem::RealEllapsedTime))
{
// NB : 0.1f + 2.f * timeInc added to avoid case were time of particle is slighty greater than 1.f after life update because that test failed
TPSAttribTime::iterator itTime = _Time.begin();
for (uint32 k = 0; k < _Size; ++k)
{
*itTime += timeInc;
if (*itTime >= 1.0f)
{
#ifdef NL_DEBUG
nlassert(CParticleSystem::_ParticleRemoveListIndex[k] == -1);
#endif
CParticleSystem::_ParticleToRemove.push_back(k);
#ifdef NL_DEBUG
nlassert(CParticleSystem::_ParticleToRemove.size() <= _Size);
#endif
CParticleSystem::_ParticleRemoveListIndex[k] = (sint)CParticleSystem::_ParticleToRemove.size() - 1;
}
++ itTime;
}
}
else
{
// system has not lasted enough for any particle to die
TPSAttribTime::iterator itTime = _Time.begin(), itEndTime = _Time.end();
do
{
*itTime += timeInc;
++itTime;
}
while (itTime != itEndTime);
}
}
else
{
for(uint k = 0; k < _Size; ++k)
{
#ifdef NL_DEBUG
nlassert(CParticleSystem::_ParticleRemoveListIndex[k] == -1);
#endif
CParticleSystem::_ParticleToRemove.push_back(k);
#ifdef NL_DEBUG
nlassert(CParticleSystem::_ParticleToRemove.size() <= _Size);
#endif
CParticleSystem::_ParticleRemoveListIndex[k] = (sint)CParticleSystem::_ParticleToRemove.size() - 1;
}
}
}
}
else
{
// the time attribute gives the life in seconds
TPSAttribTime::iterator itTime = _Time.begin(), endItTime = _Time.end();
for (; itTime != endItTime; ++itTime)
{
*itTime += CParticleSystem::RealEllapsedTime;
}
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
// When a particle is deleted, it is replaced by the last particle in the array
// if this particle is to be deleted to, must update its new index
static inline void removeParticleFromRemoveList(uint indexToRemove, uint arraySize)
{
NL_PS_FUNC(removeParticleFromRemoveList)
if (indexToRemove != arraySize)
{
if (CParticleSystem::_ParticleRemoveListIndex[arraySize] != -1)
{
// when a particle is deleted, it is replaced by the last particle in the array
// if this particle is to be deleted too, must update its new index (becomes the index of the particle that has just been deleted)
CParticleSystem::_ParticleToRemove[CParticleSystem::_ParticleRemoveListIndex[arraySize]] = indexToRemove;
CParticleSystem::_ParticleRemoveListIndex[indexToRemove] = CParticleSystem::_ParticleRemoveListIndex[arraySize];
CParticleSystem::_ParticleRemoveListIndex[arraySize] = -1; // not to remove any more
}
else
{
CParticleSystem::_ParticleRemoveListIndex[indexToRemove] = -1;
}
}
else
{
CParticleSystem::_ParticleRemoveListIndex[arraySize] = -1;
}
}
void checkRemoveArray(uint size)
{
NL_PS_FUNC(checkRemoveArray)
for(uint k = 0; k < size; ++k)
{
if (CParticleSystem::_ParticleRemoveListIndex[k] != -1)
{
nlassert(std::find(CParticleSystem::_ParticleRemoveListIndex.begin(), CParticleSystem::_ParticleRemoveListIndex.end(), CParticleSystem::_ParticleRemoveListIndex[k]) != CParticleSystem::_ParticleRemoveListIndex.end());
}
}
for(uint k = 0; k < CParticleSystem::_ParticleToRemove.size(); ++k)
{
nlassert(CParticleSystem::_ParticleRemoveListIndex[CParticleSystem::_ParticleToRemove[k]] == (sint) k);
}
}
/// ***************************************************************************************
#ifndef NL_DEBUG
inline
#endif
TAnimationTime CPSLocated::computeDateFromCollisionToNextSimStep(uint particleIndex, float particleAgeInSeconds)
{
NL_PS_FUNC( CPSLocated_computeDateFromCollisionToNextSimStep)
// compute time from the start of the sim step to the birth of the particle (or 0 if already born)
float ageAtStart = CParticleSystem::RealEllapsedTime > particleAgeInSeconds ? CParticleSystem::RealEllapsedTime - particleAgeInSeconds : 0.f;
ageAtStart /= CParticleSystem::RealEllapsedTimeRatio;
// compute time to collision. The 'NewSpeed' field is swapped with speed of particle at the sim step start when 'updateCollision' is called, and thus contains the old speed.
float norm = _Collisions[particleIndex].NewSpeed.norm();
if (norm == 0.f) return 0.f;
float timeToCollision = _Collisions[particleIndex].Dist / norm;
// So time from collision to end of sim step is :
TAnimationTime result = CParticleSystem::EllapsedTime - ageAtStart - timeToCollision;
return std::max(0.f, result);
}
/// ***************************************************************************************
void CPSLocated::removeOldParticles()
{
NL_PS_FUNC(CPSLocated_removeOldParticles)
nlassert(CParticleSystem::RealEllapsedTime > 0.f);
#ifdef NL_DEBUG
CParticleSystem::InsideRemoveLoop = true;
checkRemoveArray(_Size);
#endif
// remove all elements that were marked as too old
// if there are emitters marked as 'on' death, should correct position by moving backward (because motion is done on a whole time step, so particle is further than it should be)
if (getLastForever())
{
// if the particle lasts for ever it can be destroyed only if it touch a collision zone flaged as 'destroy'
// during the call to 'updateCollisions', the list of particles to remove will be updated so just test it
if (hasCollisionInfos())
{
for(std::vector<uint>::iterator it = CParticleSystem::_ParticleToRemove.begin(); it != CParticleSystem::_ParticleToRemove.end(); ++it)
{
if (_Collisions[*it].Dist != -1.f)
{
deleteElement(*it, computeDateFromCollisionToNextSimStep(*it, _Time[*it]));
}
else
{
deleteElement(*it);
}
removeParticleFromRemoveList(*it, _Size);
}
}
}
else
if (hasCollisionInfos()) // particle has collision, and limited lifetime
{
float ellapsedTimeRatio = CParticleSystem::EllapsedTime / CParticleSystem::RealEllapsedTime;
for(std::vector<uint>::iterator it = CParticleSystem::_ParticleToRemove.begin(); it != CParticleSystem::_ParticleToRemove.end(); ++it)
{
TAnimationTime timeUntilNextSimStep;
if (_Collisions[*it].Dist == -1.f)
{
// no collision occured
if (_Time[*it] > 1.f)
{
if (_LifeScheme)
{
_Pos[*it] -= _Speed[*it] * ((_Time[*it] - 1.f) / _TimeIncrement[*it]) * ellapsedTimeRatio;
timeUntilNextSimStep = (_Time[*it] - 1.f) / _TimeIncrement[*it];
}
else
{
_Pos[*it] -= _Speed[*it] * ((_Time[*it] - 1.f) * _InitialLife) * ellapsedTimeRatio;
timeUntilNextSimStep = (_Time[*it] - 1.f) * _InitialLife;
}
_Time[*it] = 0.9999f;
}
else
{
timeUntilNextSimStep = 0.f;
}
}
else
{
// a collision occured before particle died, so pos is already good
if (_LifeScheme)
{
timeUntilNextSimStep = computeDateFromCollisionToNextSimStep(*it, _Time[*it] / _TimeIncrement[*it]);
// compute age of particle when collision occured
_Time[*it] -= timeUntilNextSimStep * _TimeIncrement[*it];
NLMISC::clamp(_Time[*it], 0.f, 1.f); // avoid imprecisions
}
else
{
timeUntilNextSimStep = computeDateFromCollisionToNextSimStep(*it, _Time[*it] * _InitialLife);
// compute age of particle when collision occured
_Time[*it] -= timeUntilNextSimStep / (_InitialLife == 0.f ? 1.f : _InitialLife);
NLMISC::clamp(_Time[*it], 0.f, 1.f); // avoid imprecisions
}
}
deleteElement(*it, timeUntilNextSimStep);
removeParticleFromRemoveList(*it, _Size);
}
}
else // particle has no collisions, and limited lifetime
{
float ellapsedTimeRatio = CParticleSystem::EllapsedTime / CParticleSystem::RealEllapsedTime;
if (!isParametricMotionEnabled())
{
if (_LifeScheme)
{
for(std::vector<uint>::iterator it = CParticleSystem::_ParticleToRemove.begin(); it != CParticleSystem::_ParticleToRemove.end(); ++it)
{
#ifdef NL_DEBUG
for(std::vector<uint>::iterator it2 = it; it2 != CParticleSystem::_ParticleToRemove.end(); ++it2)
{
nlassert(*it2 < _Size);
}
#endif
TAnimationTime timeUntilNextSimStep;
if (_Time[*it] > 1.f)
{
// move position backward (compute its position at death)
timeUntilNextSimStep = ((_Time[*it] - 1.f) / _TimeIncrement[*it]) * ellapsedTimeRatio;
_Pos[*it] -= _Speed[*it] * timeUntilNextSimStep;
// force time to 1 because emitter 'on death' may rely on the date of emitter to compute its attributes
_Time[*it] = 0.9999f;
}
else
{
timeUntilNextSimStep = 0.f;
}
deleteElement(*it, timeUntilNextSimStep);
removeParticleFromRemoveList(*it, _Size);
#ifdef NL_DEBUG
for(std::vector<uint>::iterator it2 = it + 1; it2 != CParticleSystem::_ParticleToRemove.end(); ++it2)
{
nlassert(*it2 < _Size);
}
#endif
}
}
else
{
for(std::vector<uint>::iterator it = CParticleSystem::_ParticleToRemove.begin(); it != CParticleSystem::_ParticleToRemove.end(); ++it)
{
TAnimationTime timeUntilNextSimStep;
if (_Time[*it] > 1.f)
{
// move position backward
timeUntilNextSimStep = (_Time[*it] - 1.f) * _InitialLife * ellapsedTimeRatio;
_Pos[*it] -= _Speed[*it] * timeUntilNextSimStep;
// force time to 1 because emitter 'on death' may rely on the date of emitter to compute its attributes
_Time[*it] = 0.9999f;
}
else
{
timeUntilNextSimStep = 0.f;
}
deleteElement(*it, timeUntilNextSimStep);
removeParticleFromRemoveList(*it, _Size);
}
}
}
else
{
// parametric case
if (_LifeScheme)
{
for(std::vector<uint>::iterator it = CParticleSystem::_ParticleToRemove.begin(); it != CParticleSystem::_ParticleToRemove.end(); ++it)
{
TAnimationTime timeUntilNextSimStep;
if (_Time[*it] > 1.f)
{
// move position backward (compute its position at death)
timeUntilNextSimStep = (_Time[*it] - 1.f) / _TimeIncrement[*it];
computeParametricPos(_Owner->getSystemDate() + CParticleSystem::RealEllapsedTime - timeUntilNextSimStep, *it, _Pos[*it]);
timeUntilNextSimStep *= ellapsedTimeRatio;
// force time to 1 because emitter 'on death' may rely on the date of emitter to compute its attributes
_Time[*it] = 0.9999f;
}
else
{
timeUntilNextSimStep = 0.f;
}
deleteElement(*it, timeUntilNextSimStep);
removeParticleFromRemoveList(*it, _Size);
}
}
else
{
for(std::vector<uint>::iterator it = CParticleSystem::_ParticleToRemove.begin(); it != CParticleSystem::_ParticleToRemove.end(); ++it)
{
TAnimationTime timeUntilNextSimStep;
if (_Time[*it] > 1.f)
{
// move position backward
timeUntilNextSimStep = (_Time[*it] - 1.f) * _InitialLife;
computeParametricPos(_Owner->getSystemDate() + CParticleSystem::RealEllapsedTime - timeUntilNextSimStep, *it, _Pos[*it]);
timeUntilNextSimStep *= ellapsedTimeRatio;
// force time to 1 because emitter 'on death' may rely on the date of emitter to compute its attributes
_Time[*it] = 0.9999f;
}
else
{
timeUntilNextSimStep = 0.f;
}
deleteElement(*it, timeUntilNextSimStep);
removeParticleFromRemoveList(*it, _Size);
}
}
}
}
#ifdef NL_DEBUG
CParticleSystem::InsideRemoveLoop = false;
#endif
CParticleSystem::_ParticleToRemove.clear();
#ifdef NL_DEBUG
if (!_LastForever)
{
for(uint k = 0; k < _Size; ++k)
{
nlassert(_Time[k] >= 0.f && _Time[k] <= 1.f);
}
}
#endif
}
/// ***************************************************************************************
void CPSLocated::addNewlySpawnedParticles()
{
NL_PS_FUNC(CPSLocated_addNewlySpawnedParticles)
#ifdef NL_DEBUG
CParticleSystem::InsideNewElementsLoop = true;
#endif
CParticleSystem::CSpawnVect &spawns = *CParticleSystem::_Spawns[getIndex()];
if (spawns.SpawnInfos.empty()) return;
uint numSpawns = 0;
if (!_Owner->getAutoCountFlag())
{
CParticleSystem::_SpawnPos.resize(getMaxSize());
numSpawns = std::min((uint) (_MaxSize - _Size), (uint) spawns.SpawnInfos.size());
}
else
{
numSpawns = (uint) spawns.SpawnInfos.size();
}
CParticleSystem::TSpawnInfoVect::const_iterator endIt = spawns.SpawnInfos.begin() + numSpawns;
if (_LastForever)
{
for (CParticleSystem::TSpawnInfoVect::const_iterator it = spawns.SpawnInfos.begin(); it !=endIt; ++it)
{
// sint32 insertionIndex =
newElement(*it, false, CParticleSystem::EllapsedTime);
}
}
else
{
// to avoid warning in autocount mode
//CParticleSystem::InsideSimLoop = false;
for (CParticleSystem::TSpawnInfoVect::const_iterator it = spawns.SpawnInfos.begin(); it !=endIt; ++it)
{
sint32 insertionIndex = newElement(*it, false, CParticleSystem::EllapsedTime);
#ifdef NL_DEBUG
nlassert(insertionIndex != -1);
#endif
if (_Time[insertionIndex] >= 1.f)
{
#ifdef NL_DEBUG
nlassert(CParticleSystem::_ParticleRemoveListIndex[insertionIndex] == -1);
#endif
CParticleSystem::_ParticleToRemove.push_back(insertionIndex);
#ifdef NL_DEBUG
nlassert(CParticleSystem::_ParticleToRemove.size() <= _Size);
#endif
CParticleSystem::_ParticleRemoveListIndex[insertionIndex] = (sint)CParticleSystem::_ParticleToRemove.size() - 1;
}
}
//CParticleSystem::InsideSimLoop = true;
}
spawns.SpawnInfos.clear();
#ifdef NL_DEBUG
CParticleSystem::InsideNewElementsLoop = false;
#endif
}
/// ***************************************************************************************
bool CPSLocated::computeBBox(NLMISC::CAABBox &box) const
{
NL_PS_FUNC(CPSLocated_computeBBox)
CHECK_PS_INTEGRITY
if (!_Size) return false; // something to compute ?
TLocatedBoundCont::const_iterator it;
TPSAttribVector::const_iterator it2;
// check whether any object bound to us need a bbox
for (it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
if ((*it)->doesProduceBBox())
{
break;
}
}
if (it == _LocatedBoundCont.end())
{
return false;
}
CVector min = _Pos[0], max = _Pos[0];
for (it2 = _Pos.begin(); it2 != _Pos.end(); ++ it2)
{
const CVector &v = (*it2);
min.minof(min, v);
max.maxof(max, v);
}
box.setMinMax(min, max);
// we've considered that located had no extent in space
// now, we must call each objects that are bound to the located in order
// to complete the bbox if they have no null extent
NLMISC::CAABBox tmpBox, startBox = box;
for (it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
if ((*it)->doesProduceBBox())
{
tmpBox = startBox;
if ((*it)->completeBBox(tmpBox))
{
box = NLMISC::CAABBox::computeAABBoxUnion(tmpBox, box);
}
}
}
CHECK_PS_INTEGRITY
return true;
}
/// Setup the driver model matrix. It is set accordingly to the basis used for rendering
/// ***************************************************************************************
void CPSLocated::setupDriverModelMatrix(void)
{
NL_PS_FUNC(CPSLocated_setupDriverModelMatrix)
CHECK_PS_INTEGRITY
getDriver()->setupModelMatrix(getLocalToWorldMatrix());
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::queryCollisionInfo(void)
{
NL_PS_FUNC(CPSLocated_queryCollisionInfo)
CHECK_PS_INTEGRITY
if (_CollisionInfoNbRef)
{
++ _CollisionInfoNbRef;
}
else
{
_CollisionNextPos = new TPSAttribVector;
_CollisionInfoNbRef = 1;
_CollisionNextPos ->resize(_MaxSize);
for(uint k = 0; k < _Size; ++k)
{
_CollisionNextPos->insert();
}
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::releaseCollisionInfo(void)
{
NL_PS_FUNC(CPSLocated_releaseCollisionInfo)
CHECK_PS_INTEGRITY
nlassert(_CollisionInfoNbRef); // check whether queryCollisionInfo was called
// so the number of refs must not = 0
--_CollisionInfoNbRef;
if (_CollisionInfoNbRef == 0)
{
delete _CollisionNextPos;
_CollisionNextPos = NULL;
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::registerIntegrableForce(CPSForce *f)
{
NL_PS_FUNC(CPSLocated_registerIntegrableForce)
CHECK_PS_INTEGRITY
nlassert(std::find(_IntegrableForces.begin(), _IntegrableForces.end(), f) == _IntegrableForces.end()); // force registered twice
_IntegrableForces.push_back(f);
if (getMatrixMode() != f->getOwner()->getMatrixMode())
{
++_NumIntegrableForceWithDifferentBasis;
releaseParametricInfos();
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::unregisterIntegrableForce(CPSForce *f)
{
NL_PS_FUNC(CPSLocated_unregisterIntegrableForce)
CHECK_PS_INTEGRITY
nlassert(f->getOwner()); // f must be attached to a located
CPSVector<CPSForce *>::V::iterator it = std::find(_IntegrableForces.begin(), _IntegrableForces.end(), f);
nlassert(it != _IntegrableForces.end() );
_IntegrableForces.erase(it);
if (getMatrixMode() != f->getOwner()->getMatrixMode())
{
--_NumIntegrableForceWithDifferentBasis;
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::addNonIntegrableForceRef(void)
{
NL_PS_FUNC(CPSLocated_addNonIntegrableForceRef)
CHECK_PS_INTEGRITY
++_NonIntegrableForceNbRefs;
releaseParametricInfos();
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::releaseNonIntegrableForceRef(void)
{
NL_PS_FUNC(CPSLocated_releaseNonIntegrableForceRef)
CHECK_PS_INTEGRITY
nlassert(_NonIntegrableForceNbRefs != 0);
--_NonIntegrableForceNbRefs;
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
void CPSLocated::integrableForceBasisChanged(TPSMatrixMode matrixMode)
{
NL_PS_FUNC(CPSLocated_integrableForceBasisChanged)
CHECK_PS_INTEGRITY
if (getMatrixMode() != matrixMode)
{
++_NumIntegrableForceWithDifferentBasis;
releaseParametricInfos();
}
else
{
--_NumIntegrableForceWithDifferentBasis;
}
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
CPSLocatedBindable *CPSLocated::unbind(uint index)
{
NL_PS_FUNC(CPSLocated_unbind)
CHECK_PS_INTEGRITY
nlassert(index < _LocatedBoundCont.size());
CPSLocatedBindable *lb = _LocatedBoundCont[index];
lb->setOwner(NULL);
_LocatedBoundCont.erase(_LocatedBoundCont.begin() + index);
return lb;
CHECK_PS_INTEGRITY
}
/// ***************************************************************************************
bool CPSLocated::isBound(const CPSLocatedBindable *lb) const
{
NL_PS_FUNC(CPSLocated_isBound)
CHECK_PS_INTEGRITY
TLocatedBoundCont::const_iterator it = std::find(_LocatedBoundCont.begin(), _LocatedBoundCont.end(), lb);
return it != _LocatedBoundCont.end();
}
/// ***************************************************************************************
uint CPSLocated::getIndexOf(const CPSLocatedBindable *lb) const
{
NL_PS_FUNC(CPSLocated_getIndexOf)
CHECK_PS_INTEGRITY
for(uint k = 0; k < _LocatedBoundCont.size(); ++k)
{
if (_LocatedBoundCont[k] == lb) return k;
}
nlassert(0);
return 0;
}
///////////////////////////////////////
// CPSLocatedBindable implementation //
///////////////////////////////////////
/// ***************************************************************************************
CPSLocatedBindable::CPSLocatedBindable() : _Owner(NULL), _ExternID(0), _LOD(PSLod1n2), _Active(true)
{
NL_PS_FUNC(CPSLocatedBindable_CPSLocatedBindable)
_Owner = NULL;
}
/// ***************************************************************************************
void CPSLocatedBindable::setOwner(CPSLocated *psl)
{
NL_PS_FUNC(CPSLocatedBindable_setOwner)
if (psl == _Owner) return;
if (psl == NULL)
{
releaseAllRef();
if (_Owner)
{
// empty this located bindable. Need to be empty if it must be rebound to another located.
for (uint k = 0; k < _Owner->getSize(); ++k)
{
deleteElement(0);
}
}
}
if (_Owner && _Owner->getOwner())
{
_Owner->getOwner()->releaseRefForUserSysCoordInfo(getUserMatrixUsageCount());
}
_Owner = psl;
if (_Owner && _Owner->getOwner())
{
_Owner->getOwner()->addRefForUserSysCoordInfo(getUserMatrixUsageCount());
}
}
/// ***************************************************************************************
void CPSLocatedBindable::finalize(void)
{
NL_PS_FUNC(CPSLocatedBindable_finalize)
if (_Owner && _Owner->getOwner())
{
_Owner->getOwner()->releaseRefForUserSysCoordInfo(getUserMatrixUsageCount());
}
}
/// ***************************************************************************************
CPSLocatedBindable::~CPSLocatedBindable()
{
NL_PS_FUNC(CPSLocatedBindable_CPSLocatedBindableDtor)
if (_ExternID)
{
if (_Owner && _Owner->getOwner())
{
_Owner->getOwner()->unregisterLocatedBindableExternID(this);
}
}
}
/// ***************************************************************************************
void CPSLocatedBindable::notifyTargetRemoved(CPSLocated *ptr)
{
NL_PS_FUNC(CPSLocatedBindable_notifyTargetRemoved)
ptr->unregisterDtorObserver(this);
}
/// ***************************************************************************************
void CPSLocatedBindable::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
{
NL_PS_FUNC(CPSLocatedBindable_IStream )
sint ver = f.serialVersion(4);
f.serialPtr(_Owner);
if (ver > 1) f.serialEnum(_LOD);
if (ver > 2)
{
if (f.isReading() && !CParticleSystem::getSerializeIdentifierFlag())
{
// just skip the name
sint32 len;
f.serial(len);
f.seek(len, NLMISC::IStream::current);
}
else
{
f.serial(_Name);
}
}
if (ver > 3)
{
if (f.isReading())
{
uint32 id;
f.serial(id);
setExternID(id);
}
else
{
f.serial(_ExternID);
}
}
}
/// ***************************************************************************************
void CPSLocatedBindable::displayIcon2d(const CVector tab[], uint nbSegs, float scale)
{
NL_PS_FUNC(CPSLocatedBindable_displayIcon2d)
uint32 size = _Owner->getSize();
if (!size) return;
setupDriverModelMatrix();
const CVector I = computeI();
const CVector K = computeK();
static std::vector<NLMISC::CLine> lines;
lines.clear();
// 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];
for (uint l = 0; l < nbSegs; ++l)
{
NLMISC::CLine li;
li.V0 = p + scale * (tab[l << 1].x * I + tab[l << 1].y * K);
li.V1 = p + scale * (tab[(l << 1) + 1].x * I + tab[(l << 1) + 1].y * K);
lines.push_back(li);
}
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() );
}
}
/// ***************************************************************************************
CFontManager *CPSLocatedBindable::getFontManager(void)
{
NL_PS_FUNC(CPSLocatedBindable_getFontManager)
nlassert(_Owner);
return _Owner->getFontManager();
}
/// ***************************************************************************************
/// Shortcut to get the font manager if one was set (const version)
const CFontManager *CPSLocatedBindable::getFontManager(void) const
{
NL_PS_FUNC(CPSLocatedBindable_getFontManager)
nlassert(_Owner);
return _Owner->getFontManager();
}
/// ***************************************************************************************
// Shortcut to get the matrix of the system
const NLMISC::CMatrix &CPSLocatedBindable::getSysMat(void) const
{
NL_PS_FUNC( CPSLocatedBindable_getSysMat)
nlassert(_Owner);
return _Owner->getOwner()->getSysMat();
}
/// ***************************************************************************************
/// shortcut to get the inverted matrix of the system
const NLMISC::CMatrix &CPSLocatedBindable::getInvertedSysMat(void) const
{
NL_PS_FUNC(CPSLocatedBindable_getInvertedSysMat)
nlassert(_Owner);
return _Owner->getOwner()->getInvertedSysMat();
}
/// ***************************************************************************************
/// shortcut to get the view matrix
const NLMISC::CMatrix &CPSLocatedBindable::getViewMat(void) const
{
NL_PS_FUNC(CPSLocatedBindable_getViewMat)
nlassert(_Owner);
return _Owner->getOwner()->getViewMat();
}
/// ***************************************************************************************
/// shortcut to get the inverted view matrix
const NLMISC::CMatrix &CPSLocatedBindable::getInvertedViewMat(void) const
{
NL_PS_FUNC(CPSLocatedBindable_getInvertedViewMat)
nlassert(_Owner);
return _Owner->getOwner()->getInvertedViewMat();
}
/// ***************************************************************************************
/// shortcut to setup the model matrix (system basis or world basis)
void CPSLocatedBindable::setupDriverModelMatrix(void)
{
NL_PS_FUNC(CPSLocatedBindable_setupDriverModelMatrix)
nlassert(_Owner);
_Owner->setupDriverModelMatrix();
}
/// ***************************************************************************************
void CPSLocatedBindable::setExternID(uint32 id)
{
NL_PS_FUNC(CPSLocatedBindable_setExternID)
if (id == _ExternID) return;
CParticleSystem *ps = NULL;
if (_Owner && _Owner->getOwner())
{
ps = _Owner->getOwner();
}
if (ps)
{
ps->unregisterLocatedBindableExternID(this);
_ExternID = 0;
}
if (id != 0)
{
if (ps) ps->registerLocatedBindableExternID(id, this);
_ExternID = id;
}
}
/// ***************************************************************************************
void CPSLocatedBindable::releaseAllRef()
{
NL_PS_FUNC(CPSLocatedBindable_releaseAllRef)
}
/////////////////////////////////////////////
// CPSTargetLocatedBindable implementation //
/////////////////////////////////////////////
/// ***************************************************************************************
void CPSTargetLocatedBindable::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
{
NL_PS_FUNC(CPSTargetLocatedBindable_serial)
(void)f.serialVersion(1);
f.serialPtr(_Owner);
f.serial(_Name);
if (f.isReading())
{
// delete previous attached bindables...
for (TTargetCont::iterator it = _Targets.begin(); it != _Targets.end(); ++it)
{
delete (*it);
}
_Targets.clear();
}
f.serialContPolyPtr(_Targets);
}
/// ***************************************************************************************
void CPSTargetLocatedBindable::attachTarget(CPSLocated *ptr)
{
NL_PS_FUNC(CPSTargetLocatedBindable_attachTarget)
// a target can't be shared between different particle systems
#ifdef NL_DEBUG
if (_Owner)
{
nlassert(_Owner->getOwner() == ptr->getOwner());
}
#endif
// see whether this target has not been registered before
nlassert(std::find(_Targets.begin(), _Targets.end(), ptr) == _Targets.end());
_Targets.push_back(ptr);
// we register us to be notified when the target disappear
ptr->registerDtorObserver(this);
}
/// ***************************************************************************************
void CPSTargetLocatedBindable::notifyTargetRemoved(CPSLocated *ptr)
{
NL_PS_FUNC(CPSTargetLocatedBindable_notifyTargetRemoved)
TTargetCont::iterator it = std::find(_Targets.begin(), _Targets.end(), ptr);
nlassert(it != _Targets.end());
releaseTargetRsc(*it);
_Targets.erase(it);
CPSLocatedBindable::notifyTargetRemoved(ptr);
}
// dtor
/// ***************************************************************************************
void CPSTargetLocatedBindable::finalize(void)
{
NL_PS_FUNC(CPSTargetLocatedBindable_finalize)
/** Release the collisionInfos we've querried. We can't do it in the dtor, as calls to releaseTargetRsc wouldn't be polymorphics for derived class!
* And the behaviour of releaseTergetRsc is implemented in derived class
*/
for (TTargetCont::iterator it = _Targets.begin(); it != _Targets.end(); ++it)
{
releaseTargetRsc(*it);
}
CPSLocatedBindable::finalize();
}
/// ***************************************************************************************
CPSTargetLocatedBindable::~CPSTargetLocatedBindable()
{
NL_PS_FUNC(CPSTargetLocatedBindable_CPSTargetLocatedBindable)
// we unregister to all the targets
for (TTargetCont::iterator it = _Targets.begin(); it != _Targets.end(); ++it)
{
(*it)->unregisterDtorObserver(this);
}
}
/// ***************************************************************************************
void CPSTargetLocatedBindable::releaseRefTo(const CParticleSystemProcess *other)
{
NL_PS_FUNC(CPSTargetLocatedBindable_releaseRefTo)
TTargetCont::iterator it = std::find(_Targets.begin(), _Targets.end(), other);
if (it == _Targets.end()) return;
releaseTargetRsc(*it);
(*it)->unregisterDtorObserver(this);
_Targets.erase(it);
nlassert(std::find(_Targets.begin(), _Targets.end(), other) == _Targets.end());
}
/// ***************************************************************************************
void CPSTargetLocatedBindable::releaseAllRef()
{
NL_PS_FUNC(CPSTargetLocatedBindable_releaseAllRef)
for (TTargetCont::iterator it = _Targets.begin(); it != _Targets.end(); ++it)
{
releaseTargetRsc(*it);
(*it)->unregisterDtorObserver(this);
}
_Targets.clear();
CPSLocatedBindable::releaseAllRef();
}
/// ***************************************************************************************
uint CPSLocated::getUserMatrixUsageCount() const
{
NL_PS_FUNC(CPSLocated_getUserMatrixUsageCount)
uint count = 0;
for(TLocatedBoundCont::const_iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
count += (*it)->getUserMatrixUsageCount();
}
return count + CParticleSystemProcess::getUserMatrixUsageCount();
}
/// ***************************************************************************************
void CPSLocated::enumTexs(std::vector<NLMISC::CSmartPtr<ITexture> > &dest, IDriver &drv)
{
NL_PS_FUNC(CPSLocated_enumTexs)
for(TLocatedBoundCont::const_iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
(*it)->enumTexs(dest, drv);
}
}
/// ***************************************************************************************
void CPSLocated::setZBias(float value)
{
NL_PS_FUNC(CPSLocated_setZBias)
for(TLocatedBoundCont::const_iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
(*it)->setZBias(value);
}
}
/// ***************************************************************************************
void CPSLocated::computeCollisions(uint firstInstanceIndex, const NLMISC::CVector *posBefore, const NLMISC::CVector *posAfter)
{
NL_PS_FUNC(CPSLocated_computeCollisions)
for(TDtorObserversVect::iterator it = _DtorObserversVect.begin(); it != _DtorObserversVect.end(); ++it)
{
if ((*it)->getType() == PSZone)
{
static_cast<CPSZone *>(*it)->computeCollisions(*this, firstInstanceIndex, posBefore, posAfter);
}
}
}
/// ***************************************************************************************
void CPSLocated::computeSpawns(uint firstInstanceIndex, bool includeEmitOnce)
{
NL_PS_FUNC(CPSLocated_computeSpawns)
nlassert(CParticleSystem::InsideSimLoop);
for(TLocatedBoundCont::iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
if (!(*it)->isActive()) continue;
if ((*it)->getType() == PSEmitter)
{
CPSEmitter *emit = static_cast<CPSEmitter *>(*it);
emit->updateEmitTrigger();
switch(emit->getEmissionType())
{
case CPSEmitter::regular:
emit->computeSpawns(firstInstanceIndex);
break;
case CPSEmitter::once:
// if we're at first frame, then do emit for each emitter
nlassert(_Owner);
if (_Owner->getSystemDate() == 0.f || includeEmitOnce)
{
// if first pass, then do the emit a single time
// if firstInstanceIndex != 0 then we're dealing with newly created particles, so do the spawn too
emit->doEmitOnce(firstInstanceIndex);
}
break;
default:
break;
}
}
}
}
/// ***************************************************************************************
void CPSLocated::computeForces()
{
NL_PS_FUNC(CPSLocated_computeForces)
nlassert(CParticleSystem::InsideSimLoop);
for(TDtorObserversVect::iterator it = _DtorObserversVect.begin(); it != _DtorObserversVect.end(); ++it)
{
if ((*it)->getType() == PSForce)
{
CPSForce *force = static_cast<CPSForce *>(*it);
force->computeForces(*this);
}
}
}
/// ***************************************************************************************
void CPSCollisionInfo::update(const CPSCollisionInfo &other)
{
NL_PS_FUNC(CPSCollisionInfo_update)
if (Dist == -1)
{
// link collision in the global list of active collisions
Next = CPSLocated::_FirstCollision;
CPSLocated::_FirstCollision = this;
Dist = other.Dist;
NewPos = other.NewPos;
NewSpeed = other.NewSpeed;
CollisionZone = other.CollisionZone;
}
else if (other.Dist < Dist) // is the new collision better (e.g it happens sooner) ?
{
Dist = other.Dist;
NewPos = other.NewPos;
NewSpeed = other.NewSpeed;
CollisionZone = other.CollisionZone;
}
}
/// ***************************************************************************************
void CPSLocated::checkLife() const
{
NL_PS_FUNC(CPSLocated_checkLife)
if (!getLastForever())
{
for(uint k = 0; k < getSize(); ++k)
{
nlassert(getTime()[k] >= 0.f);
nlassert(getTime()[k] <= 1.f);
}
}
}
/// ***************************************************************************************
void CPSLocated::onShow(bool shown)
{
for(TLocatedBoundCont::iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
{
(*it)->onShow(shown);
}
}
} // NL3D