1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "nel/misc/aabbox.h"
23 #include "nel/misc/matrix.h"
24 #include "nel/misc/common.h"
25 #include "nel/3d/ps_util.h"
26 #include "nel/3d/particle_system.h"
27 #include "nel/3d/ps_zone.h"
28 #include "nel/3d/driver.h"
29 #include "nel/3d/material.h"
30 #include "nel/3d/dru.h"
31 #include "nel/3d/ps_located.h"
32 #include "nel/3d/ps_particle.h"
33 #include "nel/3d/ps_force.h"
34 #include "nel/3d/ps_emitter.h"
35 #include "nel/3d/ps_misc.h"
37 #include "nel/misc/line.h"
38 #include "nel/misc/system_info.h"
39 #include "nel/misc/common.h"
42 #include "nel/3d/particle_system_model.h"
46 #define CHECK_PS_INTEGRITY checkIntegrity();
48 #define CHECK_PS_INTEGRITY
58 std::vector
<CPSCollisionInfo
> CPSLocated::_Collisions
;
59 CPSCollisionInfo
*CPSLocated::_FirstCollision
= NULL
;
63 /// ***************************************************************************************
67 CPSLocated::CPSLocated() : /*_MaxNumFaces(0),*/
69 _MaxSize(DefaultMaxLocatedInstance
),
70 _CollisionInfoNbRef(0),
71 _CollisionNextPos(NULL
),
76 _LODDegradation(false),
77 _ParametricMotion(false),
78 _TriggerOnDeath(false),
80 _TriggerID(NELID("NONE")),
81 _NonIntegrableForceNbRefs(0),
82 _NumIntegrableForceWithDifferentBasis(0)
84 NL_PS_FUNC(CPSLocated_CPSLocated
)
88 // *****************************************************************************************************
89 const NLMISC::CMatrix
&CPSLocated::getLocalToWorldMatrix() const
91 NL_PS_FUNC(CPSLocated_getLocalToWorldMatrix
)
93 switch(getMatrixMode())
95 case PSFXWorldMatrix
: return _Owner
->getSysMat();
96 case PSIdentityMatrix
: return NLMISC::CMatrix::Identity
;
97 case PSUserMatrix
: return _Owner
->getUserMatrix();
102 return NLMISC::CMatrix::Identity
;
105 // *****************************************************************************************************
106 const NLMISC::CMatrix
&CPSLocated::getWorldToLocalMatrix() const
108 NL_PS_FUNC(CPSLocated_getWorldToLocalMatrix
)
110 switch(getMatrixMode())
112 case PSFXWorldMatrix
: return _Owner
->getInvertedSysMat();
113 case PSIdentityMatrix
: return NLMISC::CMatrix::Identity
;
114 case PSUserMatrix
: return _Owner
->getInvertedUserMatrix();
119 return NLMISC::CMatrix::Identity
;
123 /// ***************************************************************************************
124 float CPSLocated::evalMaxDuration() const
126 NL_PS_FUNC(CPSLocated_evalMaxDuration
)
127 if (_LastForever
) return -1.f
;
128 return _LifeScheme
? _LifeScheme
->getMaxValue() : _InitialLife
;
132 /// ***************************************************************************************
133 void CPSLocated::checkIntegrity() const
135 NL_PS_FUNC(CPSLocated_checkIntegrity
)
136 nlassert(_InvMass
.getMaxSize() == _Pos
.getMaxSize());
137 nlassert(_Pos
.getMaxSize() == _Speed
.getMaxSize());
138 nlassert(_Speed
.getMaxSize() == _Time
.getMaxSize());
139 nlassert(_Time
.getMaxSize() == _TimeIncrement
.getMaxSize());
141 nlassert(_InvMass
.getSize() == _Pos
.getSize());
142 nlassert(_Pos
.getSize() == _Speed
.getSize());
143 nlassert(_Speed
.getSize() == _Time
.getSize());
144 nlassert(_Time
.getSize() == _TimeIncrement
.getSize());
146 if (hasCollisionInfos())
148 nlassert(_CollisionNextPos
->getSize() == _Pos
.getSize());
149 nlassert(_CollisionNextPos
->getMaxSize() == _Pos
.getMaxSize());
154 /// ***************************************************************************************
155 bool CPSLocated::setLastForever()
157 NL_PS_FUNC(CPSLocated_setLastForever
)
160 if (_Owner
&& _Owner
->getBypassMaxNumIntegrationSteps())
162 // Should test that the system is still valid.
163 if (!_Owner
->canFinish())
165 _LastForever
= false;
166 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");
175 /// ***************************************************************************************
176 void CPSLocated::systemDateChanged()
178 NL_PS_FUNC(CPSLocated_systemDateChanged
)
180 for(TLocatedBoundCont::iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
182 (*it
)->systemDateChanged();
188 /// ***************************************************************************************
189 void CPSLocated::releaseRefTo(const CParticleSystemProcess
*other
)
191 NL_PS_FUNC(CPSLocated_releaseRefTo
)
195 for(TLocatedBoundCont::iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
197 (*it
)->releaseRefTo(other
);
203 for(TDtorObserversVect::iterator it
= _DtorObserversVect
.begin(); it
!= _DtorObserversVect
.end(); ++it
)
205 if ((*it
)->getOwner() == other
)
207 CPSLocatedBindable
*refMaker
= *it
;
208 refMaker
->notifyTargetRemoved(this);
216 /// ***************************************************************************************
217 void CPSLocated::releaseAllRef()
219 NL_PS_FUNC(CPSLocated_releaseAllRef
)
223 for(TLocatedBoundCont::iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
225 (*it
)->releaseAllRef();
229 // we must do a copy, because the subsequent call can modify this vector
230 TDtorObserversVect
copyVect(_DtorObserversVect
.begin(), _DtorObserversVect
.end());
231 // call all the dtor observers
232 for (TDtorObserversVect::iterator it
= copyVect
.begin(); it
!= copyVect
.end(); ++it
)
234 (*it
)->notifyTargetRemoved(this);
236 _DtorObserversVect
.clear();
238 nlassert(_CollisionInfoNbRef
== 0); //If this is not = 0, then someone didnt call releaseCollisionInfo
239 // If this happen, you can register with the registerDTorObserver
240 // (observer pattern)
241 // and override notifyTargetRemove to call releaseCollisionInfo
242 nlassert(_IntegrableForces
.empty());
243 nlassert(_NonIntegrableForceNbRefs
== 0);
248 /// ***************************************************************************************
249 void CPSLocated::notifyMotionTypeChanged(void)
251 NL_PS_FUNC(CPSLocated_notifyMotionTypeChanged
)
253 for (TLocatedBoundCont::const_iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
255 (*it
)->motionTypeChanged(_ParametricMotion
);
261 /// ***************************************************************************************
262 void CPSLocated::integrateSingle(float startDate
, float deltaT
, uint numStep
,
263 uint32 indexInLocated
,
264 NLMISC::CVector
*destPos
,
267 NL_PS_FUNC(CPSLocated_integrateSingle
)
269 nlassert(supportParametricMotion() && _ParametricMotion
);
270 if (_IntegrableForces
.size() != 0)
272 bool accumulate
= false;
273 for (TForceVect::const_iterator it
= _IntegrableForces
.begin(); it
!= _IntegrableForces
.end(); ++it
)
275 nlassert((*it
)->isIntegrable());
276 (*it
)->integrateSingle(startDate
, deltaT
, numStep
, this, indexInLocated
, destPos
, accumulate
, stride
);
280 else // no forces applied, just deduce position from date, initial pos and speed
283 NLMISC::CVector
*endPos
= (NLMISC::CVector
*) ( (uint8
*) destPos
+ stride
* numStep
);
285 const CPSLocated::CParametricInfo
&pi
= _PInfo
[indexInLocated
];
286 destPos
= FillBufUsingSubdiv(pi
.Pos
, pi
.Date
, startDate
, deltaT
, numStep
, destPos
, stride
);
289 float currDate
= startDate
- pi
.Date
;
290 nlassert(currDate
>= 0);
294 nlassert(destPos
< endPos
);
296 destPos
->x
= pi
.Pos
.x
+ currDate
* pi
.Speed
.x
;
297 destPos
->y
= pi
.Pos
.y
+ currDate
* pi
.Speed
.y
;
298 destPos
->z
= pi
.Pos
.z
+ currDate
* pi
.Speed
.z
;
300 destPos
= (NLMISC::CVector
*) ( (uint8
*) destPos
+ stride
);
309 /// ***************************************************************************************
310 void CPSLocated::performParametricMotion(TAnimationTime date
)
312 NL_PS_FUNC(CPSLocated_performParametricMotion
)
315 nlassert(supportParametricMotion() && _ParametricMotion
);
317 if (_IntegrableForces
.size() != 0)
319 bool accumulate
= false;
320 for (TForceVect::iterator it
= _IntegrableForces
.begin(); it
!= _IntegrableForces
.end(); ++it
)
322 nlassert((*it
)->isIntegrable());
323 (*it
)->integrate(date
, this, 0, _Size
, &_Pos
[0], &_Speed
[0], accumulate
);
329 CPSLocated::TPSAttribParametricInfo::const_iterator it
= _PInfo
.begin(),
330 endIt
= _PInfo
.end();
331 TPSAttribVector::iterator posIt
= _Pos
.begin();
335 deltaT
= date
- it
->Date
;
336 posIt
->x
= it
->Pos
.x
+ deltaT
* it
->Speed
.x
;
337 posIt
->y
= it
->Pos
.y
+ deltaT
* it
->Speed
.y
;
338 posIt
->z
= it
->Pos
.z
+ deltaT
* it
->Speed
.z
;
347 /// ***************************************************************************************
348 /// allocate parametric infos
349 void CPSLocated::allocateParametricInfos(void)
351 NL_PS_FUNC(CPSLocated_allocateParametricInfos
)
353 if (_ParametricMotion
) return;
354 nlassert(supportParametricMotion());
356 const float date
= _Owner
->getSystemDate();
357 _PInfo
.resize(_MaxSize
);
358 // copy back infos from current position and speeds
359 TPSAttribVector::const_iterator posIt
= _Pos
.begin(), endPosIt
= _Pos
.end();
360 TPSAttribVector::const_iterator speedIt
= _Speed
.begin();
361 while (posIt
!= endPosIt
)
363 _PInfo
.insert( CParametricInfo(*posIt
, *speedIt
, date
) );
366 _ParametricMotion
= true;
367 notifyMotionTypeChanged();
371 /// ***************************************************************************************
372 /// release parametric infos
373 void CPSLocated::releaseParametricInfos(void)
375 NL_PS_FUNC(CPSLocated_releaseParametricInfos
)
377 if (!_ParametricMotion
) return;
378 NLMISC::contReset(_PInfo
);
379 _ParametricMotion
= false;
380 notifyMotionTypeChanged();
384 /// ***************************************************************************************
385 /// Test whether this located support parametric motion
386 bool CPSLocated::supportParametricMotion(void) const
388 NL_PS_FUNC(CPSLocated_supportParametricMotion
)
389 return _NonIntegrableForceNbRefs
== 0 && _NumIntegrableForceWithDifferentBasis
== 0;
392 /// ***************************************************************************************
393 /** When set to true, this tells the system to use parametric motion. This is needed in a few case only,
394 * and can only work if all the forces that apply to the system are integrable
396 void CPSLocated::enableParametricMotion(bool enable
/*= true*/)
398 NL_PS_FUNC(CPSLocated_enableParametricMotion
)
400 nlassert(supportParametricMotion());
403 allocateParametricInfos();
407 releaseParametricInfos();
412 /// ***************************************************************************************
413 void CPSLocated::setMatrixMode(TPSMatrixMode matrixMode
)
415 NL_PS_FUNC(CPSLocated_setMatrixMode
)
417 if (matrixMode
!= getMatrixMode())
419 for (TLocatedBoundCont::const_iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
421 (*it
)->basisChanged(matrixMode
);
424 CParticleSystemProcess::setMatrixMode(matrixMode
);
425 for (TForceVect::iterator fIt
= _IntegrableForces
.begin(); fIt
!= _IntegrableForces
.end(); ++fIt
)
427 integrableForceBasisChanged( (*fIt
)->getOwner()->getMatrixMode() );
433 /// ***************************************************************************************
435 void CPSLocated::notifyMaxNumFacesChanged(void)
440 // we examine whether we have particle attached to us, and ask for the max number of faces they may want
442 for (TLocatedBoundCont::const_iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
444 if ((*it)->getType() == PSParticle)
446 uint maxNumFaces = ((CPSParticle *) (*it))->getMaxNumFaces();
447 ///nlassertex(maxNumFaces < ((1 << 16) - 1), ("%s", (*it)->getClassName().c_str()));
448 _MaxNumFaces += maxNumFaces;
455 /// ***************************************************************************************
456 uint
CPSLocated::getNumWantedTris() const
458 NL_PS_FUNC(CPSLocated_getNumWantedTris
)
460 if (!_Owner
) return 0;
461 uint numWantedTris
= 0;
462 for (TLocatedBoundCont::const_iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
464 if ((*it
)->getType() == PSParticle
)
466 numWantedTris
+= NLMISC::safe_cast
<CPSParticle
*>(*it
)->getNumWantedTris();
470 return numWantedTris
;
474 /// ***************************************************************************************
475 uint CPSLocated::querryMaxWantedNumFaces(void)
481 /// ***************************************************************************************
482 /// tells whether there are alive entities / particles in the system
483 bool CPSLocated::hasParticles() const
485 NL_PS_FUNC(CPSLocated_hasParticles
)
487 for (TLocatedBoundCont::const_iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
489 if ((*it
)->getType() == PSParticle
&& (*it
)->hasParticles()) return true;
495 /// ***************************************************************************************
496 /// tells whether there are alive emitters
497 bool CPSLocated::hasEmitters() const
499 NL_PS_FUNC(CPSLocated_hasEmitters
)
501 for (TLocatedBoundCont::const_iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
503 if ((*it
)->getType() == PSEmitter
&& (*it
)->hasEmitters()) return true;
509 /// ***************************************************************************************
510 void CPSLocated::getLODVect(NLMISC::CVector
&v
, float &offset
, TPSMatrixMode matrixMode
)
512 NL_PS_FUNC(CPSLocated_getLODVect
)
515 _Owner
->getLODVect(v
, offset
, matrixMode
);
519 /// ***************************************************************************************
520 float CPSLocated::getUserParam(uint numParam
) const
522 NL_PS_FUNC(CPSLocated_getUserParam
)
525 return _Owner
->getUserParam(numParam
);
528 /// ***************************************************************************************
529 CScene
*CPSLocated::getScene(void)
531 NL_PS_FUNC(CPSLocated_getScene
)
534 return _Owner
->getScene();
537 /// ***************************************************************************************
538 void CPSLocated::incrementNbDrawnParticles(uint num
)
540 NL_PS_FUNC(CPSLocated_incrementNbDrawnParticles
)
542 CParticleSystem::NbParticlesDrawn
+= num
; // for benchmark purpose
545 /// ***************************************************************************************
546 void CPSLocated::setInitialLife(TAnimationTime lifeTime
)
548 NL_PS_FUNC(CPSLocated_setInitialLife
)
550 _LastForever
= false;
551 _InitialLife
= lifeTime
;
555 /** Reset all particles current date to 0. This is needed because we do not check
556 * if particle life is over when the date of the system has not gone beyond the life duration of particles
558 for (uint k
= 0; k
< _Size
; ++k
)
565 _Owner
->systemDurationChanged();
570 /// ***************************************************************************************
571 void CPSLocated::setLifeScheme(CPSAttribMaker
<float> *scheme
)
573 NL_PS_FUNC(CPSLocated_setLifeScheme
)
576 nlassert(!scheme
->hasMemory()); // scheme with memory is invalid there !!
577 _LastForever
= false;
579 _LifeScheme
= scheme
;
583 _Owner
->systemDurationChanged();
588 /// ***************************************************************************************
589 void CPSLocated::setInitialMass(float mass
)
591 NL_PS_FUNC(CPSLocated_setInitialMass
)
599 /// ***************************************************************************************
600 void CPSLocated::setMassScheme(CPSAttribMaker
<float> *scheme
)
602 NL_PS_FUNC(CPSLocated_setMassScheme
)
605 nlassert(!scheme
->hasMemory()); // scheme with memory is invalid there !!
607 _MassScheme
= scheme
;
611 /// ***************************************************************************************
612 /// get a matrix that helps to express located B coordinate in located A basis
613 const NLMISC::CMatrix
&CPSLocated::getConversionMatrix(const CParticleSystem
&ps
, TPSMatrixMode destMode
, TPSMatrixMode srcMode
)
615 NL_PS_FUNC(CPSLocated_getConversionMatrix
)
618 case PSFXWorldMatrix
:
621 case PSFXWorldMatrix
: return NLMISC::CMatrix::Identity
;
622 case PSIdentityMatrix
: return ps
.getInvertedSysMat();
623 case PSUserMatrix
: return ps
.getUserToFXMatrix();
628 case PSIdentityMatrix
:
631 case PSFXWorldMatrix
: return ps
.getSysMat();
632 case PSIdentityMatrix
: return NLMISC::CMatrix::Identity
;
633 case PSUserMatrix
: return ps
.getUserMatrix();
641 case PSFXWorldMatrix
: return ps
.getFXToUserMatrix();
642 case PSIdentityMatrix
: return ps
.getInvertedUserMatrix();
643 case PSUserMatrix
: return NLMISC::CMatrix::Identity
;
652 return NLMISC::CMatrix::Identity
;
655 /// ***************************************************************************************
656 NLMISC::CVector
CPSLocated::computeI(void) const
658 NL_PS_FUNC(CPSLocated_computeI
)
660 const NLMISC::CMatrix
&sysMat
= _Owner
->getSysMat();
661 if (getMatrixMode() == PSIdentityMatrix
)
663 if (!sysMat
.hasScalePart())
665 return _Owner
->getInvertedViewMat().getI();
669 return sysMat
.getScaleUniform() * _Owner
->getInvertedViewMat().getI();
674 if (!sysMat
.hasScalePart())
676 // we must express the I vector in the system basis, so we need to multiply it by the inverted matrix of the system
677 return getWorldToLocalMatrix().mulVector(_Owner
->getInvertedViewMat().getI());
681 return sysMat
.getScaleUniform() * getWorldToLocalMatrix().mulVector(_Owner
->getInvertedViewMat().getI());
686 /// ***************************************************************************************
687 NLMISC::CVector
CPSLocated::computeIWithZAxisAligned(void) const
689 NL_PS_FUNC(CPSLocated_computeIWithZAxisAligned
)
691 const NLMISC::CMatrix
&sysMat
= _Owner
->getSysMat();
692 const CVector
&camI
= _Owner
->getInvertedViewMat().getI();
693 CVector
I(camI
.x
, camI
.y
, 0.f
);
695 if (getMatrixMode() == PSIdentityMatrix
)
697 if (!sysMat
.hasScalePart())
703 return sysMat
.getScaleUniform() * I
;
708 if (!sysMat
.hasScalePart())
710 // we must express the I vector in the system basis, so we need to multiply it by the inverted matrix of the system
711 return getWorldToLocalMatrix().mulVector(I
);
715 return sysMat
.getScaleUniform() * getWorldToLocalMatrix().mulVector(I
);
720 /// ***************************************************************************************
721 NLMISC::CVector
CPSLocated::computeJ(void) const
723 NL_PS_FUNC(CPSLocated_computeJ
)
725 const NLMISC::CMatrix
&sysMat
= _Owner
->getSysMat();
726 if (getMatrixMode() == PSIdentityMatrix
)
728 if (!sysMat
.hasScalePart())
730 return _Owner
->getInvertedViewMat().getJ();
734 return sysMat
.getScaleUniform() * _Owner
->getInvertedViewMat().getJ();
739 if (!sysMat
.hasScalePart())
741 // we must express the J vector in the system basis, so we need to multiply it by the inverted matrix of the system
742 return getWorldToLocalMatrix().mulVector(_Owner
->getInvertedViewMat().getJ());
746 return sysMat
.getScaleUniform() * getWorldToLocalMatrix().mulVector(_Owner
->getInvertedViewMat().getJ());
751 /// ***************************************************************************************
752 NLMISC::CVector
CPSLocated::computeK(void) const
754 NL_PS_FUNC(CPSLocated_computeK
)
756 const NLMISC::CMatrix
&sysMat
= _Owner
->getSysMat();
757 if (getMatrixMode() == PSIdentityMatrix
)
760 if (!sysMat
.hasScalePart())
762 return _Owner
->getInvertedViewMat().getK();
766 return sysMat
.getScaleUniform() * _Owner
->getInvertedViewMat().getK();
771 if (!sysMat
.hasScalePart())
773 // we must express the K vector in the system basis, so we need to multiply it by the inverted matrix of the system
774 return getWorldToLocalMatrix().mulVector(_Owner
->getInvertedViewMat().getK());
778 return sysMat
.getScaleUniform() * getWorldToLocalMatrix().mulVector(_Owner
->getInvertedViewMat().getK());
783 /// ***************************************************************************************
784 NLMISC::CVector
CPSLocated::computeKWithZAxisAligned(void) const
786 NL_PS_FUNC(CPSLocated_computeKWithZAxisAligned
)
788 const NLMISC::CMatrix
&sysMat
= _Owner
->getSysMat();
789 if (getMatrixMode() == PSIdentityMatrix
)
791 if (!sysMat
.hasScalePart())
797 return CVector(0.f
, 0.f
, sysMat
.getScaleUniform());
802 if (!sysMat
.hasScalePart())
804 // we must express the K vector in the system basis, so we need to multiply it by the inverted matrix of the system
805 return getWorldToLocalMatrix().mulVector(CVector::K
);
809 return getWorldToLocalMatrix().mulVector(CVector(0.f
, 0.f
, sysMat
.getScaleUniform()));
814 /// ***************************************************************************************
815 IDriver
*CPSLocated::getDriver() const
817 NL_PS_FUNC(CPSLocated_getDriver
)
820 nlassert (_Owner
->getDriver() ); // you haven't called setDriver on the system
821 return _Owner
->getDriver();
824 /// ***************************************************************************************
826 CPSLocated::~CPSLocated()
828 NL_PS_FUNC(CPSLocated_CPSLocated
)
830 // we must do a copy, because the subsequent call can modify this vector
831 TDtorObserversVect
copyVect(_DtorObserversVect
.begin(), _DtorObserversVect
.end());
832 // call all the dtor observers
833 for (TDtorObserversVect::iterator it
= copyVect
.begin(); it
!= copyVect
.end(); ++it
)
835 (*it
)->notifyTargetRemoved(this);
838 nlassert(_CollisionInfoNbRef
== 0); //If this is not = 0, then someone didnt call releaseCollisionInfo
839 // If this happen, you can register with the registerDTorObserver
840 // (observer pattern)
841 // and override notifyTargetRemove to call releaseCollisionInfo
842 nlassert(_IntegrableForces
.empty());
843 nlassert(_NonIntegrableForceNbRefs
== 0);
845 // delete all bindable
846 for (TLocatedBoundCont::iterator it2
= _LocatedBoundCont
.begin(); it2
!= _LocatedBoundCont
.end(); ++it2
)
851 _LocatedBoundCont
.clear();
855 delete _CollisionNextPos
;
859 /// ***************************************************************************************
861 * sorted insertion (by decreasing priority order) of a bindable (particle e.g an aspect, emitter) in a located
863 bool CPSLocated::bind(CPSLocatedBindable
*lb
)
865 NL_PS_FUNC(CPSLocated_bind
)
867 nlassert(std::find(_LocatedBoundCont
.begin(), _LocatedBoundCont
.end(), lb
) == _LocatedBoundCont
.end());
868 TLocatedBoundCont::iterator it
= _LocatedBoundCont
.begin();
869 while (it
!= _LocatedBoundCont
.end() && **it
< *lb
) // the "<" operator sort them correctly
874 _LocatedBoundCont
.insert(it
, lb
);
876 lb
->resize(_MaxSize
);
878 // any located bindable that is bound to us should have no element in it for now !!
879 // we resize it anyway...
881 uint32 initialSize
= _Size
;
884 for (uint k
= 0; k
< initialSize
; ++k
)
892 if (_ParametricMotion
) lb
->motionTypeChanged(true);
894 /// the max number of shapes may have changed
895 //notifyMaxNumFacesChanged();
899 CParticleSystem
*ps
= _Owner
;
900 if (ps
->getBypassMaxNumIntegrationSteps())
902 if (!ps
->canFinish())
904 unbind(getIndexOf(lb
));
905 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.");
909 // if there's an extern id, register in lb list
910 if (lb
->getExternID() != 0)
912 // register in ID list
913 ps
->registerLocatedBindableExternID(lb
->getExternID(), lb
);
915 _Owner
->systemDurationChanged();
922 /// ***************************************************************************************
923 void CPSLocated::remove(const CPSLocatedBindable
*p
)
925 NL_PS_FUNC(CPSLocated_remove
)
927 TLocatedBoundCont::iterator it
= std::find(_LocatedBoundCont
.begin(), _LocatedBoundCont
.end(), p
);
928 nlassert(it
!= _LocatedBoundCont
.end());
931 _LocatedBoundCont
.erase(it
);
934 _Owner
->systemDurationChanged();
939 /// ***************************************************************************************
940 void CPSLocated::registerDtorObserver(CPSLocatedBindable
*anObserver
)
942 NL_PS_FUNC(CPSLocated_registerDtorObserver
)
944 // check whether the observer wasn't registered twice
945 nlassert(std::find(_DtorObserversVect
.begin(), _DtorObserversVect
.end(), anObserver
) == _DtorObserversVect
.end());
946 _DtorObserversVect
.push_back(anObserver
);
950 /// ***************************************************************************************
951 void CPSLocated::unregisterDtorObserver(CPSLocatedBindable
*anObserver
)
953 NL_PS_FUNC(CPSLocated_unregisterDtorObserver
)
955 // check that it was registered
956 TDtorObserversVect::iterator it
= std::find(_DtorObserversVect
.begin(), _DtorObserversVect
.end(), anObserver
);
957 nlassert(it
!= _DtorObserversVect
.end());
958 _DtorObserversVect
.erase(it
);
963 /// ***************************************************************************************
964 void CPSLocated::postNewElement(const NLMISC::CVector
&pos
,
965 const NLMISC::CVector
&speed
,
966 CPSLocated
&emitterLocated
,
967 uint32 indexInEmitter
,
968 TPSMatrixMode speedCoordSystem
,
969 TAnimationTime lifeTime
)
971 NL_PS_FUNC(CPSLocated_postNewElement
)
972 nlassert(CParticleSystem::InsideSimLoop
); // should be called only inside the sim loop!
973 // 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
974 // emitter was already destroyed
975 // When postNewElement is called, the particle and the emitter that created it live at the same date, so EmitterLife - ParticleLife should be > 1.f
977 if (!emitterLocated
.getLastForever())
979 if (emitterLocated
._LifeScheme
)
981 emitterLife
= emitterLocated
._Time
[indexInEmitter
] - lifeTime
* CParticleSystem::RealEllapsedTimeRatio
* emitterLocated
._TimeIncrement
[indexInEmitter
];
982 if (emitterLife
>= 1.f
)
984 return; // emitter had finished its life
989 emitterLife
= emitterLocated
._Time
[indexInEmitter
] * emitterLocated
._InitialLife
- lifeTime
* CParticleSystem::RealEllapsedTimeRatio
;
990 if (emitterLife
>= emitterLocated
._InitialLife
)
992 return; // emitter had finished its life
994 if (emitterLocated
._InitialLife
!= 0.f
)
996 emitterLife
/= emitterLocated
._InitialLife
;
1002 emitterLife
= emitterLocated
.getTime()[indexInEmitter
];
1005 // now check that the emitter didn't collide before it spawned a particle
1006 if (emitterLocated
.hasCollisionInfos())
1008 const CPSCollisionInfo
&ci
= _Collisions
[indexInEmitter
];
1009 if (ci
.Dist
!= -1.f
)
1011 // a collision occurred, check time from collision to next time step
1012 if ((emitterLocated
.getPos()[indexInEmitter
] - ci
.NewPos
) * (pos
- ci
.NewPos
) > 0.f
) return; // discard emit that are farther than the collision
1017 // create a request to create a new element
1018 CParticleSystem::CSpawnVect
&sp
= *CParticleSystem::_Spawns
[getIndex()];
1019 if (!_Owner
->getAutoCountFlag() && sp
.MaxNumSpawns
== sp
.SpawnInfos
.size()) return; // no more place to spawn
1020 if (getMaxSize() >= ((1 << 16) - 1)) return;
1021 sp
.SpawnInfos
.resize(sp
.SpawnInfos
.size() + 1);
1022 CPSSpawnInfo
&si
= sp
.SpawnInfos
.back();
1023 si
.EmitterInfo
.Pos
= emitterLocated
.getPos()[indexInEmitter
];
1024 si
.EmitterInfo
.Speed
= emitterLocated
.getSpeed()[indexInEmitter
];
1025 si
.EmitterInfo
.InvMass
= emitterLocated
.getInvMass()[indexInEmitter
];
1026 si
.EmitterInfo
.Life
= emitterLife
;
1027 si
.EmitterInfo
.Loc
= &emitterLocated
;
1030 si
.SpeedCoordSystem
= speedCoordSystem
;
1031 si
.LifeTime
= lifeTime
;
1035 /// ***************************************************************************************
1036 sint32
CPSLocated::newElement(const CPSSpawnInfo
&si
, bool doEmitOnce
/* = false */, TAnimationTime ellapsedTime
)
1038 NL_PS_FUNC(CPSLocated_newElement
)
1040 sint32 creationIndex
;
1042 // get the convertion matrix from the emitter basis to the emittee basis
1043 // if the emitter is null, we assume that the coordinate are given in the chosen basis for this particle type
1044 if (_MaxSize
== _Size
)
1046 if (_Owner
&& _Owner
->getAutoCountFlag() && getMaxSize() < ((1 << 16) - 1) )
1048 // we are probably in edition mode -> auto-count mode helps to compute ideal particle array size
1049 // but at the expense of costly allocations
1050 uint maxSize
= getMaxSize();
1051 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)
1052 resize(maxSize
+ 1);
1053 CParticleSystem::_SpawnPos
.resize(maxSize
+ 1);
1061 // 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.
1062 // 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".
1063 // For now, we manage the local to world case. The world to local is possible, but not very useful
1064 switch(si
.EmitterInfo
.Loc
? si
.EmitterInfo
.Loc
->getMatrixMode() : this->getMatrixMode())
1066 case PSFXWorldMatrix
:
1067 switch(this->getMatrixMode())
1069 case PSFXWorldMatrix
:
1071 creationIndex
=_Pos
.insert(si
.SpawnPos
);
1074 case PSIdentityMatrix
:
1077 _Owner
->interpolateFXPosDelta(fxPosDelta
, si
.LifeTime
);
1078 creationIndex
=_Pos
.insert(_Owner
->getSysMat() * si
.SpawnPos
+ fxPosDelta
);
1084 _Owner
->interpolateFXPosDelta(fxPosDelta
, si
.LifeTime
);
1085 CVector userMatrixPosDelta
;
1086 _Owner
->interpolateUserPosDelta(userMatrixPosDelta
, si
.LifeTime
);
1087 creationIndex
=_Pos
.insert(_Owner
->getInvertedUserMatrix() * (_Owner
->getSysMat() * si
.SpawnPos
+ fxPosDelta
- userMatrixPosDelta
));
1094 case PSIdentityMatrix
:
1095 switch(this->getMatrixMode())
1097 case PSFXWorldMatrix
:
1100 _Owner
->interpolateFXPosDelta(fxPosDelta
, si
.LifeTime
);
1101 creationIndex
=_Pos
.insert(_Owner
->getInvertedSysMat() * (si
.SpawnPos
- fxPosDelta
));
1104 case PSIdentityMatrix
:
1106 creationIndex
=_Pos
.insert(si
.SpawnPos
);
1111 CVector userMatrixPosDelta
;
1112 _Owner
->interpolateUserPosDelta(userMatrixPosDelta
, si
.LifeTime
);
1113 creationIndex
=_Pos
.insert(_Owner
->getInvertedUserMatrix() * (si
.SpawnPos
- userMatrixPosDelta
));
1121 switch(this->getMatrixMode())
1123 case PSFXWorldMatrix
:
1126 _Owner
->interpolateFXPosDelta(fxPosDelta
, si
.LifeTime
);
1127 CVector userMatrixPosDelta
;
1128 _Owner
->interpolateUserPosDelta(userMatrixPosDelta
, si
.LifeTime
);
1129 creationIndex
=_Pos
.insert(_Owner
->getInvertedSysMat() * (_Owner
->getUserMatrix() * si
.SpawnPos
+ userMatrixPosDelta
- fxPosDelta
));
1132 case PSIdentityMatrix
:
1134 CVector userMatrixPosDelta
;
1135 _Owner
->interpolateUserPosDelta(userMatrixPosDelta
, si
.LifeTime
);
1136 creationIndex
=_Pos
.insert(_Owner
->getUserMatrix() * si
.SpawnPos
+ userMatrixPosDelta
);
1141 creationIndex
=_Pos
.insert(si
.SpawnPos
);
1153 nlassert(creationIndex
!= -1); // all attributs must contains the same number of elements
1155 if (si
.SpeedCoordSystem
== this->getMatrixMode()) // is speed vector expressed in the good basis ?
1157 _Speed
.insert(si
.Speed
);
1161 // must do conversion of speed
1163 const NLMISC::CMatrix
&convMat
= getConversionMatrix(*_Owner
, this->getMatrixMode(), si
.SpeedCoordSystem
);
1164 _Speed
.insert(convMat
.mulVector(si
.Speed
));
1167 _InvMass
.insert(1.f
/ ((_MassScheme
&& si
.EmitterInfo
.Loc
) ? _MassScheme
->get(si
.EmitterInfo
) : _InitialMass
) );
1168 if (CParticleSystem::InsideSimLoop
)
1170 CParticleSystem::_SpawnPos
[creationIndex
] = _Pos
[creationIndex
];
1172 // compute age of particle when it has been created
1173 if (getLastForever())
1175 // age of particle is in seconds
1176 _Time
.insert(CParticleSystem::RealEllapsedTimeRatio
* si
.LifeTime
);
1177 _TimeIncrement
.insert(_InitialLife
!= 0.f
? 1.f
/ _InitialLife
: 1.f
);
1181 const float totalLifeTime
= (_LifeScheme
&& si
.EmitterInfo
.Loc
) ? _LifeScheme
->get(si
.EmitterInfo
) : _InitialLife
;
1182 float timeIncrement
= totalLifeTime
? 1.f
/ totalLifeTime
: 10E6f
;
1183 _TimeIncrement
.insert(timeIncrement
);
1184 _Time
.insert(CParticleSystem::RealEllapsedTimeRatio
* si
.LifeTime
* timeIncrement
);
1188 // test whether parametric motion is used, and generate the infos that are needed then
1189 if (_ParametricMotion
)
1191 _PInfo
.insert( CParametricInfo(_Pos
[creationIndex
], _Speed
[creationIndex
], _Owner
->getSystemDate() + CParticleSystem::RealEllapsedTime
- CParticleSystem::RealEllapsedTimeRatio
* si
.LifeTime
) );
1195 _Pos
[creationIndex
] += si
.LifeTime
* _Speed
[creationIndex
];
1198 ///////////////////////////////////////////
1199 // generate datas for all bound objects //
1200 ///////////////////////////////////////////
1201 for (TLocatedBoundCont::iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
1203 (*it
)->newElement(si
.EmitterInfo
);
1204 // 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)
1205 if ((*it
)->getType() == PSEmitter
)
1207 CPSEmitter
*pEmit
= NLMISC::safe_cast
<CPSEmitter
*>(*it
);
1208 pEmit
->_Phase
[creationIndex
] -= std::max(0.f
, (ellapsedTime
- si
.LifeTime
));
1211 if (doEmitOnce
&& !CPSEmitter::getBypassEmitOnDeath())
1213 // can be called only outside the sim loop (when the user triggers an emitters for example)
1214 for (TLocatedBoundCont::iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
1216 if ((*it
)->getType() == PSEmitter
)
1218 CPSEmitter
*pEmit
= NLMISC::safe_cast
<CPSEmitter
*>(*it
);
1219 if (pEmit
->getEmissionType() == CPSEmitter::once
)
1221 for(uint k
= 0; k
< getSize(); ++k
)
1223 pEmit
->singleEmit(k
, 1);
1229 if (_CollisionNextPos
)
1231 _CollisionNextPos
->insert();
1233 ++_Size
; // if this is modified, you must also modify the getNewElementIndex in this class
1234 // because that method give the index of the element being created for overrider of the newElement method
1235 // of the CPSLocatedClass (which is called just above)
1239 return creationIndex
;
1243 /// ***************************************************************************************
1244 sint32
CPSLocated::newElement(const CVector
&pos
, const CVector
&speed
, CPSLocated
*emitter
, uint32 indexInEmitter
,
1245 TPSMatrixMode speedCoordSystem
, bool doEmitOnce
/* = false */)
1247 NL_PS_FUNC(CPSLocated_newElement
)
1249 si
.EmitterInfo
.Loc
= emitter
;
1252 si
.EmitterInfo
.Pos
= emitter
->getPos()[indexInEmitter
];
1253 si
.EmitterInfo
.Speed
= emitter
->getSpeed()[indexInEmitter
];
1254 si
.EmitterInfo
.InvMass
= emitter
->getInvMass()[indexInEmitter
];
1255 si
.EmitterInfo
.Life
= emitter
->getTime()[indexInEmitter
];
1259 si
.EmitterInfo
.Pos
= NLMISC::CVector::Null
;
1260 si
.EmitterInfo
.Speed
= NLMISC::CVector::Null
;
1261 si
.EmitterInfo
.InvMass
= 1.f
;
1262 si
.EmitterInfo
.Life
= 0.f
;
1266 si
.SpeedCoordSystem
= speedCoordSystem
;
1268 return newElement(si
, doEmitOnce
, 0.f
);
1272 /// ***************************************************************************************
1273 static inline uint32
IDToLittleEndian(uint32 input
)
1275 NL_PS_FUNC(IDToLittleEndian
)
1276 #ifdef NL_LITTLE_ENDIAN
1279 return ((input
& (0xff<<24))>>24)
1280 || ((input
& (0xff<<16))>>8)
1281 || ((input
& (0xff<<8))<<8)
1282 || ((input
& 0xff)<<24);
1286 /// ***************************************************************************************
1287 inline void CPSLocated::deleteElementBase(uint32 index
)
1289 NL_PS_FUNC(CPSLocated_deleteElementBase
)
1290 // remove common located's attributes
1291 _InvMass
.remove(index
);
1293 _Speed
.remove(index
);
1294 _Time
.remove(index
);
1295 _TimeIncrement
.remove(index
);
1296 if (_CollisionNextPos
)
1298 _CollisionNextPos
->remove(index
);
1300 if (_ParametricMotion
)
1302 _PInfo
.remove(index
);
1305 if (_TriggerOnDeath
)
1307 const uint32 id
= IDToLittleEndian(_TriggerID
);
1309 uint numLb
= _Owner
->getNumLocatedBindableByExternID(id
);
1310 for (uint k
= 0; k
< numLb
; ++k
)
1312 CPSLocatedBindable
*lb
= _Owner
->getLocatedBindableByExternID(id
, k
);
1313 if (lb
->getType() == PSEmitter
)
1315 CPSEmitter
*e
= NLMISC::safe_cast
<CPSEmitter
*>(lb
);
1316 e
->setEmitTrigger();
1323 /// ***************************************************************************************
1324 void CPSLocated::deleteElement(uint32 index
)
1326 NL_PS_FUNC(CPSLocated_deleteElement
)
1328 if (CParticleSystem::InsideSimLoop
)
1330 nlassert(CParticleSystem::InsideRemoveLoop
);
1334 nlassert(index
< _Size
);
1335 for (TLocatedBoundCont::iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
1337 (*it
)->deleteElement(index
);
1339 deleteElementBase(index
);
1343 /// ***************************************************************************************
1344 void CPSLocated::deleteElement(uint32 index
, TAnimationTime timeToNextSimStep
)
1346 NL_PS_FUNC(CPSLocated_deleteElement
)
1348 if (CParticleSystem::InsideSimLoop
)
1350 nlassert(CParticleSystem::InsideRemoveLoop
);
1353 nlassert(index
< _Size
);
1354 for (TLocatedBoundCont::iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
1356 (*it
)->deleteElement(index
, timeToNextSimStep
);
1358 deleteElementBase(index
);
1361 /// Resize the located container
1362 /// ***************************************************************************************
1363 void CPSLocated::resize(uint32 newSize
)
1365 NL_PS_FUNC(CPSLocated_resize
)
1367 nlassert(newSize
< (1 << 16));
1368 if (newSize
< _Size
)
1370 for (uint32 k
= _Size
- 1; k
>= newSize
; --k
)
1374 if (k
== 0) break; // we're dealing with unsigned quantities
1381 _InvMass
.resize(newSize
);
1382 _Pos
.resize(newSize
);
1383 _Speed
.resize(newSize
);
1384 _Time
.resize(newSize
);
1385 _TimeIncrement
.resize(newSize
);
1387 if (_ParametricMotion
)
1389 _PInfo
.resize(newSize
);
1392 if (_CollisionNextPos
)
1394 _CollisionNextPos
->resize(newSize
);
1398 // resize attributes for all bound objects
1399 for (TLocatedBoundCont::iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
1401 (*it
)->resize(newSize
);
1405 /// compute the new max number of faces
1406 //notifyMaxNumFacesChanged();
1410 // dummy struct for serial of a field that has been removed
1411 class CDummyCollision
1414 void serial(NLMISC::IStream
&f
)
1416 NL_PS_FUNC(CDummyCollision_serial
)
1418 float dummyDist
= 0.f
;
1419 NLMISC::CVector dummyNewPos
, dummyNewSpeed
;
1420 f
.serial(dummyDist
, dummyNewPos
, dummyNewSpeed
);
1424 /// ***************************************************************************************
1425 void CPSLocated::serial(NLMISC::IStream
&f
)
1427 NL_PS_FUNC(CPSLocated_serial
)
1429 // version 7 : - removed the field _NbFramesToSkip to get some space (it is never used)
1430 // - removed the requestStack (system graph can't contains loops now)
1431 // - removed _CollisonInfos because they are now static
1433 // version 4 to version 5 : bugfix with reading of collisions
1434 sint ver
= f
.serialVersion(7);
1435 CParticleSystemProcess::serial(f
);
1437 if (f
.isReading() && !CParticleSystem::getSerializeIdentifierFlag())
1439 // just skip the name
1442 f
.seek(len
, NLMISC::IStream::current
);
1455 // tmp fix : some fx were saved with a life that is != to 0
1456 // this cause an assertion in CPSLocated::updateLife, because all particle are assumed to have an age of 0 when the system is started
1457 // TODO : saving _Time is maybe unecessary... or find something better for CPSLocated::updateLife
1458 uint timeSize
= _Time
.getSize();
1461 std::fill(&_Time
[0], &_Time
[0] + timeSize
, 0.f
);
1464 f
.serial(_TimeIncrement
);
1468 bool lastForever
= _LastForever
;
1469 f
.serial(lastForever
);
1470 _LastForever
= lastForever
;
1474 nlassert(f
.isReading());
1475 // serial a dummy ptr (previous code did a serial ptr)
1480 #ifdef PS_FAST_ALLOC
1481 extern NLMISC::CContiguousBlockAllocator
*PSBlockAllocator
;
1482 NLMISC::CContiguousBlockAllocator
*oldAlloc
= PSBlockAllocator
;
1483 PSBlockAllocator
= NULL
;
1485 static CPSAttrib
<CDummyCollision
> col
;
1488 #ifdef PS_FAST_ALLOC
1489 PSBlockAllocator
= oldAlloc
;
1493 f
.serial(_CollisionInfoNbRef
); // TODO avoid to serialize this ?
1497 if (_CollisionInfoNbRef
)
1499 _CollisionNextPos
= new TPSAttribVector
;
1500 _CollisionNextPos
->resize(_Pos
.getMaxSize());
1501 for(uint k
= 0; k
< _Size
; ++k
)
1503 _CollisionNextPos
->insert();
1507 //CHECK_PS_INTEGRITY
1514 f
.serial(useScheme
);
1517 f
.serialPolyPtr(_LifeScheme
);
1521 f
.serial(_InitialLife
);
1525 f
.serial(useScheme
);
1528 f
.serialPolyPtr(_MassScheme
);
1532 f
.serial(_InitialMass
);
1533 nlassert(_InitialMass
> 0);
1539 bool bFalse
= false, bTrue
= true;
1543 f
.serialPolyPtr(_LifeScheme
);
1548 f
.serial(_InitialLife
);
1553 f
.serialPolyPtr(_MassScheme
);
1558 nlassert(_InitialMass
> 0);
1559 f
.serial(_InitialMass
);
1565 uint32 dummy
= 0; // was previously the field "_NbFramesToSkip"
1569 f
.serialContPolyPtr(_DtorObserversVect
);
1573 nlassert(f
.isReading());
1574 // previously, there was a request stack serialized (because system permitted loops)
1577 nlassert(size
== 0);
1579 for (uint32 k = 0; k < size; ++k)
1581 TNewElementRequestStack::value_type t;
1583 _RequestStack.push(t);
1590 nlassert(f
.isReading());
1592 f
.serial(dummy
); // was previously the flag "_UpdateLock"
1595 f
.serialContPolyPtr(_LocatedBoundCont
);
1598 // check that owners are good
1600 for(uint k
= 0; k
< _LocatedBoundCont
.size(); ++k
)
1602 nlassert(_LocatedBoundCont
[k
]->getOwner() == this);
1608 bool lodDegradation
= _LODDegradation
;
1609 f
.serial(lodDegradation
);
1610 _LODDegradation
= lodDegradation
;
1615 bool parametricMotion
= _ParametricMotion
;
1616 f
.serial(parametricMotion
);
1617 _ParametricMotion
= parametricMotion
;
1622 // evaluate our max number of faces
1623 //notifyMaxNumFacesChanged();
1625 if (_ParametricMotion
)
1627 _ParametricMotion
= false;
1628 allocateParametricInfos();
1629 _ParametricMotion
= true;
1635 bool triggerOnDeath
= _TriggerOnDeath
;
1636 f
.serial(triggerOnDeath
);
1637 _TriggerOnDeath
= triggerOnDeath
;
1638 f
.serial(_TriggerID
);
1643 /// ***************************************************************************************
1644 // integrate speed of particles. Makes eventually use of SSE instructions when present
1645 static void IntegrateSpeed(uint count
, float *src1
, const float *src2
, float *dest
, float ellapsedTime
)
1647 NL_PS_FUNC(IntegrateSpeed
)
1648 #if 0 // this works, but is not enabled for now. The precision is not that good...
1650 #ifdef NL_OS_WINDOWS
1654 if (NLMISC::CCpuInfo::hasSSE()
1655 && ((uint) src1 & 15) == ((uint) src2 & 15)
1656 && ! ((uint) src1 & 3)
1657 && ! ((uint) src2 & 3)
1658 ) // must must be sure that memory alignment is identical
1661 // compute first datas in order to align to 16 byte boudary
1663 uint alignCount = ((uint) src1 >> 2) & 3; // number of float to process
1665 while (alignCount --)
1667 *src1++ += ellapsedTime * *src2 ++;
1672 count -= alignCount;
1675 float et[4] = { ellapsedTime, ellapsedTime, ellapsedTime, ellapsedTime};
1676 // sse part of computation
1689 movaps xmm2, [ebx + 8 * edx]
1690 movaps xmm1, [eax + 8 * edx]
1693 movaps [eax + 8 * edx], xmm1
1699 // proceed with left float
1708 *src1 += ellapsedTime * *src2;
1725 uint countDiv8
= count
>>3;
1726 count
&= 7; // takes count % 8
1730 while (countDiv8
--)
1732 src1
[0] += ellapsedTime
* src2
[0];
1733 src1
[1] += ellapsedTime
* src2
[1];
1734 src1
[2] += ellapsedTime
* src2
[2];
1735 src1
[3] += ellapsedTime
* src2
[3];
1737 src1
[4] += ellapsedTime
* src2
[4];
1738 src1
[5] += ellapsedTime
* src2
[5];
1739 src1
[6] += ellapsedTime
* src2
[6];
1740 src1
[7] += ellapsedTime
* src2
[7];
1747 *src1
++ += ellapsedTime
* *src2
++;
1752 while (countDiv8
--)
1754 dest
[0] = src1
[0] + ellapsedTime
* src2
[0];
1755 dest
[1] = src1
[1] + ellapsedTime
* src2
[1];
1756 dest
[2] = src1
[2] + ellapsedTime
* src2
[2];
1757 dest
[3] = src1
[3] + ellapsedTime
* src2
[3];
1758 dest
[4] = src1
[4] + ellapsedTime
* src2
[4];
1759 dest
[5] = src1
[5] + ellapsedTime
* src2
[5];
1760 dest
[6] = src1
[6] + ellapsedTime
* src2
[6];
1761 dest
[7] = src1
[7] + ellapsedTime
* src2
[7];
1768 *dest
++ = *src1
++ + ellapsedTime
* *src2
++;
1774 /// ***************************************************************************************
1775 void CPSLocated::computeMotion()
1777 NL_PS_FUNC(CPSLocated_computeMotion
)
1779 // there are 2 integration steps : with and without collisions
1780 if (!_CollisionNextPos
) // no collisionner are used
1783 MINI_TIMER(PSMotion3
)
1784 if (_Size
!= 0) // avoid referencing _Pos[0] if there's no size, causes STL vectors to assert...
1785 IntegrateSpeed(_Size
* 3, &_Pos
[0].x
, &_Speed
[0].x
, &_Pos
[0].x
, CParticleSystem::EllapsedTime
);
1791 MINI_TIMER(PSMotion4
)
1792 // compute new position after the timeStep
1793 IntegrateSpeed(_Size
* 3, &_Pos
[0].x
, &_Speed
[0].x
, &(*_CollisionNextPos
)[0].x
, CParticleSystem::EllapsedTime
);
1794 nlassert(CPSLocated::_Collisions
.size() >= _Size
);
1795 computeCollisions(0, &_Pos
[0], &(*_CollisionNextPos
)[0]);
1796 // update new poositions by just swapping the 2 vectors
1797 _CollisionNextPos
->swap(_Pos
);
1804 /// ***************************************************************************************
1805 void CPSLocated::computeNewParticleMotion(uint firstInstanceIndex
)
1807 NL_PS_FUNC(CPSLocated_computeNewParticleMotion
)
1808 nlassert(_CollisionNextPos
);
1809 resetCollisions(_Size
);
1810 computeCollisions(firstInstanceIndex
, &CParticleSystem::_SpawnPos
[0], &_Pos
[0]);
1813 /// ***************************************************************************************
1814 void CPSLocated::resetCollisions(uint numInstances
)
1816 NL_PS_FUNC(CPSLocated_resetCollisions
)
1817 CPSCollisionInfo
*currCollision
= _FirstCollision
;
1818 while (currCollision
)
1820 currCollision
->Dist
= -1.f
;
1821 currCollision
= currCollision
->Next
;
1823 _FirstCollision
= NULL
;
1824 if (numInstances
> _Collisions
.size())
1826 uint oldSize
= (uint
) _Collisions
.size();
1827 _Collisions
.resize(numInstances
);
1828 for(uint k
= oldSize
; k
< numInstances
; ++k
)
1830 _Collisions
[k
].Index
= k
;
1835 /// ***************************************************************************************
1836 void CPSLocated::updateCollisions()
1838 NL_PS_FUNC(CPSLocated_updateCollisions
)
1839 CPSCollisionInfo
*currCollision
= _FirstCollision
;
1840 if (getLastForever())
1842 while (currCollision
)
1844 _Pos
[currCollision
->Index
] = currCollision
->NewPos
;
1845 std::swap(_Speed
[currCollision
->Index
], currCollision
->NewSpeed
); // keep speed because may be needed when removing particles
1846 // notify each located bindable that a bounce occurred ...
1847 for (TLocatedBoundCont::iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
1849 (*it
)->bounceOccurred(currCollision
->Index
, computeDateFromCollisionToNextSimStep(currCollision
->Index
, getAgeInSeconds(currCollision
->Index
)));
1851 if (currCollision
->CollisionZone
->getCollisionBehaviour() == CPSZone::destroy
)
1854 nlassert(CParticleSystem::_ParticleRemoveListIndex
[currCollision
->Index
] == -1);
1856 CParticleSystem::_ParticleToRemove
.push_back(currCollision
->Index
);
1858 nlassert(CParticleSystem::_ParticleToRemove
.size() <= _Size
);
1860 CParticleSystem::_ParticleRemoveListIndex
[currCollision
->Index
] = (sint
)CParticleSystem::_ParticleToRemove
.size() - 1;
1863 currCollision
= currCollision
->Next
;
1868 while (currCollision
)
1870 if (_Time
[currCollision
->Index
] >= 1.f
)
1872 // check whether particles died before the collision. If so, just continue (particle has already been inserted in the remove list), and cancel the collision
1873 float timeToCollision
= currCollision
->Dist
/ _Speed
[currCollision
->Index
].norm();
1874 if (_Time
[currCollision
->Index
] / _TimeIncrement
[currCollision
->Index
] - timeToCollision
* CParticleSystem::RealEllapsedTimeRatio
>= 1.f
)
1876 // says that collision did not occurs
1877 currCollision
->Dist
= -1.f
;
1878 currCollision
= currCollision
->Next
;
1882 // if particle is too old, check whether it died before the collision
1883 _Pos
[currCollision
->Index
] = currCollision
->NewPos
;
1884 std::swap(_Speed
[currCollision
->Index
], currCollision
->NewSpeed
);
1885 // notify each located bindable that a bounce occurred ...
1886 if (!_LocatedBoundCont
.empty())
1888 TAnimationTime timeFromcollisionToNextSimStep
= computeDateFromCollisionToNextSimStep(currCollision
->Index
, getAgeInSeconds(currCollision
->Index
));
1889 for (TLocatedBoundCont::iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
1891 (*it
)->bounceOccurred(currCollision
->Index
, timeFromcollisionToNextSimStep
);
1894 if (currCollision
->CollisionZone
->getCollisionBehaviour() == CPSZone::destroy
)
1896 if (_Time
[currCollision
->Index
] < 1.f
)
1898 // insert particle only if not already dead
1900 nlassert(CParticleSystem::_ParticleRemoveListIndex
[currCollision
->Index
] == -1);
1902 CParticleSystem::_ParticleToRemove
.push_back(currCollision
->Index
);
1904 nlassert(CParticleSystem::_ParticleToRemove
.size() <= _Size
);
1906 CParticleSystem::_ParticleRemoveListIndex
[currCollision
->Index
] = (sint
)CParticleSystem::_ParticleToRemove
.size() - 1;
1909 currCollision
= currCollision
->Next
;
1915 /// ***************************************************************************************
1916 void CPSLocated::doLODDegradation()
1918 NL_PS_FUNC(CPSLocated_doLODDegradation
)
1919 nlassert(CParticleSystem::InsideSimLoop
);
1920 nlassert(!CParticleSystem::InsideRemoveLoop
);
1921 CParticleSystem::InsideRemoveLoop
= true;
1922 if (CParticleSystem::EllapsedTime
> 0)
1925 // compute the number of particles to show
1926 const uint maxToHave
= (uint
) (_MaxSize
* _Owner
->getOneMinusCurrentLODRatio());
1927 if (_Size
> maxToHave
) // too much instances ?
1929 // choose a random element to start at, and a random step
1930 // this will avoid a pulse effect when the system is far away
1932 uint pos
= maxToHave
? rand() % maxToHave
: 0;
1933 uint step
= maxToHave
? rand() % maxToHave
: 0;
1939 if (pos
>= maxToHave
) pos
-= maxToHave
;
1941 while (_Size
!=maxToHave
);
1944 CParticleSystem::InsideRemoveLoop
= false;
1947 /// ***************************************************************************************
1948 void CPSLocated::step(TPSProcessPass pass
)
1950 NL_PS_FUNC(CPSLocated_step
)
1954 if (pass
!= PSMotion
)
1961 case PSEmit: target = &PSStatEmit; break;
1962 case PSCollision: target = &PSStatCollision; break;
1964 target = &PSStatRender;
1969 // apply the pass to all bound objects
1970 for (TLocatedBoundCont::iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
1972 if ((*it
)->isActive())
1974 if ((*it
)->getLOD() == PSLod1n2
|| _Owner
->getLOD() == (*it
)->getLOD()) // has this object the right LOD ?
1984 for (TLocatedBoundCont::iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
1986 if ((*it
)->isActive())
1996 /// ***************************************************************************************
1997 void CPSLocated::updateLife()
1999 NL_PS_FUNC(CPSLocated_updateLife
)
2004 if (_LifeScheme
!= NULL
)
2006 TPSAttribTime::iterator itTime
= _Time
.begin(), itTimeInc
= _TimeIncrement
.begin();
2007 for (uint32 k
= 0; k
< _Size
; ++k
)
2009 *itTime
+= CParticleSystem::RealEllapsedTime
* *itTimeInc
;
2010 if (*itTime
>= 1.0f
)
2013 nlassert(CParticleSystem::_ParticleRemoveListIndex
[k
] == -1);
2015 CParticleSystem::_ParticleToRemove
.push_back(k
);
2017 nlassert(CParticleSystem::_ParticleToRemove
.size() <= _Size
);
2019 CParticleSystem::_ParticleRemoveListIndex
[k
] = (sint
)CParticleSystem::_ParticleToRemove
.size() - 1;
2025 else /// all particles have the same lifetime
2027 if (_InitialLife
!= 0)
2030 float timeInc
= CParticleSystem::RealEllapsedTime
/ _InitialLife
;
2031 if (_Owner
->getSystemDate() + 0.1f
+ 2.f
* timeInc
>= (_InitialLife
- CParticleSystem::RealEllapsedTime
))
2033 // 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
2034 TPSAttribTime::iterator itTime
= _Time
.begin();
2035 for (uint32 k
= 0; k
< _Size
; ++k
)
2038 if (*itTime
>= 1.0f
)
2041 nlassert(CParticleSystem::_ParticleRemoveListIndex
[k
] == -1);
2043 CParticleSystem::_ParticleToRemove
.push_back(k
);
2045 nlassert(CParticleSystem::_ParticleToRemove
.size() <= _Size
);
2047 CParticleSystem::_ParticleRemoveListIndex
[k
] = (sint
)CParticleSystem::_ParticleToRemove
.size() - 1;
2054 // system has not lasted enough for any particle to die
2055 TPSAttribTime::iterator itTime
= _Time
.begin(), itEndTime
= _Time
.end();
2061 while (itTime
!= itEndTime
);
2066 for(uint k
= 0; k
< _Size
; ++k
)
2069 nlassert(CParticleSystem::_ParticleRemoveListIndex
[k
] == -1);
2071 CParticleSystem::_ParticleToRemove
.push_back(k
);
2073 nlassert(CParticleSystem::_ParticleToRemove
.size() <= _Size
);
2075 CParticleSystem::_ParticleRemoveListIndex
[k
] = (sint
)CParticleSystem::_ParticleToRemove
.size() - 1;
2082 // the time attribute gives the life in seconds
2083 TPSAttribTime::iterator itTime
= _Time
.begin(), endItTime
= _Time
.end();
2084 for (; itTime
!= endItTime
; ++itTime
)
2086 *itTime
+= CParticleSystem::RealEllapsedTime
;
2093 /// ***************************************************************************************
2094 // When a particle is deleted, it is replaced by the last particle in the array
2095 // if this particle is to be deleted to, must update its new index
2096 static inline void removeParticleFromRemoveList(uint indexToRemove
, uint arraySize
)
2098 NL_PS_FUNC(removeParticleFromRemoveList
)
2099 if (indexToRemove
!= arraySize
)
2101 if (CParticleSystem::_ParticleRemoveListIndex
[arraySize
] != -1)
2103 // when a particle is deleted, it is replaced by the last particle in the array
2104 // if this particle is to be deleted too, must update its new index (becomes the index of the particle that has just been deleted)
2105 CParticleSystem::_ParticleToRemove
[CParticleSystem::_ParticleRemoveListIndex
[arraySize
]] = indexToRemove
;
2106 CParticleSystem::_ParticleRemoveListIndex
[indexToRemove
] = CParticleSystem::_ParticleRemoveListIndex
[arraySize
];
2107 CParticleSystem::_ParticleRemoveListIndex
[arraySize
] = -1; // not to remove any more
2111 CParticleSystem::_ParticleRemoveListIndex
[indexToRemove
] = -1;
2116 CParticleSystem::_ParticleRemoveListIndex
[arraySize
] = -1;
2120 void checkRemoveArray(uint size
)
2122 NL_PS_FUNC(checkRemoveArray
)
2123 for(uint k
= 0; k
< size
; ++k
)
2125 if (CParticleSystem::_ParticleRemoveListIndex
[k
] != -1)
2127 nlassert(std::find(CParticleSystem::_ParticleRemoveListIndex
.begin(), CParticleSystem::_ParticleRemoveListIndex
.end(), CParticleSystem::_ParticleRemoveListIndex
[k
]) != CParticleSystem::_ParticleRemoveListIndex
.end());
2130 for(uint k
= 0; k
< CParticleSystem::_ParticleToRemove
.size(); ++k
)
2132 nlassert(CParticleSystem::_ParticleRemoveListIndex
[CParticleSystem::_ParticleToRemove
[k
]] == (sint
) k
);
2138 /// ***************************************************************************************
2142 TAnimationTime
CPSLocated::computeDateFromCollisionToNextSimStep(uint particleIndex
, float particleAgeInSeconds
)
2144 NL_PS_FUNC( CPSLocated_computeDateFromCollisionToNextSimStep
)
2145 // compute time from the start of the sim step to the birth of the particle (or 0 if already born)
2146 float ageAtStart
= CParticleSystem::RealEllapsedTime
> particleAgeInSeconds
? CParticleSystem::RealEllapsedTime
- particleAgeInSeconds
: 0.f
;
2147 ageAtStart
/= CParticleSystem::RealEllapsedTimeRatio
;
2148 // 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.
2149 float norm
= _Collisions
[particleIndex
].NewSpeed
.norm();
2150 if (norm
== 0.f
) return 0.f
;
2151 float timeToCollision
= _Collisions
[particleIndex
].Dist
/ norm
;
2152 // So time from collision to end of sim step is :
2153 TAnimationTime result
= CParticleSystem::EllapsedTime
- ageAtStart
- timeToCollision
;
2154 return std::max(0.f
, result
);
2157 /// ***************************************************************************************
2158 void CPSLocated::removeOldParticles()
2160 NL_PS_FUNC(CPSLocated_removeOldParticles
)
2161 nlassert(CParticleSystem::RealEllapsedTime
> 0.f
);
2163 CParticleSystem::InsideRemoveLoop
= true;
2164 checkRemoveArray(_Size
);
2166 // remove all elements that were marked as too old
2167 // 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)
2168 if (getLastForever())
2170 // if the particle lasts for ever it can be destroyed only if it touch a collision zone flaged as 'destroy'
2171 // during the call to 'updateCollisions', the list of particles to remove will be updated so just test it
2172 if (hasCollisionInfos())
2174 for(std::vector
<uint
>::iterator it
= CParticleSystem::_ParticleToRemove
.begin(); it
!= CParticleSystem::_ParticleToRemove
.end(); ++it
)
2176 if (_Collisions
[*it
].Dist
!= -1.f
)
2178 deleteElement(*it
, computeDateFromCollisionToNextSimStep(*it
, _Time
[*it
]));
2184 removeParticleFromRemoveList(*it
, _Size
);
2189 if (hasCollisionInfos()) // particle has collision, and limited lifetime
2191 float ellapsedTimeRatio
= CParticleSystem::EllapsedTime
/ CParticleSystem::RealEllapsedTime
;
2192 for(std::vector
<uint
>::iterator it
= CParticleSystem::_ParticleToRemove
.begin(); it
!= CParticleSystem::_ParticleToRemove
.end(); ++it
)
2194 TAnimationTime timeUntilNextSimStep
;
2195 if (_Collisions
[*it
].Dist
== -1.f
)
2197 // no collision occurred
2198 if (_Time
[*it
] > 1.f
)
2203 _Pos
[*it
] -= _Speed
[*it
] * ((_Time
[*it
] - 1.f
) / _TimeIncrement
[*it
]) * ellapsedTimeRatio
;
2204 timeUntilNextSimStep
= (_Time
[*it
] - 1.f
) / _TimeIncrement
[*it
];
2208 _Pos
[*it
] -= _Speed
[*it
] * ((_Time
[*it
] - 1.f
) * _InitialLife
) * ellapsedTimeRatio
;
2209 timeUntilNextSimStep
= (_Time
[*it
] - 1.f
) * _InitialLife
;
2211 _Time
[*it
] = 0.9999f
;
2215 timeUntilNextSimStep
= 0.f
;
2220 // a collision occurred before particle died, so pos is already good
2223 timeUntilNextSimStep
= computeDateFromCollisionToNextSimStep(*it
, _Time
[*it
] / _TimeIncrement
[*it
]);
2224 // compute age of particle when collision occurred
2225 _Time
[*it
] -= timeUntilNextSimStep
* _TimeIncrement
[*it
];
2226 NLMISC::clamp(_Time
[*it
], 0.f
, 1.f
); // avoid imprecisions
2230 timeUntilNextSimStep
= computeDateFromCollisionToNextSimStep(*it
, _Time
[*it
] * _InitialLife
);
2231 // compute age of particle when collision occurred
2232 _Time
[*it
] -= timeUntilNextSimStep
/ (_InitialLife
== 0.f
? 1.f
: _InitialLife
);
2233 NLMISC::clamp(_Time
[*it
], 0.f
, 1.f
); // avoid imprecisions
2238 deleteElement(*it
, timeUntilNextSimStep
);
2239 removeParticleFromRemoveList(*it
, _Size
);
2242 else // particle has no collisions, and limited lifetime
2244 float ellapsedTimeRatio
= CParticleSystem::EllapsedTime
/ CParticleSystem::RealEllapsedTime
;
2245 if (!isParametricMotionEnabled())
2249 for(std::vector
<uint
>::iterator it
= CParticleSystem::_ParticleToRemove
.begin(); it
!= CParticleSystem::_ParticleToRemove
.end(); ++it
)
2252 for(std::vector
<uint
>::iterator it2
= it
; it2
!= CParticleSystem::_ParticleToRemove
.end(); ++it2
)
2254 nlassert(*it2
< _Size
);
2257 TAnimationTime timeUntilNextSimStep
;
2258 if (_Time
[*it
] > 1.f
)
2260 // move position backward (compute its position at death)
2261 timeUntilNextSimStep
= ((_Time
[*it
] - 1.f
) / _TimeIncrement
[*it
]) * ellapsedTimeRatio
;
2262 _Pos
[*it
] -= _Speed
[*it
] * timeUntilNextSimStep
;
2264 // force time to 1 because emitter 'on death' may rely on the date of emitter to compute its attributes
2265 _Time
[*it
] = 0.9999f
;
2269 timeUntilNextSimStep
= 0.f
;
2271 deleteElement(*it
, timeUntilNextSimStep
);
2272 removeParticleFromRemoveList(*it
, _Size
);
2274 for(std::vector
<uint
>::iterator it2
= it
+ 1; it2
!= CParticleSystem::_ParticleToRemove
.end(); ++it2
)
2276 nlassert(*it2
< _Size
);
2283 for(std::vector
<uint
>::iterator it
= CParticleSystem::_ParticleToRemove
.begin(); it
!= CParticleSystem::_ParticleToRemove
.end(); ++it
)
2285 TAnimationTime timeUntilNextSimStep
;
2286 if (_Time
[*it
] > 1.f
)
2288 // move position backward
2289 timeUntilNextSimStep
= (_Time
[*it
] - 1.f
) * _InitialLife
* ellapsedTimeRatio
;
2290 _Pos
[*it
] -= _Speed
[*it
] * timeUntilNextSimStep
;
2291 // force time to 1 because emitter 'on death' may rely on the date of emitter to compute its attributes
2292 _Time
[*it
] = 0.9999f
;
2296 timeUntilNextSimStep
= 0.f
;
2298 deleteElement(*it
, timeUntilNextSimStep
);
2299 removeParticleFromRemoveList(*it
, _Size
);
2308 for(std::vector
<uint
>::iterator it
= CParticleSystem::_ParticleToRemove
.begin(); it
!= CParticleSystem::_ParticleToRemove
.end(); ++it
)
2310 TAnimationTime timeUntilNextSimStep
;
2311 if (_Time
[*it
] > 1.f
)
2313 // move position backward (compute its position at death)
2314 timeUntilNextSimStep
= (_Time
[*it
] - 1.f
) / _TimeIncrement
[*it
];
2315 computeParametricPos(_Owner
->getSystemDate() + CParticleSystem::RealEllapsedTime
- timeUntilNextSimStep
, *it
, _Pos
[*it
]);
2316 timeUntilNextSimStep
*= ellapsedTimeRatio
;
2317 // force time to 1 because emitter 'on death' may rely on the date of emitter to compute its attributes
2318 _Time
[*it
] = 0.9999f
;
2322 timeUntilNextSimStep
= 0.f
;
2324 deleteElement(*it
, timeUntilNextSimStep
);
2325 removeParticleFromRemoveList(*it
, _Size
);
2330 for(std::vector
<uint
>::iterator it
= CParticleSystem::_ParticleToRemove
.begin(); it
!= CParticleSystem::_ParticleToRemove
.end(); ++it
)
2332 TAnimationTime timeUntilNextSimStep
;
2333 if (_Time
[*it
] > 1.f
)
2335 // move position backward
2336 timeUntilNextSimStep
= (_Time
[*it
] - 1.f
) * _InitialLife
;
2337 computeParametricPos(_Owner
->getSystemDate() + CParticleSystem::RealEllapsedTime
- timeUntilNextSimStep
, *it
, _Pos
[*it
]);
2338 timeUntilNextSimStep
*= ellapsedTimeRatio
;
2339 // force time to 1 because emitter 'on death' may rely on the date of emitter to compute its attributes
2340 _Time
[*it
] = 0.9999f
;
2344 timeUntilNextSimStep
= 0.f
;
2346 deleteElement(*it
, timeUntilNextSimStep
);
2347 removeParticleFromRemoveList(*it
, _Size
);
2353 CParticleSystem::InsideRemoveLoop
= false;
2355 CParticleSystem::_ParticleToRemove
.clear();
2359 for(uint k
= 0; k
< _Size
; ++k
)
2361 nlassert(_Time
[k
] >= 0.f
&& _Time
[k
] <= 1.f
);
2367 /// ***************************************************************************************
2368 void CPSLocated::addNewlySpawnedParticles()
2370 NL_PS_FUNC(CPSLocated_addNewlySpawnedParticles
)
2372 CParticleSystem::InsideNewElementsLoop
= true;
2374 CParticleSystem::CSpawnVect
&spawns
= *CParticleSystem::_Spawns
[getIndex()];
2375 if (spawns
.SpawnInfos
.empty()) return;
2377 if (!_Owner
->getAutoCountFlag())
2379 CParticleSystem::_SpawnPos
.resize(getMaxSize());
2380 numSpawns
= std::min((uint
) (_MaxSize
- _Size
), (uint
) spawns
.SpawnInfos
.size());
2384 numSpawns
= (uint
) spawns
.SpawnInfos
.size();
2386 CParticleSystem::TSpawnInfoVect::const_iterator endIt
= spawns
.SpawnInfos
.begin() + numSpawns
;
2389 for (CParticleSystem::TSpawnInfoVect::const_iterator it
= spawns
.SpawnInfos
.begin(); it
!=endIt
; ++it
)
2391 // sint32 insertionIndex =
2392 newElement(*it
, false, CParticleSystem::EllapsedTime
);
2397 // to avoid warning in autocount mode
2398 //CParticleSystem::InsideSimLoop = false;
2399 for (CParticleSystem::TSpawnInfoVect::const_iterator it
= spawns
.SpawnInfos
.begin(); it
!=endIt
; ++it
)
2401 sint32 insertionIndex
= newElement(*it
, false, CParticleSystem::EllapsedTime
);
2403 nlassert(insertionIndex
!= -1);
2405 if (_Time
[insertionIndex
] >= 1.f
)
2408 nlassert(CParticleSystem::_ParticleRemoveListIndex
[insertionIndex
] == -1);
2410 CParticleSystem::_ParticleToRemove
.push_back(insertionIndex
);
2412 nlassert(CParticleSystem::_ParticleToRemove
.size() <= _Size
);
2414 CParticleSystem::_ParticleRemoveListIndex
[insertionIndex
] = (sint
)CParticleSystem::_ParticleToRemove
.size() - 1;
2417 //CParticleSystem::InsideSimLoop = true;
2419 spawns
.SpawnInfos
.clear();
2421 CParticleSystem::InsideNewElementsLoop
= false;
2425 /// ***************************************************************************************
2426 bool CPSLocated::computeBBox(NLMISC::CAABBox
&box
) const
2428 NL_PS_FUNC(CPSLocated_computeBBox
)
2430 if (!_Size
) return false; // something to compute ?
2433 TLocatedBoundCont::const_iterator it
;
2434 TPSAttribVector::const_iterator it2
;
2436 // check whether any object bound to us need a bbox
2438 for (it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
2440 if ((*it
)->doesProduceBBox())
2446 if (it
== _LocatedBoundCont
.end())
2451 CVector min
= _Pos
[0], max
= _Pos
[0];
2453 for (it2
= _Pos
.begin(); it2
!= _Pos
.end(); ++ it2
)
2455 const CVector
&v
= (*it2
);
2460 box
.setMinMax(min
, max
);
2462 // we've considered that located had no extent in space
2463 // now, we must call each objects that are bound to the located in order
2464 // to complete the bbox if they have no null extent
2466 NLMISC::CAABBox tmpBox
, startBox
= box
;
2468 for (it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
2470 if ((*it
)->doesProduceBBox())
2473 if ((*it
)->completeBBox(tmpBox
))
2475 box
= NLMISC::CAABBox::computeAABBoxUnion(tmpBox
, box
);
2484 /// Setup the driver model matrix. It is set accordingly to the basis used for rendering
2485 /// ***************************************************************************************
2486 void CPSLocated::setupDriverModelMatrix(void)
2488 NL_PS_FUNC(CPSLocated_setupDriverModelMatrix
)
2490 getDriver()->setupModelMatrix(getLocalToWorldMatrix());
2494 /// ***************************************************************************************
2495 void CPSLocated::queryCollisionInfo(void)
2497 NL_PS_FUNC(CPSLocated_queryCollisionInfo
)
2499 if (_CollisionInfoNbRef
)
2501 ++ _CollisionInfoNbRef
;
2505 _CollisionNextPos
= new TPSAttribVector
;
2506 _CollisionInfoNbRef
= 1;
2507 _CollisionNextPos
->resize(_MaxSize
);
2508 for(uint k
= 0; k
< _Size
; ++k
)
2510 _CollisionNextPos
->insert();
2516 /// ***************************************************************************************
2517 void CPSLocated::releaseCollisionInfo(void)
2519 NL_PS_FUNC(CPSLocated_releaseCollisionInfo
)
2521 nlassert(_CollisionInfoNbRef
); // check whether queryCollisionInfo was called
2522 // so the number of refs must not = 0
2523 --_CollisionInfoNbRef
;
2524 if (_CollisionInfoNbRef
== 0)
2526 delete _CollisionNextPos
;
2527 _CollisionNextPos
= NULL
;
2534 /// ***************************************************************************************
2535 void CPSLocated::registerIntegrableForce(CPSForce
*f
)
2537 NL_PS_FUNC(CPSLocated_registerIntegrableForce
)
2539 nlassert(std::find(_IntegrableForces
.begin(), _IntegrableForces
.end(), f
) == _IntegrableForces
.end()); // force registered twice
2540 _IntegrableForces
.push_back(f
);
2541 if (getMatrixMode() != f
->getOwner()->getMatrixMode())
2543 ++_NumIntegrableForceWithDifferentBasis
;
2544 releaseParametricInfos();
2550 /// ***************************************************************************************
2551 void CPSLocated::unregisterIntegrableForce(CPSForce
*f
)
2553 NL_PS_FUNC(CPSLocated_unregisterIntegrableForce
)
2555 nlassert(f
->getOwner()); // f must be attached to a located
2556 CPSVector
<CPSForce
*>::V::iterator it
= std::find(_IntegrableForces
.begin(), _IntegrableForces
.end(), f
);
2557 nlassert(it
!= _IntegrableForces
.end() );
2558 _IntegrableForces
.erase(it
);
2559 if (getMatrixMode() != f
->getOwner()->getMatrixMode())
2561 --_NumIntegrableForceWithDifferentBasis
;
2566 /// ***************************************************************************************
2567 void CPSLocated::addNonIntegrableForceRef(void)
2569 NL_PS_FUNC(CPSLocated_addNonIntegrableForceRef
)
2571 ++_NonIntegrableForceNbRefs
;
2572 releaseParametricInfos();
2576 /// ***************************************************************************************
2577 void CPSLocated::releaseNonIntegrableForceRef(void)
2579 NL_PS_FUNC(CPSLocated_releaseNonIntegrableForceRef
)
2581 nlassert(_NonIntegrableForceNbRefs
!= 0);
2582 --_NonIntegrableForceNbRefs
;
2587 /// ***************************************************************************************
2588 void CPSLocated::integrableForceBasisChanged(TPSMatrixMode matrixMode
)
2590 NL_PS_FUNC(CPSLocated_integrableForceBasisChanged
)
2592 if (getMatrixMode() != matrixMode
)
2594 ++_NumIntegrableForceWithDifferentBasis
;
2595 releaseParametricInfos();
2599 --_NumIntegrableForceWithDifferentBasis
;
2605 /// ***************************************************************************************
2606 CPSLocatedBindable
*CPSLocated::unbind(uint index
)
2608 NL_PS_FUNC(CPSLocated_unbind
)
2610 nlassert(index
< _LocatedBoundCont
.size());
2611 CPSLocatedBindable
*lb
= _LocatedBoundCont
[index
];
2613 _LocatedBoundCont
.erase(_LocatedBoundCont
.begin() + index
);
2618 /// ***************************************************************************************
2619 bool CPSLocated::isBound(const CPSLocatedBindable
*lb
) const
2621 NL_PS_FUNC(CPSLocated_isBound
)
2623 TLocatedBoundCont::const_iterator it
= std::find(_LocatedBoundCont
.begin(), _LocatedBoundCont
.end(), lb
);
2624 return it
!= _LocatedBoundCont
.end();
2627 /// ***************************************************************************************
2628 uint
CPSLocated::getIndexOf(const CPSLocatedBindable
*lb
) const
2630 NL_PS_FUNC(CPSLocated_getIndexOf
)
2632 for(uint k
= 0; k
< _LocatedBoundCont
.size(); ++k
)
2634 if (_LocatedBoundCont
[k
] == lb
) return k
;
2642 ///////////////////////////////////////
2643 // CPSLocatedBindable implementation //
2644 ///////////////////////////////////////
2647 /// ***************************************************************************************
2648 CPSLocatedBindable::CPSLocatedBindable() : _Owner(NULL
), _ExternID(0), _LOD(PSLod1n2
), _Active(true)
2650 NL_PS_FUNC(CPSLocatedBindable_CPSLocatedBindable
)
2654 /// ***************************************************************************************
2655 void CPSLocatedBindable::setOwner(CPSLocated
*psl
)
2657 NL_PS_FUNC(CPSLocatedBindable_setOwner
)
2658 if (psl
== _Owner
) return;
2664 // empty this located bindable. Need to be empty if it must be rebound to another located.
2665 for (uint k
= 0; k
< _Owner
->getSize(); ++k
)
2671 if (_Owner
&& _Owner
->getOwner())
2673 _Owner
->getOwner()->releaseRefForUserSysCoordInfo(getUserMatrixUsageCount());
2676 if (_Owner
&& _Owner
->getOwner())
2678 _Owner
->getOwner()->addRefForUserSysCoordInfo(getUserMatrixUsageCount());
2682 /// ***************************************************************************************
2683 void CPSLocatedBindable::finalize(void)
2685 NL_PS_FUNC(CPSLocatedBindable_finalize
)
2686 if (_Owner
&& _Owner
->getOwner())
2688 _Owner
->getOwner()->releaseRefForUserSysCoordInfo(getUserMatrixUsageCount());
2692 /// ***************************************************************************************
2693 CPSLocatedBindable::~CPSLocatedBindable()
2695 NL_PS_FUNC(CPSLocatedBindable_CPSLocatedBindableDtor
)
2698 if (_Owner
&& _Owner
->getOwner())
2700 _Owner
->getOwner()->unregisterLocatedBindableExternID(this);
2705 /// ***************************************************************************************
2706 void CPSLocatedBindable::notifyTargetRemoved(CPSLocated
*ptr
)
2708 NL_PS_FUNC(CPSLocatedBindable_notifyTargetRemoved
)
2709 ptr
->unregisterDtorObserver(this);
2712 /// ***************************************************************************************
2713 void CPSLocatedBindable::serial(NLMISC::IStream
&f
)
2715 NL_PS_FUNC(CPSLocatedBindable_IStream
)
2716 sint ver
= f
.serialVersion(4);
2717 f
.serialPtr(_Owner
);
2718 if (ver
> 1) f
.serialEnum(_LOD
);
2721 if (f
.isReading() && !CParticleSystem::getSerializeIdentifierFlag())
2723 // just skip the name
2726 f
.seek(len
, NLMISC::IStream::current
);
2743 f
.serial(_ExternID
);
2749 /// ***************************************************************************************
2750 void CPSLocatedBindable::displayIcon2d(const CVector tab
[], uint nbSegs
, float scale
)
2752 NL_PS_FUNC(CPSLocatedBindable_displayIcon2d
)
2753 uint32 size
= _Owner
->getSize();
2755 setupDriverModelMatrix();
2757 const CVector I
= computeI();
2758 const CVector K
= computeK();
2760 static std::vector
<NLMISC::CLine
> lines
;
2764 // ugly slow code, but not for runtime
2765 for (uint k
= 0; k
< size
; ++k
)
2767 // center of the current particle
2768 const CVector p
= _Owner
->getPos()[k
];
2772 for (uint l
= 0; l
< nbSegs
; ++l
)
2775 li
.V0
= p
+ scale
* (tab
[l
<< 1].x
* I
+ tab
[l
<< 1].y
* K
);
2776 li
.V1
= p
+ scale
* (tab
[(l
<< 1) + 1].x
* I
+ tab
[(l
<< 1) + 1].y
* K
);
2777 lines
.push_back(li
);
2782 mat
.setBlendFunc(CMaterial::one
, CMaterial::one
);
2783 mat
.setZWrite(false);
2784 mat
.setLighting(false);
2786 mat
.setZFunc(CMaterial::less
);
2792 CPSLocatedBindable
*lb
;
2793 _Owner
->getOwner()->getCurrentEditedElement(loc
, index
, lb
);
2795 mat
.setColor((lb
== NULL
|| this == lb
) && loc
== _Owner
&& index
== k
? CRGBA::Red
: CRGBA(127, 127, 127));
2798 CDRU::drawLinesUnlit(lines
, mat
, *getDriver() );
2803 /// ***************************************************************************************
2804 CFontManager
*CPSLocatedBindable::getFontManager(void)
2806 NL_PS_FUNC(CPSLocatedBindable_getFontManager
)
2808 return _Owner
->getFontManager();
2811 /// ***************************************************************************************
2812 /// Shortcut to get the font manager if one was set (const version)
2813 const CFontManager
*CPSLocatedBindable::getFontManager(void) const
2815 NL_PS_FUNC(CPSLocatedBindable_getFontManager
)
2817 return _Owner
->getFontManager();
2821 /// ***************************************************************************************
2822 // Shortcut to get the matrix of the system
2823 const NLMISC::CMatrix
&CPSLocatedBindable::getSysMat(void) const
2825 NL_PS_FUNC( CPSLocatedBindable_getSysMat
)
2827 return _Owner
->getOwner()->getSysMat();
2830 /// ***************************************************************************************
2831 /// shortcut to get the inverted matrix of the system
2832 const NLMISC::CMatrix
&CPSLocatedBindable::getInvertedSysMat(void) const
2834 NL_PS_FUNC(CPSLocatedBindable_getInvertedSysMat
)
2836 return _Owner
->getOwner()->getInvertedSysMat();
2840 /// ***************************************************************************************
2841 /// shortcut to get the view matrix
2842 const NLMISC::CMatrix
&CPSLocatedBindable::getViewMat(void) const
2844 NL_PS_FUNC(CPSLocatedBindable_getViewMat
)
2846 return _Owner
->getOwner()->getViewMat();
2850 /// ***************************************************************************************
2851 /// shortcut to get the inverted view matrix
2852 const NLMISC::CMatrix
&CPSLocatedBindable::getInvertedViewMat(void) const
2854 NL_PS_FUNC(CPSLocatedBindable_getInvertedViewMat
)
2856 return _Owner
->getOwner()->getInvertedViewMat();
2859 /// ***************************************************************************************
2860 /// shortcut to setup the model matrix (system basis or world basis)
2861 void CPSLocatedBindable::setupDriverModelMatrix(void)
2863 NL_PS_FUNC(CPSLocatedBindable_setupDriverModelMatrix
)
2865 _Owner
->setupDriverModelMatrix();
2868 /// ***************************************************************************************
2869 void CPSLocatedBindable::setExternID(uint32 id
)
2871 NL_PS_FUNC(CPSLocatedBindable_setExternID
)
2872 if (id
== _ExternID
) return;
2873 CParticleSystem
*ps
= NULL
;
2874 if (_Owner
&& _Owner
->getOwner())
2876 ps
= _Owner
->getOwner();
2880 ps
->unregisterLocatedBindableExternID(this);
2885 if (ps
) ps
->registerLocatedBindableExternID(id
, this);
2890 /// ***************************************************************************************
2891 void CPSLocatedBindable::releaseAllRef()
2893 NL_PS_FUNC(CPSLocatedBindable_releaseAllRef
)
2900 /////////////////////////////////////////////
2901 // CPSTargetLocatedBindable implementation //
2902 /////////////////////////////////////////////
2904 /// ***************************************************************************************
2905 void CPSTargetLocatedBindable::serial(NLMISC::IStream
&f
)
2907 NL_PS_FUNC(CPSTargetLocatedBindable_serial
)
2908 (void)f
.serialVersion(1);
2909 f
.serialPtr(_Owner
);
2913 // delete previous attached bindables...
2914 for (TTargetCont::iterator it
= _Targets
.begin(); it
!= _Targets
.end(); ++it
)
2920 f
.serialContPolyPtr(_Targets
);
2924 /// ***************************************************************************************
2925 void CPSTargetLocatedBindable::attachTarget(CPSLocated
*ptr
)
2927 NL_PS_FUNC(CPSTargetLocatedBindable_attachTarget
)
2929 // a target can't be shared between different particle systems
2933 nlassert(_Owner
->getOwner() == ptr
->getOwner());
2937 // see whether this target has not been registered before
2938 nlassert(std::find(_Targets
.begin(), _Targets
.end(), ptr
) == _Targets
.end());
2939 _Targets
.push_back(ptr
);
2941 // we register us to be notified when the target disappear
2942 ptr
->registerDtorObserver(this);
2946 /// ***************************************************************************************
2947 void CPSTargetLocatedBindable::notifyTargetRemoved(CPSLocated
*ptr
)
2949 NL_PS_FUNC(CPSTargetLocatedBindable_notifyTargetRemoved
)
2950 TTargetCont::iterator it
= std::find(_Targets
.begin(), _Targets
.end(), ptr
);
2951 nlassert(it
!= _Targets
.end());
2952 releaseTargetRsc(*it
);
2955 CPSLocatedBindable::notifyTargetRemoved(ptr
);
2962 /// ***************************************************************************************
2963 void CPSTargetLocatedBindable::finalize(void)
2965 NL_PS_FUNC(CPSTargetLocatedBindable_finalize
)
2966 /** 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!
2967 * And the behaviour of releaseTergetRsc is implemented in derived class
2969 for (TTargetCont::iterator it
= _Targets
.begin(); it
!= _Targets
.end(); ++it
)
2971 releaseTargetRsc(*it
);
2973 CPSLocatedBindable::finalize();
2976 /// ***************************************************************************************
2977 CPSTargetLocatedBindable::~CPSTargetLocatedBindable()
2979 NL_PS_FUNC(CPSTargetLocatedBindable_CPSTargetLocatedBindable
)
2980 // we unregister to all the targets
2981 for (TTargetCont::iterator it
= _Targets
.begin(); it
!= _Targets
.end(); ++it
)
2983 (*it
)->unregisterDtorObserver(this);
2987 /// ***************************************************************************************
2988 void CPSTargetLocatedBindable::releaseRefTo(const CParticleSystemProcess
*other
)
2990 NL_PS_FUNC(CPSTargetLocatedBindable_releaseRefTo
)
2991 TTargetCont::iterator it
= std::find(_Targets
.begin(), _Targets
.end(), other
);
2992 if (it
== _Targets
.end()) return;
2993 releaseTargetRsc(*it
);
2994 (*it
)->unregisterDtorObserver(this);
2996 nlassert(std::find(_Targets
.begin(), _Targets
.end(), other
) == _Targets
.end());
2999 /// ***************************************************************************************
3000 void CPSTargetLocatedBindable::releaseAllRef()
3002 NL_PS_FUNC(CPSTargetLocatedBindable_releaseAllRef
)
3003 for (TTargetCont::iterator it
= _Targets
.begin(); it
!= _Targets
.end(); ++it
)
3005 releaseTargetRsc(*it
);
3006 (*it
)->unregisterDtorObserver(this);
3009 CPSLocatedBindable::releaseAllRef();
3012 /// ***************************************************************************************
3013 uint
CPSLocated::getUserMatrixUsageCount() const
3015 NL_PS_FUNC(CPSLocated_getUserMatrixUsageCount
)
3017 for(TLocatedBoundCont::const_iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
3019 count
+= (*it
)->getUserMatrixUsageCount();
3021 return count
+ CParticleSystemProcess::getUserMatrixUsageCount();
3024 /// ***************************************************************************************
3025 void CPSLocated::enumTexs(std::vector
<NLMISC::CSmartPtr
<ITexture
> > &dest
, IDriver
&drv
)
3027 NL_PS_FUNC(CPSLocated_enumTexs
)
3028 for(TLocatedBoundCont::const_iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
3030 (*it
)->enumTexs(dest
, drv
);
3034 /// ***************************************************************************************
3035 void CPSLocated::setZBias(float value
)
3037 NL_PS_FUNC(CPSLocated_setZBias
)
3038 for(TLocatedBoundCont::const_iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
3040 (*it
)->setZBias(value
);
3044 /// ***************************************************************************************
3045 void CPSLocated::computeCollisions(uint firstInstanceIndex
, const NLMISC::CVector
*posBefore
, const NLMISC::CVector
*posAfter
)
3047 NL_PS_FUNC(CPSLocated_computeCollisions
)
3048 for(TDtorObserversVect::iterator it
= _DtorObserversVect
.begin(); it
!= _DtorObserversVect
.end(); ++it
)
3050 if ((*it
)->getType() == PSZone
)
3052 static_cast<CPSZone
*>(*it
)->computeCollisions(*this, firstInstanceIndex
, posBefore
, posAfter
);
3057 /// ***************************************************************************************
3058 void CPSLocated::computeSpawns(uint firstInstanceIndex
, bool includeEmitOnce
)
3060 NL_PS_FUNC(CPSLocated_computeSpawns
)
3061 nlassert(CParticleSystem::InsideSimLoop
);
3062 for(TLocatedBoundCont::iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
3064 if (!(*it
)->isActive()) continue;
3065 if ((*it
)->getType() == PSEmitter
)
3067 CPSEmitter
*emit
= static_cast<CPSEmitter
*>(*it
);
3068 emit
->updateEmitTrigger();
3069 switch(emit
->getEmissionType())
3071 case CPSEmitter::regular
:
3072 emit
->computeSpawns(firstInstanceIndex
);
3074 case CPSEmitter::once
:
3075 // if we're at first frame, then do emit for each emitter
3077 if (_Owner
->getSystemDate() == 0.f
|| includeEmitOnce
)
3079 // if first pass, then do the emit a single time
3080 // if firstInstanceIndex != 0 then we're dealing with newly created particles, so do the spawn too
3081 emit
->doEmitOnce(firstInstanceIndex
);
3091 /// ***************************************************************************************
3092 void CPSLocated::computeForces()
3094 NL_PS_FUNC(CPSLocated_computeForces
)
3095 nlassert(CParticleSystem::InsideSimLoop
);
3096 for(TDtorObserversVect::iterator it
= _DtorObserversVect
.begin(); it
!= _DtorObserversVect
.end(); ++it
)
3098 if ((*it
)->getType() == PSForce
)
3100 CPSForce
*force
= static_cast<CPSForce
*>(*it
);
3101 force
->computeForces(*this);
3106 /// ***************************************************************************************
3107 void CPSCollisionInfo::update(const CPSCollisionInfo
&other
)
3109 NL_PS_FUNC(CPSCollisionInfo_update
)
3112 // link collision in the global list of active collisions
3113 Next
= CPSLocated::_FirstCollision
;
3114 CPSLocated::_FirstCollision
= this;
3116 NewPos
= other
.NewPos
;
3117 NewSpeed
= other
.NewSpeed
;
3118 CollisionZone
= other
.CollisionZone
;
3120 else if (other
.Dist
< Dist
) // is the new collision better (e.g it happens sooner) ?
3123 NewPos
= other
.NewPos
;
3124 NewSpeed
= other
.NewSpeed
;
3125 CollisionZone
= other
.CollisionZone
;
3129 /// ***************************************************************************************
3130 void CPSLocated::checkLife() const
3132 NL_PS_FUNC(CPSLocated_checkLife
)
3133 if (!getLastForever())
3135 for(uint k
= 0; k
< getSize(); ++k
)
3137 nlassert(getTime()[k
] >= 0.f
);
3138 nlassert(getTime()[k
] <= 1.f
);
3143 /// ***************************************************************************************
3144 void CPSLocated::onShow(bool shown
)
3146 for(TLocatedBoundCont::iterator it
= _LocatedBoundCont
.begin(); it
!= _LocatedBoundCont
.end(); ++it
)
3148 (*it
)->onShow(shown
);