Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / ps_located.cpp
blob1af7b370d512e4f7a4489a224568e21199d443e7
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
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.
8 //
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/>.
17 #include "std3d.h"
21 #include <algorithm>
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"
45 #ifdef NL_DEBUG
46 #define CHECK_PS_INTEGRITY checkIntegrity();
47 #else
48 #define CHECK_PS_INTEGRITY
49 #endif
51 #ifdef DEBUG_NEW
52 #define new DEBUG_NEW
53 #endif
55 namespace NL3D {
58 std::vector<CPSCollisionInfo> CPSLocated::_Collisions;
59 CPSCollisionInfo *CPSLocated::_FirstCollision = NULL;
63 /// ***************************************************************************************
64 /**
65 * Constructor
67 CPSLocated::CPSLocated() : /*_MaxNumFaces(0),*/
68 _Size(0),
69 _MaxSize(DefaultMaxLocatedInstance),
70 _CollisionInfoNbRef(0),
71 _CollisionNextPos(NULL),
72 _InitialLife(1.f),
73 _LifeScheme(NULL),
74 _InitialMass(1.f),
75 _MassScheme(NULL),
76 _LODDegradation(false),
77 _ParametricMotion(false),
78 _TriggerOnDeath(false),
79 _LastForever(true),
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)
92 nlassert(_Owner);
93 switch(getMatrixMode())
95 case PSFXWorldMatrix: return _Owner->getSysMat();
96 case PSIdentityMatrix: return NLMISC::CMatrix::Identity;
97 case PSUserMatrix: return _Owner->getUserMatrix();
98 default:
99 nlassert(0);
101 nlassert(0);
102 return NLMISC::CMatrix::Identity;
105 // *****************************************************************************************************
106 const NLMISC::CMatrix &CPSLocated::getWorldToLocalMatrix() const
108 NL_PS_FUNC(CPSLocated_getWorldToLocalMatrix)
109 nlassert(_Owner);
110 switch(getMatrixMode())
112 case PSFXWorldMatrix: return _Owner->getInvertedSysMat();
113 case PSIdentityMatrix: return NLMISC::CMatrix::Identity;
114 case PSUserMatrix: return _Owner->getInvertedUserMatrix();
115 default:
116 nlassert(0);
118 nlassert(0);
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)
158 CHECK_PS_INTEGRITY
159 _LastForever = true;
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");
167 return false;
170 CHECK_PS_INTEGRITY
171 return true;
175 /// ***************************************************************************************
176 void CPSLocated::systemDateChanged()
178 NL_PS_FUNC(CPSLocated_systemDateChanged)
179 CHECK_PS_INTEGRITY
180 for(TLocatedBoundCont::iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
182 (*it)->systemDateChanged();
184 CHECK_PS_INTEGRITY
188 /// ***************************************************************************************
189 void CPSLocated::releaseRefTo(const CParticleSystemProcess *other)
191 NL_PS_FUNC(CPSLocated_releaseRefTo)
192 CHECK_PS_INTEGRITY
193 // located bindables
195 for(TLocatedBoundCont::iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
197 (*it)->releaseRefTo(other);
200 // dtor observers
203 for(TDtorObserversVect::iterator it = _DtorObserversVect.begin(); it != _DtorObserversVect.end(); ++it)
205 if ((*it)->getOwner() == other)
207 CPSLocatedBindable *refMaker = *it;
208 refMaker->notifyTargetRemoved(this);
209 break;
213 CHECK_PS_INTEGRITY
216 /// ***************************************************************************************
217 void CPSLocated::releaseAllRef()
219 NL_PS_FUNC(CPSLocated_releaseAllRef)
220 CHECK_PS_INTEGRITY
221 // located bindables
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);
244 CHECK_PS_INTEGRITY
248 /// ***************************************************************************************
249 void CPSLocated::notifyMotionTypeChanged(void)
251 NL_PS_FUNC(CPSLocated_notifyMotionTypeChanged)
252 CHECK_PS_INTEGRITY
253 for (TLocatedBoundCont::const_iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
255 (*it)->motionTypeChanged(_ParametricMotion);
257 CHECK_PS_INTEGRITY
261 /// ***************************************************************************************
262 void CPSLocated::integrateSingle(float startDate, float deltaT, uint numStep,
263 uint32 indexInLocated,
264 NLMISC::CVector *destPos,
265 uint stride) const
267 NL_PS_FUNC(CPSLocated_integrateSingle)
268 CHECK_PS_INTEGRITY
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);
277 accumulate = true;
280 else // no forces applied, just deduce position from date, initial pos and speed
282 #ifdef NL_DEBUG
283 NLMISC::CVector *endPos = (NLMISC::CVector *) ( (uint8 *) destPos + stride * numStep);
284 #endif
285 const CPSLocated::CParametricInfo &pi = _PInfo[indexInLocated];
286 destPos = FillBufUsingSubdiv(pi.Pos, pi.Date, startDate, deltaT, numStep, destPos, stride);
287 if (numStep != 0)
289 float currDate = startDate - pi.Date;
290 nlassert(currDate >= 0);
293 #ifdef NL_DEBUG
294 nlassert(destPos < endPos);
295 #endif
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;
299 currDate += deltaT;
300 destPos = (NLMISC::CVector *) ( (uint8 *) destPos + stride);
302 while (--numStep);
305 CHECK_PS_INTEGRITY
309 /// ***************************************************************************************
310 void CPSLocated::performParametricMotion(TAnimationTime date)
312 NL_PS_FUNC(CPSLocated_performParametricMotion)
313 CHECK_PS_INTEGRITY
314 if (!_Size) return;
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);
324 accumulate = true;
327 else
329 CPSLocated::TPSAttribParametricInfo::const_iterator it = _PInfo.begin(),
330 endIt = _PInfo.end();
331 TPSAttribVector::iterator posIt = _Pos.begin();
332 float deltaT;
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;
339 ++posIt;
340 ++it;
342 while (it != endIt);
344 CHECK_PS_INTEGRITY
347 /// ***************************************************************************************
348 /// allocate parametric infos
349 void CPSLocated::allocateParametricInfos(void)
351 NL_PS_FUNC(CPSLocated_allocateParametricInfos)
352 CHECK_PS_INTEGRITY
353 if (_ParametricMotion) return;
354 nlassert(supportParametricMotion());
355 nlassert(_Owner);
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) );
364 ++posIt;
366 _ParametricMotion = true;
367 notifyMotionTypeChanged();
368 CHECK_PS_INTEGRITY
371 /// ***************************************************************************************
372 /// release parametric infos
373 void CPSLocated::releaseParametricInfos(void)
375 NL_PS_FUNC(CPSLocated_releaseParametricInfos)
376 CHECK_PS_INTEGRITY
377 if (!_ParametricMotion) return;
378 NLMISC::contReset(_PInfo);
379 _ParametricMotion = false;
380 notifyMotionTypeChanged();
381 CHECK_PS_INTEGRITY
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)
399 CHECK_PS_INTEGRITY
400 nlassert(supportParametricMotion());
401 if (enable)
403 allocateParametricInfos();
405 else
407 releaseParametricInfos();
409 CHECK_PS_INTEGRITY
412 /// ***************************************************************************************
413 void CPSLocated::setMatrixMode(TPSMatrixMode matrixMode)
415 NL_PS_FUNC(CPSLocated_setMatrixMode)
416 CHECK_PS_INTEGRITY
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() );
430 CHECK_PS_INTEGRITY
433 /// ***************************************************************************************
435 void CPSLocated::notifyMaxNumFacesChanged(void)
437 CHECK_PS_INTEGRITY
438 if (!_Owner) return;
440 // we examine whether we have particle attached to us, and ask for the max number of faces they may want
441 _MaxNumFaces = 0;
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;
451 CHECK_PS_INTEGRITY
455 /// ***************************************************************************************
456 uint CPSLocated::getNumWantedTris() const
458 NL_PS_FUNC(CPSLocated_getNumWantedTris)
459 CHECK_PS_INTEGRITY
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();
469 CHECK_PS_INTEGRITY
470 return numWantedTris;
474 /// ***************************************************************************************
475 uint CPSLocated::querryMaxWantedNumFaces(void)
477 return _MaxNumFaces;
481 /// ***************************************************************************************
482 /// tells whether there are alive entities / particles in the system
483 bool CPSLocated::hasParticles() const
485 NL_PS_FUNC(CPSLocated_hasParticles)
486 CHECK_PS_INTEGRITY
487 for (TLocatedBoundCont::const_iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
489 if ((*it)->getType() == PSParticle && (*it)->hasParticles()) return true;
491 CHECK_PS_INTEGRITY
492 return false;
495 /// ***************************************************************************************
496 /// tells whether there are alive emitters
497 bool CPSLocated::hasEmitters() const
499 NL_PS_FUNC(CPSLocated_hasEmitters)
500 CHECK_PS_INTEGRITY
501 for (TLocatedBoundCont::const_iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
503 if ((*it)->getType() == PSEmitter && (*it)->hasEmitters()) return true;
505 CHECK_PS_INTEGRITY
506 return false;
509 /// ***************************************************************************************
510 void CPSLocated::getLODVect(NLMISC::CVector &v, float &offset, TPSMatrixMode matrixMode)
512 NL_PS_FUNC(CPSLocated_getLODVect)
513 nlassert(_Owner);
514 CHECK_PS_INTEGRITY
515 _Owner->getLODVect(v, offset, matrixMode);
516 CHECK_PS_INTEGRITY
519 /// ***************************************************************************************
520 float CPSLocated::getUserParam(uint numParam) const
522 NL_PS_FUNC(CPSLocated_getUserParam)
523 nlassert(_Owner);
524 CHECK_PS_INTEGRITY
525 return _Owner->getUserParam(numParam);
528 /// ***************************************************************************************
529 CScene *CPSLocated::getScene(void)
531 NL_PS_FUNC(CPSLocated_getScene)
532 nlassert(_Owner);
533 CHECK_PS_INTEGRITY
534 return _Owner->getScene();
537 /// ***************************************************************************************
538 void CPSLocated::incrementNbDrawnParticles(uint num)
540 NL_PS_FUNC(CPSLocated_incrementNbDrawnParticles)
541 CHECK_PS_INTEGRITY
542 CParticleSystem::NbParticlesDrawn += num; // for benchmark purpose
545 /// ***************************************************************************************
546 void CPSLocated::setInitialLife(TAnimationTime lifeTime)
548 NL_PS_FUNC(CPSLocated_setInitialLife)
549 CHECK_PS_INTEGRITY
550 _LastForever = false;
551 _InitialLife = lifeTime;
552 delete _LifeScheme;
553 _LifeScheme = NULL;
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)
560 _Time[k] = 0.f;
563 if (_Owner)
565 _Owner->systemDurationChanged();
567 CHECK_PS_INTEGRITY
570 /// ***************************************************************************************
571 void CPSLocated::setLifeScheme(CPSAttribMaker<float> *scheme)
573 NL_PS_FUNC(CPSLocated_setLifeScheme)
574 CHECK_PS_INTEGRITY
575 nlassert(scheme);
576 nlassert(!scheme->hasMemory()); // scheme with memory is invalid there !!
577 _LastForever = false;
578 delete _LifeScheme;
579 _LifeScheme = scheme;
581 if (_Owner)
583 _Owner->systemDurationChanged();
585 CHECK_PS_INTEGRITY
588 /// ***************************************************************************************
589 void CPSLocated::setInitialMass(float mass)
591 NL_PS_FUNC(CPSLocated_setInitialMass)
592 CHECK_PS_INTEGRITY
593 _InitialMass = mass;
594 delete _MassScheme;
595 _MassScheme = NULL;
596 CHECK_PS_INTEGRITY
599 /// ***************************************************************************************
600 void CPSLocated::setMassScheme(CPSAttribMaker<float> *scheme)
602 NL_PS_FUNC(CPSLocated_setMassScheme)
603 CHECK_PS_INTEGRITY
604 nlassert(scheme);
605 nlassert(!scheme->hasMemory()); // scheme with memory is invalid there !!
606 delete _MassScheme;
607 _MassScheme = scheme;
608 CHECK_PS_INTEGRITY
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)
616 switch(destMode)
618 case PSFXWorldMatrix:
619 switch(srcMode)
621 case PSFXWorldMatrix: return NLMISC::CMatrix::Identity;
622 case PSIdentityMatrix: return ps.getInvertedSysMat();
623 case PSUserMatrix: return ps.getUserToFXMatrix();
624 default:
625 nlassert(0);
627 break;
628 case PSIdentityMatrix:
629 switch(srcMode)
631 case PSFXWorldMatrix: return ps.getSysMat();
632 case PSIdentityMatrix: return NLMISC::CMatrix::Identity;
633 case PSUserMatrix: return ps.getUserMatrix();
634 default:
635 nlassert(0);
637 break;
638 case PSUserMatrix:
639 switch(srcMode)
641 case PSFXWorldMatrix: return ps.getFXToUserMatrix();
642 case PSIdentityMatrix: return ps.getInvertedUserMatrix();
643 case PSUserMatrix: return NLMISC::CMatrix::Identity;
644 default:
645 nlassert(0);
647 break;
648 default:
649 nlassert(0);
651 nlassert(0);
652 return NLMISC::CMatrix::Identity;
655 /// ***************************************************************************************
656 NLMISC::CVector CPSLocated::computeI(void) const
658 NL_PS_FUNC(CPSLocated_computeI)
659 CHECK_PS_INTEGRITY
660 const NLMISC::CMatrix &sysMat = _Owner->getSysMat();
661 if (getMatrixMode() == PSIdentityMatrix)
663 if (!sysMat.hasScalePart())
665 return _Owner->getInvertedViewMat().getI();
667 else
669 return sysMat.getScaleUniform() * _Owner->getInvertedViewMat().getI();
672 else
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());
679 else
681 return sysMat.getScaleUniform() * getWorldToLocalMatrix().mulVector(_Owner->getInvertedViewMat().getI());
686 /// ***************************************************************************************
687 NLMISC::CVector CPSLocated::computeIWithZAxisAligned(void) const
689 NL_PS_FUNC(CPSLocated_computeIWithZAxisAligned)
690 CHECK_PS_INTEGRITY
691 const NLMISC::CMatrix &sysMat = _Owner->getSysMat();
692 const CVector &camI = _Owner->getInvertedViewMat().getI();
693 CVector I(camI.x, camI.y, 0.f);
694 I.normalize();
695 if (getMatrixMode() == PSIdentityMatrix)
697 if (!sysMat.hasScalePart())
699 return I;
701 else
703 return sysMat.getScaleUniform() * I;
706 else
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);
713 else
715 return sysMat.getScaleUniform() * getWorldToLocalMatrix().mulVector(I);
720 /// ***************************************************************************************
721 NLMISC::CVector CPSLocated::computeJ(void) const
723 NL_PS_FUNC(CPSLocated_computeJ)
724 CHECK_PS_INTEGRITY
725 const NLMISC::CMatrix &sysMat = _Owner->getSysMat();
726 if (getMatrixMode() == PSIdentityMatrix)
728 if (!sysMat.hasScalePart())
730 return _Owner->getInvertedViewMat().getJ();
732 else
734 return sysMat.getScaleUniform() * _Owner->getInvertedViewMat().getJ();
737 else
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());
744 else
746 return sysMat.getScaleUniform() * getWorldToLocalMatrix().mulVector(_Owner->getInvertedViewMat().getJ());
751 /// ***************************************************************************************
752 NLMISC::CVector CPSLocated::computeK(void) const
754 NL_PS_FUNC(CPSLocated_computeK)
755 CHECK_PS_INTEGRITY
756 const NLMISC::CMatrix &sysMat = _Owner->getSysMat();
757 if (getMatrixMode() == PSIdentityMatrix)
760 if (!sysMat.hasScalePart())
762 return _Owner->getInvertedViewMat().getK();
764 else
766 return sysMat.getScaleUniform() * _Owner->getInvertedViewMat().getK();
769 else
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());
776 else
778 return sysMat.getScaleUniform() * getWorldToLocalMatrix().mulVector(_Owner->getInvertedViewMat().getK());
783 /// ***************************************************************************************
784 NLMISC::CVector CPSLocated::computeKWithZAxisAligned(void) const
786 NL_PS_FUNC(CPSLocated_computeKWithZAxisAligned)
787 CHECK_PS_INTEGRITY
788 const NLMISC::CMatrix &sysMat = _Owner->getSysMat();
789 if (getMatrixMode() == PSIdentityMatrix)
791 if (!sysMat.hasScalePart())
793 return CVector::K;
795 else
797 return CVector(0.f, 0.f, sysMat.getScaleUniform());
800 else
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);
807 else
809 return getWorldToLocalMatrix().mulVector(CVector(0.f, 0.f, sysMat.getScaleUniform()));
814 /// ***************************************************************************************
815 IDriver *CPSLocated::getDriver() const
817 NL_PS_FUNC(CPSLocated_getDriver)
818 CHECK_PS_INTEGRITY
819 nlassert(_Owner);
820 nlassert (_Owner->getDriver() ); // you haven't called setDriver on the system
821 return _Owner->getDriver();
824 /// ***************************************************************************************
825 /// dtor
826 CPSLocated::~CPSLocated()
828 NL_PS_FUNC(CPSLocated_CPSLocated)
829 CHECK_PS_INTEGRITY
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)
848 (*it2)->finalize();
849 delete *it2;
851 _LocatedBoundCont.clear();
853 delete _LifeScheme;
854 delete _MassScheme;
855 delete _CollisionNextPos;
856 CHECK_PS_INTEGRITY
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)
866 CHECK_PS_INTEGRITY
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
871 ++it;
874 _LocatedBoundCont.insert(it, lb);
875 lb->setOwner(this);
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;
882 CPSEmitterInfo ei;
883 ei.setDefaults();
884 for (uint k = 0; k < initialSize; ++k)
886 _Size = k;
887 lb->newElement(ei);
889 _Size = initialSize;
892 if (_ParametricMotion) lb->motionTypeChanged(true);
894 /// the max number of shapes may have changed
895 //notifyMaxNumFacesChanged();
897 if (_Owner)
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.");
906 return false;
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();
918 CHECK_PS_INTEGRITY
919 return true;
922 /// ***************************************************************************************
923 void CPSLocated::remove(const CPSLocatedBindable *p)
925 NL_PS_FUNC(CPSLocated_remove)
926 CHECK_PS_INTEGRITY
927 TLocatedBoundCont::iterator it = std::find(_LocatedBoundCont.begin(), _LocatedBoundCont.end(), p);
928 nlassert(it != _LocatedBoundCont.end());
929 (*it)->finalize();
930 delete *it;
931 _LocatedBoundCont.erase(it);
932 if (_Owner)
934 _Owner->systemDurationChanged();
936 CHECK_PS_INTEGRITY
939 /// ***************************************************************************************
940 void CPSLocated::registerDtorObserver(CPSLocatedBindable *anObserver)
942 NL_PS_FUNC(CPSLocated_registerDtorObserver)
943 CHECK_PS_INTEGRITY
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);
947 CHECK_PS_INTEGRITY
950 /// ***************************************************************************************
951 void CPSLocated::unregisterDtorObserver(CPSLocatedBindable *anObserver)
953 NL_PS_FUNC(CPSLocated_unregisterDtorObserver)
954 CHECK_PS_INTEGRITY
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);
959 CHECK_PS_INTEGRITY
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
976 float emitterLife;
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
987 else
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;
1000 else
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;
1028 si.SpawnPos = pos;
1029 si.Speed = speed;
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)
1039 CHECK_PS_INTEGRITY
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);
1055 else
1057 return -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);
1073 break;
1074 case PSIdentityMatrix:
1076 CVector fxPosDelta;
1077 _Owner->interpolateFXPosDelta(fxPosDelta, si.LifeTime);
1078 creationIndex =_Pos.insert(_Owner->getSysMat() * si.SpawnPos + fxPosDelta);
1080 break;
1081 case PSUserMatrix:
1083 CVector 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));
1089 break;
1090 default:
1091 nlassert(0);
1093 break;
1094 case PSIdentityMatrix:
1095 switch(this->getMatrixMode())
1097 case PSFXWorldMatrix:
1099 CVector fxPosDelta;
1100 _Owner->interpolateFXPosDelta(fxPosDelta, si.LifeTime);
1101 creationIndex =_Pos.insert(_Owner->getInvertedSysMat() * (si.SpawnPos - fxPosDelta));
1103 break;
1104 case PSIdentityMatrix:
1106 creationIndex =_Pos.insert(si.SpawnPos);
1108 break;
1109 case PSUserMatrix:
1111 CVector userMatrixPosDelta;
1112 _Owner->interpolateUserPosDelta(userMatrixPosDelta, si.LifeTime);
1113 creationIndex =_Pos.insert(_Owner->getInvertedUserMatrix() * (si.SpawnPos - userMatrixPosDelta));
1115 break;
1116 default:
1117 nlassert(0);
1119 break;
1120 case PSUserMatrix:
1121 switch(this->getMatrixMode())
1123 case PSFXWorldMatrix:
1125 CVector fxPosDelta;
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));
1131 break;
1132 case PSIdentityMatrix:
1134 CVector userMatrixPosDelta;
1135 _Owner->interpolateUserPosDelta(userMatrixPosDelta, si.LifeTime);
1136 creationIndex =_Pos.insert(_Owner->getUserMatrix() * si.SpawnPos + userMatrixPosDelta);
1138 break;
1139 case PSUserMatrix:
1141 creationIndex =_Pos.insert(si.SpawnPos);
1143 break;
1144 default:
1145 nlassert(0);
1147 break;
1148 default:
1149 nlassert(0);
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);
1159 else
1161 // must do conversion of speed
1162 nlassert(_Owner);
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);
1179 else
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) );
1193 else
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)
1238 CHECK_PS_INTEGRITY
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)
1248 CPSSpawnInfo si;
1249 si.EmitterInfo.Loc = emitter;
1250 if (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];
1257 else
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;
1264 si.SpawnPos = pos;
1265 si.Speed = speed;
1266 si.SpeedCoordSystem = speedCoordSystem;
1267 si.LifeTime = 0.f;
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
1277 return input;
1278 #else
1279 return ((input & (0xff<<24))>>24)
1280 || ((input & (0xff<<16))>>8)
1281 || ((input & (0xff<<8))<<8)
1282 || ((input & 0xff)<<24);
1283 #endif
1286 /// ***************************************************************************************
1287 inline void CPSLocated::deleteElementBase(uint32 index)
1289 NL_PS_FUNC(CPSLocated_deleteElementBase)
1290 // remove common located's attributes
1291 _InvMass.remove(index);
1292 _Pos.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);
1304 --_Size;
1305 if (_TriggerOnDeath)
1307 const uint32 id = IDToLittleEndian(_TriggerID);
1308 nlassert(_Owner);
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();
1320 CHECK_PS_INTEGRITY
1323 /// ***************************************************************************************
1324 void CPSLocated::deleteElement(uint32 index)
1326 NL_PS_FUNC(CPSLocated_deleteElement)
1327 #ifdef NL_DEBUG
1328 if (CParticleSystem::InsideSimLoop)
1330 nlassert(CParticleSystem::InsideRemoveLoop);
1332 #endif
1333 CHECK_PS_INTEGRITY
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)
1347 #ifdef NL_DEBUG
1348 if (CParticleSystem::InsideSimLoop)
1350 nlassert(CParticleSystem::InsideRemoveLoop);
1352 #endif
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)
1366 CHECK_PS_INTEGRITY
1367 nlassert(newSize < (1 << 16));
1368 if (newSize < _Size)
1370 for (uint32 k = _Size - 1; k >= newSize; --k)
1372 deleteElement(k);
1374 if (k == 0) break; // we're dealing with unsigned quantities
1376 _Size = newSize;
1380 _MaxSize = newSize;
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();
1407 CHECK_PS_INTEGRITY
1410 // dummy struct for serial of a field that has been removed
1411 class CDummyCollision
1413 public:
1414 void serial(NLMISC::IStream &f)
1416 NL_PS_FUNC(CDummyCollision_serial)
1417 f.serialVersion(1);
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
1440 sint32 len;
1441 f.serial(len);
1442 f.seek(len, NLMISC::IStream::current);
1444 else
1446 f.serial(_Name);
1449 f.serial(_InvMass);
1450 f.serial(_Pos);
1451 f.serial(_Speed);
1452 f.serial(_Time);
1453 if (f.isReading())
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();
1459 if (timeSize != 0)
1461 std::fill(&_Time[0], &_Time[0] + timeSize, 0.f);
1464 f.serial(_TimeIncrement);
1465 f.serial(_Size);
1466 f.serial(_MaxSize);
1468 bool lastForever = _LastForever;
1469 f.serial(lastForever);
1470 _LastForever = lastForever;
1472 if (ver < 7)
1474 nlassert(f.isReading());
1475 // serial a dummy ptr (previous code did a serial ptr)
1476 uint64 dummyPtr;
1477 f.serial(dummyPtr);
1478 if (dummyPtr)
1480 #ifdef PS_FAST_ALLOC
1481 extern NLMISC::CContiguousBlockAllocator *PSBlockAllocator;
1482 NLMISC::CContiguousBlockAllocator *oldAlloc = PSBlockAllocator;
1483 PSBlockAllocator = NULL;
1484 #endif
1485 static CPSAttrib<CDummyCollision> col;
1486 col.clear();
1487 f.serial(col);
1488 #ifdef PS_FAST_ALLOC
1489 PSBlockAllocator = oldAlloc;
1490 #endif
1493 f.serial(_CollisionInfoNbRef); // TODO avoid to serialize this ?
1495 if (f.isReading())
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
1508 if (f.isReading())
1510 delete _LifeScheme;
1511 delete _MassScheme;
1513 bool useScheme;
1514 f.serial(useScheme);
1515 if (useScheme)
1517 f.serialPolyPtr(_LifeScheme);
1519 else
1521 f.serial(_InitialLife);
1522 _LifeScheme = NULL;
1525 f.serial(useScheme);
1526 if (useScheme)
1528 f.serialPolyPtr(_MassScheme);
1530 else
1532 f.serial(_InitialMass);
1533 nlassert(_InitialMass > 0);
1534 _MassScheme = NULL;
1537 else
1539 bool bFalse = false, bTrue = true;
1540 if (_LifeScheme)
1542 f.serial(bTrue);
1543 f.serialPolyPtr(_LifeScheme);
1545 else
1547 f.serial(bFalse);
1548 f.serial(_InitialLife);
1550 if (_MassScheme)
1552 f.serial(bTrue);
1553 f.serialPolyPtr(_MassScheme);
1555 else
1557 f.serial(bFalse);
1558 nlassert(_InitialMass > 0);
1559 f.serial(_InitialMass);
1563 if (ver < 7)
1565 uint32 dummy = 0; // was previously the field "_NbFramesToSkip"
1566 f.serial(dummy);
1569 f.serialContPolyPtr(_DtorObserversVect);
1571 if (ver < 7)
1573 nlassert(f.isReading());
1574 // previously, there was a request stack serialized (because system permitted loops)
1575 uint32 size;
1576 f.serial(size);
1577 nlassert(size == 0);
1579 for (uint32 k = 0; k < size; ++k)
1581 TNewElementRequestStack::value_type t;
1582 f.serial(t);
1583 _RequestStack.push(t);
1588 if (ver < 7)
1590 nlassert(f.isReading());
1591 bool dummy;
1592 f.serial(dummy); // was previously the flag "_UpdateLock"
1595 f.serialContPolyPtr(_LocatedBoundCont);
1598 // check that owners are good
1599 #ifdef NL_DEBUG
1600 for(uint k = 0; k < _LocatedBoundCont.size(); ++k)
1602 nlassert(_LocatedBoundCont[k]->getOwner() == this);
1604 #endif
1606 if (ver > 1)
1608 bool lodDegradation = _LODDegradation;
1609 f.serial(lodDegradation);
1610 _LODDegradation = lodDegradation;
1613 if (ver > 2)
1615 bool parametricMotion = _ParametricMotion;
1616 f.serial(parametricMotion);
1617 _ParametricMotion = parametricMotion;
1620 if (f.isReading())
1622 // evaluate our max number of faces
1623 //notifyMaxNumFacesChanged();
1625 if (_ParametricMotion)
1627 _ParametricMotion = false;
1628 allocateParametricInfos();
1629 _ParametricMotion = true;
1633 if (ver > 3)
1635 bool triggerOnDeath = _TriggerOnDeath;
1636 f.serial(triggerOnDeath);
1637 _TriggerOnDeath = triggerOnDeath;
1638 f.serial(_TriggerID);
1640 CHECK_PS_INTEGRITY
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;
1673 if (count > 3)
1675 float et[4] = { ellapsedTime, ellapsedTime, ellapsedTime, ellapsedTime};
1676 // sse part of computation
1677 __asm
1679 mov ecx, count
1680 shr ecx, 2
1683 xor edx, edx
1685 mov eax, src1
1686 mov ebx, src2
1687 movups xmm0, et[0]
1688 myLoop:
1689 movaps xmm2, [ebx + 8 * edx]
1690 movaps xmm1, [eax + 8 * edx]
1691 mulps xmm2, xmm0
1692 addps xmm1, xmm2
1693 movaps [eax + 8 * edx], xmm1
1694 add edx, 2
1695 dec ecx
1696 jne myLoop
1699 // proceed with left float
1700 count &= 3;
1702 if (count)
1704 src1 += alignCount;
1705 src2 += alignCount;
1708 *src1 += ellapsedTime * *src2;
1710 ++src1;
1711 ++src2;
1713 while (--count);
1717 else
1718 #endif
1720 #endif
1722 // standard version
1724 // standard version
1725 uint countDiv8 = count>>3;
1726 count &= 7; // takes count % 8
1728 if (dest == src1)
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];
1742 src2 += 8;
1743 src1 += 8;
1745 while (count--)
1747 *src1++ += ellapsedTime * *src2++;
1750 else
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];
1762 src2 += 8;
1763 src1 += 8;
1764 dest += 8;
1766 while (count--)
1768 *dest++ = *src1++ + ellapsedTime * *src2++;
1774 /// ***************************************************************************************
1775 void CPSLocated::computeMotion()
1777 NL_PS_FUNC(CPSLocated_computeMotion)
1778 nlassert(_Size);
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);
1788 else
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)
1853 #ifdef NL_DEBUG
1854 nlassert(CParticleSystem::_ParticleRemoveListIndex[currCollision->Index] == -1);
1855 #endif
1856 CParticleSystem::_ParticleToRemove.push_back(currCollision->Index);
1857 #ifdef NL_DEBUG
1858 nlassert(CParticleSystem::_ParticleToRemove.size() <= _Size);
1859 #endif
1860 CParticleSystem::_ParticleRemoveListIndex[currCollision->Index] = (sint)CParticleSystem::_ParticleToRemove.size() - 1;
1863 currCollision = currCollision->Next;
1866 else
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;
1879 continue;
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
1899 #ifdef NL_DEBUG
1900 nlassert(CParticleSystem::_ParticleRemoveListIndex[currCollision->Index] == -1);
1901 #endif
1902 CParticleSystem::_ParticleToRemove.push_back(currCollision->Index);
1903 #ifdef NL_DEBUG
1904 nlassert(CParticleSystem::_ParticleToRemove.size() <= _Size);
1905 #endif
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)
1924 nlassert(_Owner);
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;
1937 deleteElement(pos);
1938 pos += step;
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)
1951 CHECK_PS_INTEGRITY
1952 if (!_Size) return;
1954 if (pass != PSMotion)
1958 uint64 *target;
1959 switch(pass)
1961 case PSEmit: target = &PSStatEmit; break;
1962 case PSCollision: target = &PSStatCollision; break;
1963 default:
1964 target = &PSStatRender;
1965 break;
1967 MINI_TIMER(*target)
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 ?
1976 (*it)->step(pass);
1982 else
1984 for (TLocatedBoundCont::iterator it = _LocatedBoundCont.begin(); it != _LocatedBoundCont.end(); ++it)
1986 if ((*it)->isActive())
1988 (*it)->step(pass);
1993 CHECK_PS_INTEGRITY
1996 /// ***************************************************************************************
1997 void CPSLocated::updateLife()
1999 NL_PS_FUNC(CPSLocated_updateLife)
2000 CHECK_PS_INTEGRITY
2001 if (!_Size) return;
2002 if (! _LastForever)
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)
2012 #ifdef NL_DEBUG
2013 nlassert(CParticleSystem::_ParticleRemoveListIndex[k] == -1);
2014 #endif
2015 CParticleSystem::_ParticleToRemove.push_back(k);
2016 #ifdef NL_DEBUG
2017 nlassert(CParticleSystem::_ParticleToRemove.size() <= _Size);
2018 #endif
2019 CParticleSystem::_ParticleRemoveListIndex[k] = (sint)CParticleSystem::_ParticleToRemove.size() - 1;
2021 ++itTime;
2022 ++itTimeInc;
2025 else /// all particles have the same lifetime
2027 if (_InitialLife != 0)
2029 nlassert(_Owner);
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)
2037 *itTime += timeInc;
2038 if (*itTime >= 1.0f)
2040 #ifdef NL_DEBUG
2041 nlassert(CParticleSystem::_ParticleRemoveListIndex[k] == -1);
2042 #endif
2043 CParticleSystem::_ParticleToRemove.push_back(k);
2044 #ifdef NL_DEBUG
2045 nlassert(CParticleSystem::_ParticleToRemove.size() <= _Size);
2046 #endif
2047 CParticleSystem::_ParticleRemoveListIndex[k] = (sint)CParticleSystem::_ParticleToRemove.size() - 1;
2049 ++ itTime;
2052 else
2054 // system has not lasted enough for any particle to die
2055 TPSAttribTime::iterator itTime = _Time.begin(), itEndTime = _Time.end();
2058 *itTime += timeInc;
2059 ++itTime;
2061 while (itTime != itEndTime);
2064 else
2066 for(uint k = 0; k < _Size; ++k)
2068 #ifdef NL_DEBUG
2069 nlassert(CParticleSystem::_ParticleRemoveListIndex[k] == -1);
2070 #endif
2071 CParticleSystem::_ParticleToRemove.push_back(k);
2072 #ifdef NL_DEBUG
2073 nlassert(CParticleSystem::_ParticleToRemove.size() <= _Size);
2074 #endif
2075 CParticleSystem::_ParticleRemoveListIndex[k] = (sint)CParticleSystem::_ParticleToRemove.size() - 1;
2080 else
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;
2089 CHECK_PS_INTEGRITY
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
2109 else
2111 CParticleSystem::_ParticleRemoveListIndex[indexToRemove] = -1;
2114 else
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 /// ***************************************************************************************
2139 #ifndef NL_DEBUG
2140 inline
2141 #endif
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);
2162 #ifdef NL_DEBUG
2163 CParticleSystem::InsideRemoveLoop = true;
2164 checkRemoveArray(_Size);
2165 #endif
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]));
2180 else
2182 deleteElement(*it);
2184 removeParticleFromRemoveList(*it, _Size);
2188 else
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)
2201 if (_LifeScheme)
2203 _Pos[*it] -= _Speed[*it] * ((_Time[*it] - 1.f) / _TimeIncrement[*it]) * ellapsedTimeRatio;
2204 timeUntilNextSimStep = (_Time[*it] - 1.f) / _TimeIncrement[*it];
2206 else
2208 _Pos[*it] -= _Speed[*it] * ((_Time[*it] - 1.f) * _InitialLife) * ellapsedTimeRatio;
2209 timeUntilNextSimStep = (_Time[*it] - 1.f) * _InitialLife;
2211 _Time[*it] = 0.9999f;
2213 else
2215 timeUntilNextSimStep = 0.f;
2218 else
2220 // a collision occurred before particle died, so pos is already good
2221 if (_LifeScheme)
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
2228 else
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())
2247 if (_LifeScheme)
2249 for(std::vector<uint>::iterator it = CParticleSystem::_ParticleToRemove.begin(); it != CParticleSystem::_ParticleToRemove.end(); ++it)
2251 #ifdef NL_DEBUG
2252 for(std::vector<uint>::iterator it2 = it; it2 != CParticleSystem::_ParticleToRemove.end(); ++it2)
2254 nlassert(*it2 < _Size);
2256 #endif
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;
2267 else
2269 timeUntilNextSimStep = 0.f;
2271 deleteElement(*it, timeUntilNextSimStep);
2272 removeParticleFromRemoveList(*it, _Size);
2273 #ifdef NL_DEBUG
2274 for(std::vector<uint>::iterator it2 = it + 1; it2 != CParticleSystem::_ParticleToRemove.end(); ++it2)
2276 nlassert(*it2 < _Size);
2278 #endif
2281 else
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;
2294 else
2296 timeUntilNextSimStep = 0.f;
2298 deleteElement(*it, timeUntilNextSimStep);
2299 removeParticleFromRemoveList(*it, _Size);
2303 else
2305 // parametric case
2306 if (_LifeScheme)
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;
2320 else
2322 timeUntilNextSimStep = 0.f;
2324 deleteElement(*it, timeUntilNextSimStep);
2325 removeParticleFromRemoveList(*it, _Size);
2328 else
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;
2342 else
2344 timeUntilNextSimStep = 0.f;
2346 deleteElement(*it, timeUntilNextSimStep);
2347 removeParticleFromRemoveList(*it, _Size);
2352 #ifdef NL_DEBUG
2353 CParticleSystem::InsideRemoveLoop = false;
2354 #endif
2355 CParticleSystem::_ParticleToRemove.clear();
2356 #ifdef NL_DEBUG
2357 if (!_LastForever)
2359 for(uint k = 0; k < _Size; ++k)
2361 nlassert(_Time[k] >= 0.f && _Time[k] <= 1.f);
2364 #endif
2367 /// ***************************************************************************************
2368 void CPSLocated::addNewlySpawnedParticles()
2370 NL_PS_FUNC(CPSLocated_addNewlySpawnedParticles)
2371 #ifdef NL_DEBUG
2372 CParticleSystem::InsideNewElementsLoop = true;
2373 #endif
2374 CParticleSystem::CSpawnVect &spawns = *CParticleSystem::_Spawns[getIndex()];
2375 if (spawns.SpawnInfos.empty()) return;
2376 uint numSpawns = 0;
2377 if (!_Owner->getAutoCountFlag())
2379 CParticleSystem::_SpawnPos.resize(getMaxSize());
2380 numSpawns = std::min((uint) (_MaxSize - _Size), (uint) spawns.SpawnInfos.size());
2382 else
2384 numSpawns = (uint) spawns.SpawnInfos.size();
2386 CParticleSystem::TSpawnInfoVect::const_iterator endIt = spawns.SpawnInfos.begin() + numSpawns;
2387 if (_LastForever)
2389 for (CParticleSystem::TSpawnInfoVect::const_iterator it = spawns.SpawnInfos.begin(); it !=endIt; ++it)
2391 // sint32 insertionIndex =
2392 newElement(*it, false, CParticleSystem::EllapsedTime);
2395 else
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);
2402 #ifdef NL_DEBUG
2403 nlassert(insertionIndex != -1);
2404 #endif
2405 if (_Time[insertionIndex] >= 1.f)
2407 #ifdef NL_DEBUG
2408 nlassert(CParticleSystem::_ParticleRemoveListIndex[insertionIndex] == -1);
2409 #endif
2410 CParticleSystem::_ParticleToRemove.push_back(insertionIndex);
2411 #ifdef NL_DEBUG
2412 nlassert(CParticleSystem::_ParticleToRemove.size() <= _Size);
2413 #endif
2414 CParticleSystem::_ParticleRemoveListIndex[insertionIndex] = (sint)CParticleSystem::_ParticleToRemove.size() - 1;
2417 //CParticleSystem::InsideSimLoop = true;
2419 spawns.SpawnInfos.clear();
2420 #ifdef NL_DEBUG
2421 CParticleSystem::InsideNewElementsLoop = false;
2422 #endif
2425 /// ***************************************************************************************
2426 bool CPSLocated::computeBBox(NLMISC::CAABBox &box) const
2428 NL_PS_FUNC(CPSLocated_computeBBox)
2429 CHECK_PS_INTEGRITY
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())
2442 break;
2446 if (it == _LocatedBoundCont.end())
2448 return false;
2451 CVector min = _Pos[0], max = _Pos[0];
2453 for (it2 = _Pos.begin(); it2 != _Pos.end(); ++ it2)
2455 const CVector &v = (*it2);
2456 min.minof(min, v);
2457 max.maxof(max, v);
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())
2472 tmpBox = startBox;
2473 if ((*it)->completeBBox(tmpBox))
2475 box = NLMISC::CAABBox::computeAABBoxUnion(tmpBox, box);
2479 CHECK_PS_INTEGRITY
2480 return true;
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)
2489 CHECK_PS_INTEGRITY
2490 getDriver()->setupModelMatrix(getLocalToWorldMatrix());
2491 CHECK_PS_INTEGRITY
2494 /// ***************************************************************************************
2495 void CPSLocated::queryCollisionInfo(void)
2497 NL_PS_FUNC(CPSLocated_queryCollisionInfo)
2498 CHECK_PS_INTEGRITY
2499 if (_CollisionInfoNbRef)
2501 ++ _CollisionInfoNbRef;
2503 else
2505 _CollisionNextPos = new TPSAttribVector;
2506 _CollisionInfoNbRef = 1;
2507 _CollisionNextPos ->resize(_MaxSize);
2508 for(uint k = 0; k < _Size; ++k)
2510 _CollisionNextPos->insert();
2513 CHECK_PS_INTEGRITY
2516 /// ***************************************************************************************
2517 void CPSLocated::releaseCollisionInfo(void)
2519 NL_PS_FUNC(CPSLocated_releaseCollisionInfo)
2520 CHECK_PS_INTEGRITY
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;
2529 CHECK_PS_INTEGRITY
2534 /// ***************************************************************************************
2535 void CPSLocated::registerIntegrableForce(CPSForce *f)
2537 NL_PS_FUNC(CPSLocated_registerIntegrableForce)
2538 CHECK_PS_INTEGRITY
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();
2546 CHECK_PS_INTEGRITY
2550 /// ***************************************************************************************
2551 void CPSLocated::unregisterIntegrableForce(CPSForce *f)
2553 NL_PS_FUNC(CPSLocated_unregisterIntegrableForce)
2554 CHECK_PS_INTEGRITY
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;
2563 CHECK_PS_INTEGRITY
2566 /// ***************************************************************************************
2567 void CPSLocated::addNonIntegrableForceRef(void)
2569 NL_PS_FUNC(CPSLocated_addNonIntegrableForceRef)
2570 CHECK_PS_INTEGRITY
2571 ++_NonIntegrableForceNbRefs;
2572 releaseParametricInfos();
2573 CHECK_PS_INTEGRITY
2576 /// ***************************************************************************************
2577 void CPSLocated::releaseNonIntegrableForceRef(void)
2579 NL_PS_FUNC(CPSLocated_releaseNonIntegrableForceRef)
2580 CHECK_PS_INTEGRITY
2581 nlassert(_NonIntegrableForceNbRefs != 0);
2582 --_NonIntegrableForceNbRefs;
2583 CHECK_PS_INTEGRITY
2587 /// ***************************************************************************************
2588 void CPSLocated::integrableForceBasisChanged(TPSMatrixMode matrixMode)
2590 NL_PS_FUNC(CPSLocated_integrableForceBasisChanged)
2591 CHECK_PS_INTEGRITY
2592 if (getMatrixMode() != matrixMode)
2594 ++_NumIntegrableForceWithDifferentBasis;
2595 releaseParametricInfos();
2597 else
2599 --_NumIntegrableForceWithDifferentBasis;
2601 CHECK_PS_INTEGRITY
2605 /// ***************************************************************************************
2606 CPSLocatedBindable *CPSLocated::unbind(uint index)
2608 NL_PS_FUNC(CPSLocated_unbind)
2609 CHECK_PS_INTEGRITY
2610 nlassert(index < _LocatedBoundCont.size());
2611 CPSLocatedBindable *lb = _LocatedBoundCont[index];
2612 lb->setOwner(NULL);
2613 _LocatedBoundCont.erase(_LocatedBoundCont.begin() + index);
2614 CHECK_PS_INTEGRITY
2615 return lb;
2618 /// ***************************************************************************************
2619 bool CPSLocated::isBound(const CPSLocatedBindable *lb) const
2621 NL_PS_FUNC(CPSLocated_isBound)
2622 CHECK_PS_INTEGRITY
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)
2631 CHECK_PS_INTEGRITY
2632 for(uint k = 0; k < _LocatedBoundCont.size(); ++k)
2634 if (_LocatedBoundCont[k] == lb) return k;
2636 nlassert(0);
2637 return 0;
2642 ///////////////////////////////////////
2643 // CPSLocatedBindable implementation //
2644 ///////////////////////////////////////
2647 /// ***************************************************************************************
2648 CPSLocatedBindable::CPSLocatedBindable() : _Owner(NULL), _ExternID(0), _LOD(PSLod1n2), _Active(true)
2650 NL_PS_FUNC(CPSLocatedBindable_CPSLocatedBindable)
2651 _Owner = NULL;
2654 /// ***************************************************************************************
2655 void CPSLocatedBindable::setOwner(CPSLocated *psl)
2657 NL_PS_FUNC(CPSLocatedBindable_setOwner)
2658 if (psl == _Owner) return;
2659 if (psl == NULL)
2661 releaseAllRef();
2662 if (_Owner)
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)
2667 deleteElement(0);
2671 if (_Owner && _Owner->getOwner())
2673 _Owner->getOwner()->releaseRefForUserSysCoordInfo(getUserMatrixUsageCount());
2675 _Owner = psl;
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)
2696 if (_ExternID)
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);
2719 if (ver > 2)
2721 if (f.isReading() && !CParticleSystem::getSerializeIdentifierFlag())
2723 // just skip the name
2724 sint32 len;
2725 f.serial(len);
2726 f.seek(len, NLMISC::IStream::current);
2728 else
2730 f.serial(_Name);
2733 if (ver > 3)
2735 if (f.isReading())
2737 uint32 id;
2738 f.serial(id);
2739 setExternID(id);
2741 else
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();
2754 if (!size) return;
2755 setupDriverModelMatrix();
2757 const CVector I = computeI();
2758 const CVector K = computeK();
2760 static std::vector<NLMISC::CLine> lines;
2762 lines.clear();
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)
2774 NLMISC::CLine li;
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);
2780 CMaterial mat;
2782 mat.setBlendFunc(CMaterial::one, CMaterial::one);
2783 mat.setZWrite(false);
2784 mat.setLighting(false);
2785 mat.setBlend(true);
2786 mat.setZFunc(CMaterial::less);
2790 CPSLocated *loc;
2791 uint32 index;
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)
2807 nlassert(_Owner);
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)
2816 nlassert(_Owner);
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)
2826 nlassert(_Owner);
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)
2835 nlassert(_Owner);
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)
2845 nlassert(_Owner);
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)
2855 nlassert(_Owner);
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)
2864 nlassert(_Owner);
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();
2878 if (ps)
2880 ps->unregisterLocatedBindableExternID(this);
2881 _ExternID = 0;
2883 if (id != 0)
2885 if (ps) ps->registerLocatedBindableExternID(id, this);
2886 _ExternID = id;
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);
2910 f.serial(_Name);
2911 if (f.isReading())
2913 // delete previous attached bindables...
2914 for (TTargetCont::iterator it = _Targets.begin(); it != _Targets.end(); ++it)
2916 delete (*it);
2918 _Targets.clear();
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
2930 #ifdef NL_DEBUG
2931 if (_Owner)
2933 nlassert(_Owner->getOwner() == ptr->getOwner());
2935 #endif
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);
2953 _Targets.erase(it);
2955 CPSLocatedBindable::notifyTargetRemoved(ptr);
2960 // dtor
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);
2995 _Targets.erase(it);
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);
3008 _Targets.clear();
3009 CPSLocatedBindable::releaseAllRef();
3012 /// ***************************************************************************************
3013 uint CPSLocated::getUserMatrixUsageCount() const
3015 NL_PS_FUNC(CPSLocated_getUserMatrixUsageCount)
3016 uint count = 0;
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);
3073 break;
3074 case CPSEmitter::once:
3075 // if we're at first frame, then do emit for each emitter
3076 nlassert(_Owner);
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);
3083 break;
3084 default:
3085 break;
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)
3110 if (Dist == -1)
3112 // link collision in the global list of active collisions
3113 Next = CPSLocated::_FirstCollision;
3114 CPSLocated::_FirstCollision = this;
3115 Dist = other.Dist;
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) ?
3122 Dist = other.Dist;
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);
3153 } // NL3D