Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / particle_system.cpp
blob4d7fba3c611dbced4489e71deec80bf52fdbc288
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "std3d.h"
24 #include "nel/3d/particle_system.h"
25 #include "nel/3d/ps_located.h"
26 #include "nel/3d/driver.h"
27 #include "nel/3d/vertex_buffer.h"
28 #include "nel/3d/material.h"
29 #include "nel/3d/index_buffer.h"
30 #include "nel/3d/nelu.h"
31 #include "nel/3d/ps_util.h"
32 #include "nel/3d/ps_particle.h"
33 #include "nel/3d/ps_emitter.h"
34 #include "nel/3d/ps_sound.h"
35 #include "nel/3d/particle_system_shape.h"
36 #include "nel/3d/ps_located.h"
37 #include "nel/misc/aabbox.h"
38 #include "nel/misc/file.h"
39 #include "nel/misc/stream.h"
41 // tmp
42 #include "nel/3d/particle_system_model.h"
45 #ifdef NL_DEBUG
46 #define CHECK_INTEGRITY checkIntegrity();
47 #else
48 #define CHECK_INTEGRITY
49 #endif
51 #ifdef DEBUG_NEW
52 #define new DEBUG_NEW
53 #endif
57 namespace NL3D
60 uint32 CParticleSystem::NbParticlesDrawn = 0;
61 UPSSoundServer * CParticleSystem::_SoundServer = NULL;
62 CParticleSystem::TGlobalValuesMap CParticleSystem::_GlobalValuesMap;
63 CParticleSystem::TGlobalVectorValuesMap CParticleSystem::_GlobalVectorValuesMap;
65 // sim step infos
66 TAnimationTime CParticleSystem::EllapsedTime = 0.f;
67 TAnimationTime CParticleSystem::InverseTotalEllapsedTime = 0.f;
68 TAnimationTime CParticleSystem::RealEllapsedTime = 0.f;
69 float CParticleSystem::RealEllapsedTimeRatio = 1.f;
70 bool CParticleSystem::InsideSimLoop = false;
71 bool CParticleSystem::InsideRemoveLoop = false;
72 bool CParticleSystem::InsideNewElementsLoop = false;;
73 std::vector<NLMISC::CVector> CParticleSystem::_SpawnPos;
77 //bool FilterPS[10] = { false, false, false, false, false, false, false, false, false, false };
80 #ifdef NL_DEBUG
81 uint CParticleSystem::_NumInstances = 0;
82 #endif
85 static const float PS_MIN_TIMEOUT = 1.f; // the test that check if there are no particles left
86 #if defined(NL_DEBUG) || defined(NL_PS_DEBUG)
87 bool CParticleSystem::_SerialIdentifiers = true;
88 #else
89 bool CParticleSystem::_SerialIdentifiers = false;
90 #endif
91 bool CParticleSystem::_ForceDisplayBBox = false;
92 CParticleSystemModel *CParticleSystem::OwnerModel = NULL;
98 ///////////////////////////////////
99 // CPaticleSystem implementation //
100 ///////////////////////////////////
103 /// the default max distance of view for particle systems
104 const float PSDefaultMaxViewDist = 300.f;
107 * Constructor
109 CParticleSystem::CParticleSystem() : _Driver(NULL),
110 _FontGenerator(NULL),
111 _FontManager(NULL),
112 _UserCoordSystemInfo(NULL),
113 _Date(0),
114 _LastUpdateDate(-1),
115 _CurrEditedElementLocated(NULL),
116 _CurrEditedElementLocatedBindable(NULL),
117 _CurrEditedElementIndex(0),
118 _Scene(NULL),
119 _TimeThreshold(0.15f),
120 _SystemDate(0.f),
121 _MaxNbIntegrations(2),
122 _LODRatio(0.5f),
123 _OneMinusCurrentLODRatio(0),
124 _MaxViewDist(PSDefaultMaxViewDist),
125 _MaxDistLODBias(0.05f),
126 _InvMaxViewDist(1.f / PSDefaultMaxViewDist),
127 _InvCurrentViewDist(1.f / PSDefaultMaxViewDist),
128 _AutoLODEmitRatio(0.f),
129 _DieCondition(none),
130 _DelayBeforeDieTest(-1.f),
131 _NumWantedTris(0),
132 _AnimType(AnimInCluster),
133 _UserParamGlobalValue(NULL),
134 _BypassGlobalUserParam(0),
135 _PresetBehaviour(UserBehaviour),
136 _ColorAttenuationScheme(NULL),
137 _GlobalColor(NLMISC::CRGBA::White),
138 _GlobalColorLighted(NLMISC::CRGBA::White),
139 _LightingColor(NLMISC::CRGBA::White),
140 _UserColor(NLMISC::CRGBA::White),
141 _ComputeBBox(true),
142 _BBoxTouched(true),
143 _AccurateIntegration(true),
144 _CanSlowDown(true),
145 _DestroyModelWhenOutOfRange(false),
146 _DestroyWhenOutOfFrustum(false),
147 _Sharing(false),
148 _AutoLOD(false),
149 _KeepEllapsedTimeForLifeUpdate(false),
150 _AutoLODSkipParticles(false),
151 _EnableLoadBalancing(true),
152 _EmitThreshold(true),
153 _BypassIntegrationStepLimit(false),
154 _ForceGlobalColorLighting(false),
155 _AutoComputeDelayBeforeDeathTest(true),
156 _AutoCount(false),
157 _HiddenAtCurrentFrame(true),
158 _HiddenAtPreviousFrame(true),
159 _AutoLODStartDistPercent(0.1f),
160 _AutoLODDegradationExponent(1)
162 NL_PS_FUNC_MAIN(CParticleSystem_CParticleSystem)
163 std::fill(_UserParam, _UserParam + MaxPSUserParam, 0.0f);
164 #ifdef NL_DEBUG
165 ++_NumInstances;
166 #endif
171 std::vector<NLMISC::CSmartPtr<CParticleSystem::CSpawnVect> > CParticleSystem::_Spawns; // spawns for the current system being processed
172 std::vector<uint> CParticleSystem::_ParticleToRemove;
173 std::vector<sint> CParticleSystem::_ParticleRemoveListIndex;
178 ///=======================================================================================
179 /// immediatly shut down all the sound in this system
180 void CParticleSystem::stopSound()
182 NL_PS_FUNC_MAIN(CParticleSystem_stopSound)
183 for (uint k = 0; k < this->getNbProcess(); ++k)
185 CPSLocated *psl = dynamic_cast<NL3D::CPSLocated *>(this->getProcess(k));
186 if (psl)
188 for (uint l = 0; l < psl->getNbBoundObjects(); ++l)
190 if (psl->getBoundObject(l)->getType() == PSSound)
192 static_cast<CPSSound *>(psl->getBoundObject(l))->stopSound();
199 ///=======================================================================================
200 void CParticleSystem::reactivateSound()
202 NL_PS_FUNC_MAIN(CParticleSystem_reactivateSound)
203 for (uint k = 0; k < this->getNbProcess(); ++k)
205 CPSLocated *psl = dynamic_cast<NL3D::CPSLocated *>(this->getProcess(k));
206 if (psl)
208 for (uint l = 0; l < psl->getNbBoundObjects(); ++l)
210 if (psl->getBoundObject(l)->getType() == PSSound)
212 static_cast<CPSSound *>(psl->getBoundObject(l))->reactivateSound();
220 ///=======================================================================================
221 void CParticleSystem::enableLoadBalancing(bool enabled /*=true*/)
223 NL_PS_FUNC_MAIN(CParticleSystem_enableLoadBalancing)
224 if (enabled)
226 //notifyMaxNumFacesChanged();
228 _EnableLoadBalancing = enabled;
231 ///=======================================================================================
233 void CParticleSystem::notifyMaxNumFacesChanged(void)
235 if (!_EnableLoadBalancing) return;
236 _MaxNumFacesWanted = 0;
237 for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
239 _MaxNumFacesWanted += (*it)->querryMaxWantedNumFaces();
244 ///=======================================================================================
245 void CParticleSystem::updateNumWantedTris()
247 NL_PS_FUNC_MAIN(CParticleSystem_updateNumWantedTris)
248 _NumWantedTris = 0;
249 for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
251 _NumWantedTris += (*it)->getNumWantedTris();
255 ///=======================================================================================
256 float CParticleSystem::getWantedNumTris(float dist)
258 NL_PS_FUNC_MAIN(CParticleSystem_getWantedNumTris)
259 if (!_EnableLoadBalancing) return 0; // no contribution to the load balancing
260 if (dist > _MaxViewDist) return 0;
261 float retValue = ((1.f - dist * _InvMaxViewDist) * _NumWantedTris);
262 ///nlassertex(retValue >= 0 && retValue < 10000, ("dist = %f, _MaxViewDist = %f, _MaxNumFacesWanted = %d, retValue = %f", dist, _MaxViewDist, _MaxNumFacesWanted, retValue));
263 return retValue;
267 ///=======================================================================================
268 void CParticleSystem::setNumTris(uint numFaces)
270 NL_PS_FUNC_MAIN(CParticleSystem_setNumTris)
271 if (_EnableLoadBalancing)
273 float modelDist = (getSysMat().getPos() - _InvertedViewMat.getPos()).norm();
274 /*uint numFaceWanted = (uint) getWantedNumTris(modelDist);*/
276 const float epsilon = 10E-5f;
279 uint wantedNumTri = (uint) getWantedNumTris(modelDist);
280 if (numFaces >= wantedNumTri || wantedNumTri == 0 || _NumWantedTris == 0 || modelDist < epsilon)
282 _InvCurrentViewDist = _InvMaxViewDist;
284 else
287 _InvCurrentViewDist = (_NumWantedTris - numFaces) / (_NumWantedTris * modelDist);
290 else
292 // always take full detail when there's no load balancing
293 _InvCurrentViewDist = _InvMaxViewDist;
298 ///=======================================================================================
299 /// dtor
300 CParticleSystem::~CParticleSystem()
302 NL_PS_FUNC_MAIN(CParticleSystem_CParticleSystemDtor)
303 for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
305 delete *it;
307 if (_ColorAttenuationScheme)
308 delete _ColorAttenuationScheme;
309 if (_UserParamGlobalValue)
310 delete _UserParamGlobalValue;
311 delete _UserCoordSystemInfo;
312 #ifdef NL_DEBUG
313 --_NumInstances;
314 #endif
317 ///=======================================================================================
318 void CParticleSystem::setViewMat(const NLMISC::CMatrix &m)
320 NL_PS_FUNC_MAIN(CParticleSystem_setViewMat)
321 _ViewMat = m;
322 _InvertedViewMat = m.inverted();
325 ///=======================================================================================
326 bool CParticleSystem::hasEmitters() const
328 NL_PS_FUNC_MAIN(CParticleSystem_hasEmitters)
329 for (TProcessVect::const_iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
331 if ((*it)->hasEmitters()) return true;
333 return false;
336 ///=======================================================================================
337 bool CParticleSystem::hasParticles() const
339 NL_PS_FUNC_MAIN(CParticleSystem_hasParticles)
340 for (TProcessVect::const_iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
342 if ((*it)->hasParticles()) return true;
344 return false;
347 ///=======================================================================================
348 bool CParticleSystem::hasTemporaryParticles() const
350 NL_PS_FUNC_MAIN(CParticleSystem_hasTemporaryParticles)
351 for (TProcessVect::const_iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
353 if ((*it)->isLocated())
355 CPSLocated *loc = static_cast<CPSLocated *>(*it);
356 if (loc->hasParticles()) return true;
359 return false;
362 ///=======================================================================================
363 void CParticleSystem::stepLocated(TPSProcessPass pass)
365 NL_PS_FUNC_MAIN(CParticleSystem_stepLocated)
366 for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
368 (*it)->step(pass);
372 ///=======================================================================================
373 #ifndef NL_DEBUG
374 inline
375 #endif
376 float CParticleSystem::getDistFromViewer() const
378 NL_PS_FUNC_MAIN(CParticleSystem_getDistFromViewer)
379 const CVector d = getSysMat().getPos() - _InvertedViewMat.getPos();
380 return d.norm();
383 ///=======================================================================================
384 #ifndef NL_DEBUG
385 inline
386 #endif
387 float CParticleSystem::updateLODRatio()
389 NL_PS_FUNC_MAIN(CParticleSystem_updateLODRatio)
390 float dist = getDistFromViewer();
391 _OneMinusCurrentLODRatio = 1.f - (dist * _InvCurrentViewDist);
392 NLMISC::clamp(_OneMinusCurrentLODRatio, 0.f, 1.f);
393 return dist;
396 ///=======================================================================================
397 inline void CParticleSystem::updateColor(float distFromViewer)
399 NL_PS_FUNC_MAIN(CParticleSystem_updateColor)
400 if (_ColorAttenuationScheme)
402 float ratio = distFromViewer * _InvMaxViewDist;
403 NLMISC::clamp(ratio, 0.f, 1.f);
404 _GlobalColor = _ColorAttenuationScheme->get(ratio);
406 else
408 _GlobalColor = NLMISC::CRGBA::White;
410 _GlobalColor.modulateFromColor(_GlobalColor, _UserColor);
411 _GlobalColorLighted.modulateFromColor(_GlobalColor, _LightingColor);
416 static void displaySysPos(IDriver *drv, const CVector &pos, CRGBA col)
418 if (!drv) return;
419 drv->setupModelMatrix(CMatrix::Identity);
420 CPSUtil::displayArrow(drv, pos, CVector::K, 1.f, CRGBA::White, col);
425 ///=======================================================================================
426 void CParticleSystem::step(TPass pass, TAnimationTime ellapsedTime, CParticleSystemShape &shape, CParticleSystemModel &model)
428 NL_PS_FUNC_MAIN(CParticleSystem_step)
429 CHECK_INTEGRITY
430 OwnerModel = &model;
431 if (!_CoordSystemInfo.Matrix)
433 nlwarning("3D: BUG: CParticleSystem::step -> !_CoordSystemInfo.Matrix");
434 return;
436 nlassert(_CoordSystemInfo.Matrix); // matrix not set for position of system
437 if (_UserCoordSystemInfo)
439 nlassert(_CoordSystemInfo.Matrix);
441 switch (pass)
443 case SolidRender:
444 EllapsedTime = RealEllapsedTime = ellapsedTime;
445 RealEllapsedTimeRatio = 1.f;
446 /// When shared, the LOD ratio must be computed there
447 if (_Sharing)
449 float dist = updateLODRatio();
450 updateColor(dist);
452 else
454 updateColor(getDistFromViewer());
456 // update time
457 ++_Date;
458 // update global color
459 stepLocated(PSSolidRender);
461 break;
462 case BlendRender:
463 EllapsedTime = RealEllapsedTime = ellapsedTime;
464 RealEllapsedTimeRatio = 1.f;
465 /// When shared, the LOD ratio must be computed there
466 /// When shared, the LOD ratio must be computed there
467 if (_Sharing)
469 float dist = updateLODRatio();
470 updateColor(dist);
472 else
474 updateColor(getDistFromViewer());
476 // update time
477 ++_Date;
478 // update global color
479 stepLocated(PSBlendRender);
480 if (_ForceDisplayBBox)
482 NLMISC::CAABBox box;
483 computeBBox(box);
484 getDriver()->setupModelMatrix(*_CoordSystemInfo.Matrix);
485 CPSUtil::displayBBox(getDriver(), box);
487 break;
488 case ToolRender:
489 EllapsedTime = RealEllapsedTime = ellapsedTime;
490 RealEllapsedTimeRatio = 1.f;
491 stepLocated(PSToolRender);
492 break;
493 case Anim:
495 if (ellapsedTime <= 0.f) return;
496 // update user param from global value if needed, unless this behaviour is bypassed has indicated by a flag in _BypassGlobalUserParam
497 if (_UserParamGlobalValue)
499 nlctassert(MaxPSUserParam < 8); // there should be less than 8 parameters because of mask stored in a byte
500 uint8 bypassMask = 1;
501 for(uint k = 0; k < MaxPSUserParam; ++k)
503 if (_UserParamGlobalValue[k] && !(_BypassGlobalUserParam & bypassMask)) // if there is a global value for this param and if the update is not bypassed
505 _UserParam[k] = _UserParamGlobalValue[k]->second;
507 bypassMask <<= 1;
511 uint nbPass = 1;
512 EllapsedTime = ellapsedTime;
513 _BBoxTouched = true;
514 if (_AccurateIntegration)
516 if (EllapsedTime > _TimeThreshold)
518 nbPass = (uint32) ceilf(EllapsedTime / _TimeThreshold);
519 if (!_BypassIntegrationStepLimit && nbPass > _MaxNbIntegrations)
521 nbPass = _MaxNbIntegrations;
522 if (_CanSlowDown)
524 EllapsedTime = _TimeThreshold;
525 nlassert(_TimeThreshold != 0);
526 InverseTotalEllapsedTime = 1.f / (_TimeThreshold * nbPass);
528 else
530 EllapsedTime = ellapsedTime / nbPass;
531 InverseTotalEllapsedTime = ellapsedTime != 0 ? 1.f / ellapsedTime : 0.f;
534 else
536 EllapsedTime = ellapsedTime / nbPass;
537 InverseTotalEllapsedTime = ellapsedTime != 0 ? 1.f / ellapsedTime : 0.f;
540 else
542 InverseTotalEllapsedTime = ellapsedTime != 0 ? 1.f / ellapsedTime : 0.f;
545 else
547 InverseTotalEllapsedTime = ellapsedTime != 0 ? 1.f / ellapsedTime : 0.f;
549 updateLODRatio();
551 MINI_TIMER(PSAnim3)
552 if (_AutoLOD && !_Sharing)
554 float currLODRatio = 1.f - _OneMinusCurrentLODRatio;
555 if (currLODRatio <= _AutoLODStartDistPercent)
557 _AutoLODEmitRatio = 1.f; // no LOD applied
559 else
561 float lodValue = (currLODRatio - 1.f) / (_AutoLODStartDistPercent - 1.f);
562 NLMISC::clamp(lodValue, 0.f, 1.f);
563 float finalValue = lodValue;
564 for(uint l = 1; l < _AutoLODDegradationExponent; ++l)
566 finalValue *= lodValue;
568 _AutoLODEmitRatio = (1.f - _MaxDistLODBias) * finalValue + _MaxDistLODBias;
569 if (_AutoLODEmitRatio < 0.f) _AutoLODEmitRatio = 0.f;
574 MINI_TIMER(PSAnim4)
575 // set start position. Used by emitters that emit from Local basis to world
576 if (!_HiddenAtPreviousFrame && !_HiddenAtCurrentFrame)
578 _CoordSystemInfo.CurrentDeltaPos = _CoordSystemInfo.OldPos - _CoordSystemInfo.Matrix->getPos();
579 if (_UserCoordSystemInfo)
581 CCoordSystemInfo &csi = _UserCoordSystemInfo->CoordSystemInfo;
582 csi.CurrentDeltaPos = csi.OldPos - csi.Matrix->getPos();
585 else
587 _CoordSystemInfo.CurrentDeltaPos = NLMISC::CVector::Null;
588 _CoordSystemInfo.OldPos = _CoordSystemInfo.Matrix->getPos();
589 if (_UserCoordSystemInfo)
591 CCoordSystemInfo &csi = _UserCoordSystemInfo->CoordSystemInfo;
592 csi.CurrentDeltaPos = NLMISC::CVector::Null;
593 csi.OldPos = csi.Matrix->getPos();
596 //displaySysPos(_Driver, _CurrentDeltaPos + _OldSysMat.getPos(), CRGBA::Red);
597 // process passes
599 RealEllapsedTime = _KeepEllapsedTimeForLifeUpdate ? (ellapsedTime / nbPass)
600 : EllapsedTime;
601 RealEllapsedTimeRatio = RealEllapsedTime / EllapsedTime;
602 /*PSMaxET = std::max(PSMaxET, ellapsedTime);
603 PSMaxNBPass = std::max(PSMaxNBPass, nbPass);*/
607 // Sort by emission depth. We assume that the ps is a directed acyclic graph (so no loop will be encountered)
608 if (shape._ProcessOrder.empty())
610 getSortingByEmitterPrecedence(shape._ProcessOrder);
612 // nodes sorted by degree
613 InsideSimLoop = true;
614 // make enough room for spawns
615 uint numProcess = (uint)_ProcessVect.size();
616 if (numProcess > _Spawns.size())
618 uint oldSize = (uint)_Spawns.size();
619 _Spawns.resize(numProcess);
620 for(uint k = oldSize; k < numProcess; ++k)
622 _Spawns[k] = new CSpawnVect;
625 for(uint k = 0; k < numProcess; ++k)
627 if (!_ProcessVect[k]->isLocated()) continue;
628 CPSLocated *loc = static_cast<CPSLocated *>(_ProcessVect[k]);
629 if (loc->hasLODDegradation())
631 loc->doLODDegradation();
633 _Spawns[k]->MaxNumSpawns = loc->getMaxSize();
638 MINI_TIMER(PSAnim5)
639 // position of the system at the end of the integration
640 _CoordSystemInfo.CurrentDeltaPos += (_CoordSystemInfo.Matrix->getPos() - _CoordSystemInfo.OldPos) * (EllapsedTime * InverseTotalEllapsedTime);
641 if (_UserCoordSystemInfo)
643 CCoordSystemInfo &csi = _UserCoordSystemInfo->CoordSystemInfo;
644 csi.CurrentDeltaPos += (csi.Matrix->getPos() - csi.OldPos) * (EllapsedTime * InverseTotalEllapsedTime);
647 for(uint k = 0; k < shape._ProcessOrder.size(); ++k)
649 if (!_ProcessVect[shape._ProcessOrder[k]]->isLocated()) continue;
650 CPSLocated *loc = static_cast<CPSLocated *>(_ProcessVect[shape._ProcessOrder[k]]);
651 if (_ParticleRemoveListIndex.size() < loc->getMaxSize())
653 _ParticleRemoveListIndex.resize(loc->getMaxSize(), -1);
655 if (loc->getSize() != 0)
657 #ifdef NL_DEBUG
658 loc->checkLife();
659 #endif
660 if (loc->hasCollisionInfos())
662 loc->resetCollisions(loc->getSize());
664 // compute motion (including collisions)
665 if (!loc->isParametricMotionEnabled()) loc->computeMotion();
666 // Update life and mark particles that must be removed
667 loc->updateLife();
668 // Spawn particles. Emitters date is updated only after so we check in CPSLocated::postNewElement
669 // if the emitter was still alive at this date, otherwise we discard the post
670 loc->computeSpawns(0, false);
671 if (loc->hasCollisionInfos()) loc->updateCollisions();
672 // Remove too old particles, making room for new ones
673 if (!_ParticleToRemove.empty())
675 loc->removeOldParticles();
677 #ifdef NL_DEBUG
678 loc->checkLife();
679 #endif
681 if (!_Spawns[shape._ProcessOrder[k]]->SpawnInfos.empty())
683 uint insertionIndex = loc->getSize(); // index at which new particles will be inserted
684 // add new particles that where posted by ancestor emitters, and also mark those that must already be deleted
685 loc->addNewlySpawnedParticles();
686 if (insertionIndex != loc->getSize())
688 // Compute motion for spawned particles. This is useful only if particle can collide because
689 if (loc->hasCollisionInfos())
691 loc->resetCollisions(loc->getSize());
692 loc->computeNewParticleMotion(insertionIndex);
694 loc->computeSpawns(insertionIndex, true);
695 if (loc->hasCollisionInfos()) loc->updateCollisions();
696 // Remove too old particles among the newly created ones.
697 if (!_ParticleToRemove.empty())
699 loc->removeOldParticles();
701 #ifdef NL_DEBUG
702 loc->checkLife();
703 #endif
706 if (!loc->isParametricMotionEnabled()) loc->computeForces();
708 _SystemDate += RealEllapsedTime;
709 for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
711 #ifdef NL_DEBUG
712 if ((*it)->isLocated())
714 CPSLocated *loc = static_cast<CPSLocated *>(*it);
715 loc->checkLife();
717 #endif
719 MINI_TIMER(PSAnim2)
720 (*it)->step(PSMotion);
724 while (--nbPass);
726 MINI_TIMER(PSAnim10)
727 // perform parametric motion if present
728 for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
730 if ((*it)->isParametricMotionEnabled()) (*it)->performParametricMotion(_SystemDate);
734 updateNumWantedTris();
736 InsideSimLoop = false;
739 MINI_TIMER(PSAnim11)
740 // memorize position of matrix for next frame (becomes old position)
741 _CoordSystemInfo.OldPos = _CoordSystemInfo.Matrix->getPos();
742 if (_UserCoordSystemInfo)
744 CCoordSystemInfo &csi = _UserCoordSystemInfo->CoordSystemInfo;
745 csi.OldPos = csi.Matrix->getPos();
748 _HiddenAtPreviousFrame = _HiddenAtCurrentFrame;
752 RealEllapsedTimeRatio = 0.f;
753 CHECK_INTEGRITY
757 ///=======================================================================================
758 void CParticleSystem::serial(NLMISC::IStream &f)
760 CHECK_INTEGRITY
761 NL_PS_FUNC_MAIN(CParticleSystem_serial)
762 sint version = f.serialVersion(19);
764 // version 19: sysmat no more serialized (useless)
765 // version 18: _AutoComputeDelayBeforeDeathTest
766 // version 17: _ForceGlobalColorLighting flag
767 // version 16: _BypassIntegrationStepLimit flag
768 // version 14: emit threshold
769 // version 13: max dist lod bias for auto-LOD
770 // version 12: global userParams
771 // version 11: enable load balancing flag
772 // version 9: Sharing flag added
773 // Auto-lod parameters
774 // New integration flag
775 // Global color attenuation
776 // version 8: Replaced the attribute '_PerformMotionWhenOutOfFrustum' by a _AnimType field which allow more precise control
778 //f.serial(_ViewMat);
780 // patch for old fx : force to recompute duration when fx is saved to avoid prbs
781 if (!f.isReading())
783 if (_AutoComputeDelayBeforeDeathTest)
785 _DelayBeforeDieTest = evalDuration();
789 if (version < 19)
791 NLMISC::CMatrix dummy;
792 f.serial(dummy);
794 f.serial(_Date);
795 if (f.isReading())
797 delete _ColorAttenuationScheme;
798 // delete previous multimap
799 _LBMap.clear();
800 // delete previously attached process
801 TProcessVect::iterator it;
802 for (it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
804 delete (*it);
807 _ProcessVect.clear();
809 f.serialContPolyPtr(_ProcessVect);
811 _FontGenerator = NULL;
812 _FontManager = NULL;
813 if (_UserParamGlobalValue)
814 delete _UserParamGlobalValue;
815 _UserParamGlobalValue = NULL;
816 _BypassGlobalUserParam = 0;
817 // see if some process need to access the user matrix
818 delete _UserCoordSystemInfo;
819 _UserCoordSystemInfo = NULL;
820 for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
822 addRefForUserSysCoordInfo((*it)->getUserMatrixUsageCount());
825 else
827 f.serialContPolyPtr(_ProcessVect);
830 if (version > 1) // name of the system
832 if (f.isReading() && !getSerializeIdentifierFlag())
834 // just skip the name
835 sint32 len;
836 f.serial(len);
837 f.seek(len, NLMISC::IStream::current);
839 else
841 f.serial(_Name);
845 if (version > 2) // infos about integration, and LOD
847 bool accurateIntegration = _AccurateIntegration; // read from bitfield
848 f.serial(accurateIntegration);
849 _AccurateIntegration = accurateIntegration;
850 if (_AccurateIntegration)
852 bool canSlowDown = _CanSlowDown;
853 f.serial(canSlowDown);
854 _CanSlowDown = canSlowDown;
855 f.serial(_TimeThreshold, _MaxNbIntegrations);
857 f.serial(_InvMaxViewDist, _LODRatio);
858 _MaxViewDist = 1.f / _InvMaxViewDist;
859 _InvCurrentViewDist = _InvMaxViewDist;
862 if (version > 3) // tell whether the system must compute his bbox, hold a precomputed bbox
864 bool computeBBox = _ComputeBBox;
865 f.serial(computeBBox);
866 _ComputeBBox = computeBBox;
867 if (!computeBBox)
869 f.serial(_PreComputedBBox);
873 if (version > 4) // lifetime information
875 bool destroyModelWhenOutOfRange = _DestroyModelWhenOutOfRange; // read from bitfield
876 f.serial(destroyModelWhenOutOfRange);
877 _DestroyModelWhenOutOfRange = destroyModelWhenOutOfRange;
878 f.serialEnum(_DieCondition);
879 if (_DieCondition != none)
881 f.serial(_DelayBeforeDieTest);
885 if (version > 5)
887 bool destroyWhenOutOfFrustum = _DestroyWhenOutOfFrustum; // read from bitfield
888 f.serial(destroyWhenOutOfFrustum);
889 _DestroyWhenOutOfFrustum = destroyWhenOutOfFrustum;
892 if (version > 6 && version < 8)
894 bool performMotionWOOF;
895 if (f.isReading())
897 f.serial(performMotionWOOF);
898 performMotionWhenOutOfFrustum(performMotionWOOF);
900 else
902 performMotionWOOF = doesPerformMotionWhenOutOfFrustum();
903 f.serial(performMotionWOOF);
907 if (version > 7)
909 f.serialEnum(_AnimType);
910 f.serialEnum(_PresetBehaviour);
913 if (version > 8)
915 bool sharing = _Sharing; // read from bitfield
916 f.serial(sharing);
917 _Sharing = sharing;
918 bool autoLOD = _AutoLOD; // read from bitfield
919 f.serial(autoLOD);
920 _AutoLOD = autoLOD;
922 if (_AutoLOD)
924 f.serial(_AutoLODStartDistPercent, _AutoLODDegradationExponent);
925 bool autoLODSkipParticles = _AutoLODSkipParticles; // read from bitfield
926 f.serial(autoLODSkipParticles);
927 _AutoLODSkipParticles = autoLODSkipParticles;
929 bool keepEllapsedTimeForLifeUpdate = _KeepEllapsedTimeForLifeUpdate;
930 f.serial(keepEllapsedTimeForLifeUpdate);
931 _KeepEllapsedTimeForLifeUpdate = keepEllapsedTimeForLifeUpdate;
932 f.serialPolyPtr(_ColorAttenuationScheme);
935 if (version >= 11)
937 bool enableLoadBalancing = _EnableLoadBalancing; // read from bitfield
938 f.serial(enableLoadBalancing);
939 _EnableLoadBalancing = enableLoadBalancing;
942 if (version >= 12)
944 // serial infos about global user params
945 nlctassert(MaxPSUserParam < 8); // In this version mask of used global user params are stored in a byte..
946 if (f.isReading())
948 uint8 mask;
949 f.serial(mask);
950 if (mask)
952 std::string globalValueName;
953 uint8 testMask = 1;
954 for(uint k = 0; k < MaxPSUserParam; ++k)
956 if (mask & testMask)
958 f.serial(globalValueName);
959 bindGlobalValueToUserParam(globalValueName.c_str(), k);
961 testMask <<= 1;
965 else
967 uint8 mask = 0;
968 if (_UserParamGlobalValue)
970 for(uint k = 0; k < MaxPSUserParam; ++k)
972 if (_UserParamGlobalValue[k]) mask |= (1 << k);
975 f.serial(mask);
976 if (_UserParamGlobalValue)
978 for(uint k = 0; k < MaxPSUserParam; ++k)
980 if (_UserParamGlobalValue[k])
982 std::string valueName = _UserParamGlobalValue[k]->first;
983 f.serial(valueName);
989 if (version >= 13)
991 if (_AutoLOD && !_Sharing)
993 f.serial(_MaxDistLODBias);
996 if (version >= 14)
998 bool emitThreshold = _EmitThreshold; // read from bitfiled
999 f.serial(emitThreshold);
1000 _EmitThreshold = emitThreshold;
1003 if (version >= 15)
1005 bool bypassIntegrationStepLimit = _BypassIntegrationStepLimit; // read from bitfield
1006 f.serial(bypassIntegrationStepLimit);
1007 _BypassIntegrationStepLimit = bypassIntegrationStepLimit;
1010 if (version >= 17)
1012 bool forceGlobalColorLighting = _ForceGlobalColorLighting; // read from bitfield
1013 f.serial(forceGlobalColorLighting);
1014 _ForceGlobalColorLighting = forceGlobalColorLighting;
1017 if (version >= 18)
1019 bool autoComputeDelayBeforeDeathTest = _AutoComputeDelayBeforeDeathTest; // read from bitfield
1020 f.serial(autoComputeDelayBeforeDeathTest);
1021 _AutoComputeDelayBeforeDeathTest = autoComputeDelayBeforeDeathTest;
1023 else
1025 nlassert(f.isReading());
1026 // for all previously created system, force to eval the system duration in an automatyic way
1027 setDelayBeforeDeathConditionTest(-1.f);
1028 _AutoComputeDelayBeforeDeathTest = true;
1031 if (f.isReading())
1033 //notifyMaxNumFacesChanged();
1034 updateNumWantedTris();
1035 activatePresetBehaviour(_PresetBehaviour); // apply behaviour changes
1036 updateProcessIndices();
1038 CHECK_INTEGRITY
1039 // tmp
1040 //if (f.isReading())
1041 // {
1042 // dumpHierarchy();
1043 // }
1047 ///=======================================================================================
1048 bool CParticleSystem::attach(CParticleSystemProcess *ptr)
1050 NL_PS_FUNC_MAIN(CParticleSystem_attach)
1051 nlassert(ptr);
1052 nlassert(std::find(_ProcessVect.begin(), _ProcessVect.end(), ptr) == _ProcessVect.end() ); // can't attach twice
1053 //nlassert(ptr->getOwner() == NULL);
1054 _ProcessVect.push_back(ptr);
1055 ptr->setOwner(this);
1056 ptr->setIndex((uint32)_ProcessVect.size() - 1);
1057 //notifyMaxNumFacesChanged();
1058 if (getBypassMaxNumIntegrationSteps())
1060 if (!canFinish())
1062 remove(ptr);
1063 nlwarning("<void CParticleSystem::attach> Can't attach object : this causes the system to last forever, and it has been flagged with 'BypassMaxNumIntegrationSteps'. Object is not attached");
1064 return false;
1067 systemDurationChanged();
1068 return true;
1071 ///=======================================================================================
1072 void CParticleSystem::remove(CParticleSystemProcess *ptr)
1074 NL_PS_FUNC_MAIN(CParticleSystem_remove)
1075 TProcessVect::iterator it = std::find(_ProcessVect.begin(), _ProcessVect.end(), ptr);
1076 nlassert(it != _ProcessVect.end() );
1077 ptr->setOwner(NULL);
1078 _ProcessVect.erase(it);
1079 delete ptr;
1080 systemDurationChanged();
1081 updateProcessIndices();
1084 ///=======================================================================================
1085 void CParticleSystem::forceComputeBBox(NLMISC::CAABBox &aabbox)
1087 NL_PS_FUNC_MAIN(CParticleSystem_forceComputeBBox)
1088 bool foundOne = false;
1089 NLMISC::CAABBox tmpBox;
1090 for (TProcessVect::const_iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
1092 if ((*it)->computeBBox(tmpBox))
1094 // rotate the aabbox so that it is in the correct basis
1095 const CMatrix &convMat = CPSLocated::getConversionMatrix(*this, PSFXWorldMatrix, (*it)->getMatrixMode());
1096 tmpBox = NLMISC::CAABBox::transformAABBox(convMat, tmpBox);
1098 if (foundOne)
1100 aabbox = NLMISC::CAABBox::computeAABBoxUnion(aabbox, tmpBox);
1102 else
1104 aabbox = tmpBox;
1105 foundOne = true;
1110 if (!foundOne)
1112 aabbox.setCenter(NLMISC::CVector::Null);
1113 aabbox.setHalfSize(NLMISC::CVector::Null);
1117 ///=======================================================================================
1118 void CParticleSystem::computeBBox(NLMISC::CAABBox &aabbox)
1120 NL_PS_FUNC_MAIN(CParticleSystem_computeBBox)
1121 if (!_ComputeBBox || !_BBoxTouched)
1123 aabbox = _PreComputedBBox;
1124 return;
1126 forceComputeBBox(aabbox);
1127 _BBoxTouched = false;
1128 _PreComputedBBox = aabbox;
1131 ///=======================================================================================
1132 void CParticleSystem::setSysMat(const CMatrix *m)
1134 NL_PS_FUNC_MAIN(CParticleSystem_setSysMat)
1135 _CoordSystemInfo.Matrix = m;
1136 if (_SystemDate == 0.f)
1138 _CoordSystemInfo.OldPos = m ? m->getPos() : CVector::Null;
1140 if (!m) return;
1141 _CoordSystemInfo.InvMatrix = _CoordSystemInfo.Matrix->inverted();
1144 ///=======================================================================================
1145 void CParticleSystem::setUserMatrix(const NLMISC::CMatrix *m)
1147 NL_PS_FUNC_MAIN(CParticleSystem_setUserMatrix)
1148 if (!_UserCoordSystemInfo) return; // no process in the system references the user matrix
1149 CCoordSystemInfo &csi = _UserCoordSystemInfo->CoordSystemInfo;
1150 csi.Matrix = m;
1151 if (_SystemDate == 0.f)
1153 csi.OldPos = m ? m->getPos() : getSysMat().getPos(); // _CoordSystemInfo.Matrix is relevant if at least one call to setSysMat has been performed before
1155 if (!m) return;
1156 csi.InvMatrix = csi.Matrix->inverted();
1157 // build conversion matrix between father user matrix & fx matrix
1158 // TODO : lazy evaluation for this ?
1159 _UserCoordSystemInfo->UserBasisToFXBasis = _CoordSystemInfo.InvMatrix * *(csi.Matrix);
1160 _UserCoordSystemInfo->FXBasisToUserBasis = csi.InvMatrix * getSysMat();
1163 ///=======================================================================================
1164 bool CParticleSystem::hasOpaqueObjects(void) const
1166 NL_PS_FUNC_MAIN(CParticleSystem_hasOpaqueObjects)
1167 /// for each process
1168 for (TProcessVect::const_iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
1170 if ((*it)->isLocated())
1172 CPSLocated *loc = static_cast<CPSLocated *>(*it);
1173 for (uint k = 0; k < loc->getNbBoundObjects(); ++k)
1175 CPSLocatedBindable *lb = loc->getBoundObject(k);
1176 if (lb->getType() == PSParticle)
1178 if (((CPSParticle *) lb)->hasOpaqueFaces()) return true;
1183 return false;
1186 ///=======================================================================================
1187 bool CParticleSystem::hasTransparentObjects(void) const
1189 NL_PS_FUNC_MAIN(CParticleSystem_hasTransparentObjects)
1190 /// for each process
1191 for (TProcessVect::const_iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
1193 if ((*it)->isLocated())
1195 CPSLocated *loc = static_cast<CPSLocated *>(*it);
1196 for (uint k = 0; k < loc->getNbBoundObjects(); ++k)
1198 CPSLocatedBindable *lb = loc->getBoundObject(k);
1199 if (lb->getType() == PSParticle)
1201 if (((CPSParticle *) lb)->hasTransparentFaces()) return true;
1206 return false;
1209 ///=======================================================================================
1210 bool CParticleSystem::hasLightableObjects() const
1212 NL_PS_FUNC_MAIN(CParticleSystem_hasLightableObjects)
1213 if (_ForceGlobalColorLighting) return true;
1214 /// for each process
1215 for (TProcessVect::const_iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
1217 if ((*it)->isLocated())
1219 CPSLocated *loc = static_cast<CPSLocated *>(*it);
1220 for (uint k = 0; k < loc->getNbBoundObjects(); ++k)
1222 CPSLocatedBindable *lb = loc->getBoundObject(k);
1223 if (lb->getType() == PSParticle)
1225 if (((CPSParticle *) lb)->hasLightableFaces()) return true;
1226 if (((CPSParticle *) lb)->usesGlobalColorLighting()) return true;
1231 return false;
1234 ///=======================================================================================
1235 void CParticleSystem::getLODVect(NLMISC::CVector &v, float &offset, TPSMatrixMode matrixMode)
1237 NL_PS_FUNC_MAIN(CParticleSystem_getLODVect)
1238 switch(matrixMode)
1240 case PSFXWorldMatrix:
1242 const CVector tv = getInvertedSysMat().mulVector(_InvertedViewMat.getJ());
1243 const CVector org = getInvertedSysMat() * _InvertedViewMat.getPos();
1244 v = _InvCurrentViewDist * tv;
1245 offset = - org * v;
1247 break;
1248 case PSIdentityMatrix:
1250 v = _InvCurrentViewDist * _InvertedViewMat.getJ();
1251 offset = - _InvertedViewMat.getPos() * v;
1253 break;
1254 case PSUserMatrix:
1256 const CVector tv = getInvertedUserMatrix().mulVector(_InvertedViewMat.getJ());
1257 const CVector org = getInvertedUserMatrix() * _InvertedViewMat.getPos();
1258 v = _InvCurrentViewDist * tv;
1259 offset = - org * v;
1261 break;
1262 default:
1263 nlassert(0);
1264 break;
1268 ///=======================================================================================
1269 TPSLod CParticleSystem::getLOD(void) const
1271 NL_PS_FUNC_MAIN(CParticleSystem_getLOD)
1272 const float dist = fabsf(_InvCurrentViewDist * (getSysMat().getPos() - _InvertedViewMat.getPos()) * _InvertedViewMat.getJ());
1273 return dist > _LODRatio ? PSLod2 : PSLod1;
1277 ///=======================================================================================
1278 void CParticleSystem::registerLocatedBindableExternID(uint32 id, CPSLocatedBindable *lb)
1280 NL_PS_FUNC_MAIN(CParticleSystem_registerLocatedBindableExternID)
1281 nlassert(lb);
1282 nlassert(lb->getOwner() && lb->getOwner()->getOwner() == this); // the located bindable must belong to that system
1283 #ifdef NL_DEBUG
1284 // check that this lb hasn't been inserted yet
1285 TLBMap::iterator lbd = _LBMap.lower_bound(id), ubd = _LBMap.upper_bound(id);
1286 nlassert(std::find(lbd, ubd, TLBMap::value_type (id, lb)) == ubd);
1287 nlassert(std::find(lbd, ubd, TLBMap::value_type (id, lb)) == ubd );
1289 #endif
1290 _LBMap.insert(TLBMap::value_type (id, lb) );
1293 ///=======================================================================================
1294 void CParticleSystem::unregisterLocatedBindableExternID(CPSLocatedBindable *lb)
1296 NL_PS_FUNC_MAIN(CParticleSystem_unregisterLocatedBindableExternID)
1297 nlassert(lb);
1298 nlassert(lb->getOwner() && lb->getOwner()->getOwner() == this); // the located bindable must belong to that system
1299 uint32 id = lb->getExternID();
1300 if (!id) return;
1301 TLBMap::iterator lbd = _LBMap.lower_bound(id), ubd = _LBMap.upper_bound(id);
1302 TLBMap::iterator el = std::find(lbd, ubd, TLBMap::value_type (id, lb));
1303 nlassert(el != ubd);
1304 _LBMap.erase(el);
1307 ///=======================================================================================
1308 uint CParticleSystem::getNumLocatedBindableByExternID(uint32 id) const
1310 NL_PS_FUNC_MAIN(CParticleSystem_getNumLocatedBindableByExternID)
1311 return (uint)_LBMap.count(id);
1314 ///=======================================================================================
1315 CPSLocatedBindable *CParticleSystem::getLocatedBindableByExternID(uint32 id, uint index)
1317 NL_PS_FUNC_MAIN(CParticleSystem_getLocatedBindableByExternID)
1318 if (index >= _LBMap.count(id))
1320 return NULL;
1322 TLBMap::const_iterator el = _LBMap.lower_bound(id);
1323 uint left = index;
1324 while (left--) ++el;
1325 return el->second;
1329 ///=======================================================================================
1330 const CPSLocatedBindable *CParticleSystem::getLocatedBindableByExternID(uint32 id, uint index) const
1332 NL_PS_FUNC_MAIN(CParticleSystem_getLocatedBindableByExternID)
1333 if (index >= _LBMap.count(id))
1335 return NULL;
1337 TLBMap::const_iterator el = _LBMap.lower_bound(id);
1338 uint left = index;
1339 while (left--) ++el;
1340 return el->second;
1343 ///=======================================================================================
1344 bool CParticleSystem::merge(CParticleSystemShape *pss)
1346 NL_PS_FUNC_MAIN(CParticleSystem_merge)
1347 nlassert(pss);
1348 nlassert(_Scene);
1349 CParticleSystem *duplicate = pss->instanciatePS(*this->_Scene); // duplicate the p.s. to merge
1350 // now we transfer the located of the duplicated ps to this object...
1351 for (TProcessVect::iterator it = duplicate->_ProcessVect.begin(); it != duplicate->_ProcessVect.end(); ++it)
1353 if (!attach(*it))
1355 for (TProcessVect::iterator clearIt = duplicate->_ProcessVect.begin(); clearIt != it; ++it)
1357 detach(getIndexOf(**it));
1359 nlwarning("<CParticleSystem::merge> Can't do the merge : this causes the system to last forever, and it has been flagged with 'BypassMaxNumIntegrationSteps'. Merge is not done.");
1360 return false;
1364 if (getBypassMaxNumIntegrationSteps())
1366 if (!canFinish())
1368 for (TProcessVect::iterator it = duplicate->_ProcessVect.begin(); it != duplicate->_ProcessVect.end(); ++it)
1370 detach(getIndexOf(**it));
1372 nlwarning("<CParticleSystem::merge> Can't do the merge : this causes the system to last forever, and it has been flagged with 'BypassMaxNumIntegrationSteps'. Merge is not done.");
1373 return false;
1377 duplicate->_ProcessVect.clear();
1378 delete duplicate;
1379 systemDurationChanged();
1380 CHECK_INTEGRITY
1381 return true;
1384 ///=======================================================================================
1385 void CParticleSystem::activatePresetBehaviour(TPresetBehaviour behaviour)
1387 NL_PS_FUNC_MAIN(CParticleSystem_activatePresetBehaviour)
1388 switch(behaviour)
1390 case EnvironmentFX:
1391 setDestroyModelWhenOutOfRange(false);
1392 setDestroyCondition(none);
1393 destroyWhenOutOfFrustum(false);
1394 setAnimType(AnimVisible);
1395 setBypassMaxNumIntegrationSteps(false);
1396 _KeepEllapsedTimeForLifeUpdate = false;
1397 break;
1398 case RunningEnvironmentFX:
1399 setDestroyModelWhenOutOfRange(false);
1400 setDestroyCondition(none);
1401 destroyWhenOutOfFrustum(false);
1402 setAnimType(AnimInCluster);
1403 setBypassMaxNumIntegrationSteps(false);
1404 _KeepEllapsedTimeForLifeUpdate = false;
1405 break;
1406 case SpellFX:
1407 setDestroyModelWhenOutOfRange(true);
1408 setDestroyCondition(noMoreParticles);
1409 destroyWhenOutOfFrustum(false);
1410 setAnimType(AnimAlways);
1411 setBypassMaxNumIntegrationSteps(true);
1412 _KeepEllapsedTimeForLifeUpdate = false;
1413 break;
1414 case LoopingSpellFX:
1415 setDestroyModelWhenOutOfRange(false);
1416 setDestroyCondition(noMoreParticles);
1417 destroyWhenOutOfFrustum(false);
1418 // setAnimType(AnimInCluster); // TODO : AnimAlways could be better ?
1419 setAnimType(AnimVisible);
1420 setBypassMaxNumIntegrationSteps(false);
1421 _KeepEllapsedTimeForLifeUpdate = false;
1422 break;
1423 case MinorFX:
1424 setDestroyModelWhenOutOfRange(true);
1425 setDestroyCondition(noMoreParticles);
1426 destroyWhenOutOfFrustum(true);
1427 setAnimType(AnimVisible);
1428 setBypassMaxNumIntegrationSteps(false);
1429 _KeepEllapsedTimeForLifeUpdate = false;
1430 break;
1431 case MovingLoopingFX:
1432 setDestroyModelWhenOutOfRange(false);
1433 setDestroyCondition(none);
1434 destroyWhenOutOfFrustum(false);
1435 setAnimType(AnimVisible);
1436 setBypassMaxNumIntegrationSteps(false);
1437 _KeepEllapsedTimeForLifeUpdate = true;
1438 break;
1439 case SpawnedEnvironmentFX:
1440 setDestroyModelWhenOutOfRange(true);
1441 setDestroyCondition(noMoreParticles);
1442 destroyWhenOutOfFrustum(false);
1443 setAnimType(AnimAlways);
1444 setBypassMaxNumIntegrationSteps(false);
1445 _KeepEllapsedTimeForLifeUpdate = false;
1446 break;
1447 case GroundFX:
1448 setDestroyModelWhenOutOfRange(false);
1449 setDestroyCondition(none);
1450 destroyWhenOutOfFrustum(false);
1451 setAnimType(AnimAlways);
1452 setBypassMaxNumIntegrationSteps(false);
1453 _KeepEllapsedTimeForLifeUpdate = true;
1454 break;
1455 case Projectile:
1456 setDestroyModelWhenOutOfRange(false);
1457 setDestroyCondition(noMoreParticles);
1458 destroyWhenOutOfFrustum(false);
1459 setAnimType(AnimVisible);
1460 setBypassMaxNumIntegrationSteps(false);
1461 _KeepEllapsedTimeForLifeUpdate = true;
1462 break;
1463 default: break;
1465 _PresetBehaviour = behaviour;
1469 ///=======================================================================================
1470 CParticleSystemProcess *CParticleSystem::detach(uint index)
1472 NL_PS_FUNC_MAIN(CParticleSystem_detach)
1473 nlassert(index < _ProcessVect.size());
1474 CParticleSystemProcess *proc = _ProcessVect[index];
1475 // release references other process may have to this system
1476 for(TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
1478 (*it)->releaseRefTo(proc);
1480 // erase from the vector
1481 _ProcessVect.erase(_ProcessVect.begin() + index);
1482 proc->setOwner(NULL);
1484 systemDurationChanged();
1485 // not part of this system any more
1486 return proc;
1489 ///=======================================================================================
1490 bool CParticleSystem::isProcess(const CParticleSystemProcess *process) const
1492 NL_PS_FUNC_MAIN(CParticleSystem_isProcess)
1493 for(TProcessVect::const_iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
1495 if (*it == process) return true;
1497 return false;
1500 ///=======================================================================================
1501 uint CParticleSystem::getIndexOf(const CParticleSystemProcess &process) const
1503 NL_PS_FUNC_MAIN(CParticleSystem_getIndexOf)
1504 #ifdef NL_DEBUG
1505 nlassert(isProcess(&process));
1506 #endif
1507 return process.getIndex();
1510 ///=======================================================================================
1511 uint CParticleSystem::getNumID() const
1513 NL_PS_FUNC_MAIN(CParticleSystem_getNumID)
1514 return (uint)_LBMap.size();
1517 ///=======================================================================================
1518 uint32 CParticleSystem::getID(uint index) const
1520 NL_PS_FUNC_MAIN(CParticleSystem_getID)
1521 TLBMap::const_iterator it = _LBMap.begin();
1522 for(uint k = 0; k < index; ++k)
1524 if (it == _LBMap.end()) return 0;
1525 ++it;
1527 return it->first;
1530 ///=======================================================================================
1531 void CParticleSystem::getIDs(std::vector<uint32> &dest) const
1533 NL_PS_FUNC_MAIN(CParticleSystem_getIDs)
1534 dest.resize(_LBMap.size());
1535 uint k = 0;
1536 for(TLBMap::const_iterator it = _LBMap.begin(); it != _LBMap.end(); ++it)
1538 dest[k] = it->first;
1539 ++k;
1543 ///=======================================================================================
1544 void CParticleSystem::interpolateFXPosDelta(NLMISC::CVector &dest, TAnimationTime deltaT)
1546 NL_PS_FUNC_MAIN(CParticleSystem_interpolateFXPosDelta)
1547 nlassert(_CoordSystemInfo.Matrix);
1548 dest = _CoordSystemInfo.CurrentDeltaPos - (deltaT * InverseTotalEllapsedTime) * (_CoordSystemInfo.Matrix->getPos() - _CoordSystemInfo.OldPos);
1551 ///=======================================================================================
1552 void CParticleSystem::interpolateUserPosDelta(NLMISC::CVector &dest, TAnimationTime deltaT)
1554 NL_PS_FUNC_MAIN(CParticleSystem_interpolateUserPosDelta)
1555 if (!_UserCoordSystemInfo)
1557 interpolateFXPosDelta(dest, deltaT);
1559 else
1561 CCoordSystemInfo &csi = _UserCoordSystemInfo->CoordSystemInfo;
1562 dest = csi.CurrentDeltaPos - (deltaT * InverseTotalEllapsedTime) * (csi.Matrix->getPos() - csi.OldPos);
1566 ///=======================================================================================
1567 void CParticleSystem::bindGlobalValueToUserParam(const std::string &globalValueName, uint userParamIndex)
1569 NL_PS_FUNC_MAIN(CParticleSystem_bindGlobalValueToUserParam)
1570 nlassert(userParamIndex < MaxPSUserParam);
1571 if (globalValueName.empty()) // disable a user param global value
1573 if (!_UserParamGlobalValue) return;
1574 _UserParamGlobalValue[userParamIndex] = NULL;
1575 for(uint k = 0; k < MaxPSUserParam; ++k)
1577 if (_UserParamGlobalValue[k] != NULL) return;
1579 // no more entry used
1580 delete _UserParamGlobalValue;
1581 _UserParamGlobalValue = NULL;
1583 else // enable a user param global value
1585 if (!_UserParamGlobalValue)
1587 // no table has been allocated yet, so create one
1588 _UserParamGlobalValue = new const TGlobalValuesMap::value_type *[MaxPSUserParam];
1589 std::fill(_UserParamGlobalValue, _UserParamGlobalValue + MaxPSUserParam, (TGlobalValuesMap::value_type *) NULL);
1591 // has the global value be created yet ?
1592 TGlobalValuesMap::const_iterator it = _GlobalValuesMap.find(globalValueName);
1593 if (it != _GlobalValuesMap.end())
1595 // yes, make a reference on it
1596 _UserParamGlobalValue[userParamIndex] = &(*it);
1598 else
1600 // create a new entry
1601 std::pair<TGlobalValuesMap::iterator, bool> itPair = _GlobalValuesMap.insert(TGlobalValuesMap::value_type(globalValueName, 0.f));
1602 _UserParamGlobalValue[userParamIndex] = &(*(itPair.first));
1607 ///=======================================================================================
1608 void CParticleSystem::setGlobalValue(const std::string &name, float value)
1610 NL_PS_FUNC(CParticleSystem_setGlobalValue)
1611 nlassert(!name.empty());
1612 NLMISC::clamp(value, 0.f, 1.f);
1613 _GlobalValuesMap[name] = value;
1616 ///=======================================================================================
1617 float CParticleSystem::getGlobalValue(const std::string &name)
1619 NL_PS_FUNC(CParticleSystem_getGlobalValue)
1620 TGlobalValuesMap::const_iterator it = _GlobalValuesMap.find(name);
1621 if (it != _GlobalValuesMap.end()) return it->second;
1622 return 0.f; // not a known value
1625 ///=======================================================================================
1626 std::string CParticleSystem::getGlobalValueName(uint userParamIndex) const
1628 NL_PS_FUNC_MAIN(CParticleSystem_getGlobalValueName)
1629 nlassert(userParamIndex < MaxPSUserParam);
1630 if (!_UserParamGlobalValue) return "";
1631 if (!_UserParamGlobalValue[userParamIndex]) return "";
1632 return _UserParamGlobalValue[userParamIndex]->first;
1635 ///=======================================================================================
1636 void CParticleSystem::setGlobalVectorValue(const std::string &name, const NLMISC::CVector &value)
1638 NL_PS_FUNC(CParticleSystem_setGlobalVectorValue)
1639 nlassert(!name.empty());
1640 _GlobalVectorValuesMap[name] = value;
1644 ///=======================================================================================
1645 NLMISC::CVector CParticleSystem::getGlobalVectorValue(const std::string &name)
1647 NL_PS_FUNC(CParticleSystem_getGlobalVectorValue)
1648 nlassert(!name.empty());
1649 TGlobalVectorValuesMap::const_iterator it = _GlobalVectorValuesMap.find(name);
1650 if (it != _GlobalVectorValuesMap.end()) return it->second;
1651 return NLMISC::CVector::Null; // not a known value
1654 ///=======================================================================================
1655 CParticleSystem::CGlobalVectorValueHandle CParticleSystem::getGlobalVectorValueHandle(const std::string &name)
1657 NL_PS_FUNC(CParticleSystem_getGlobalVectorValueHandle)
1658 nlassert(!name.empty());
1659 TGlobalVectorValuesMap::iterator it = _GlobalVectorValuesMap.find(name);
1660 if (it == _GlobalVectorValuesMap.end())
1662 it = _GlobalVectorValuesMap.insert(TGlobalVectorValuesMap::value_type(name, NLMISC::CVector::Null)).first;
1664 CGlobalVectorValueHandle handle;
1665 handle._Value = &it->second;
1666 handle._Name = &it->first;
1667 return handle;
1670 ///=======================================================================================
1671 void CParticleSystem::setMaxDistLODBias(float lodBias)
1673 NL_PS_FUNC_MAIN(CParticleSystem_setMaxDistLODBias)
1674 NLMISC::clamp(lodBias, 0.f, 1.f);
1675 _MaxDistLODBias = lodBias;
1678 ///=======================================================================================
1679 bool CParticleSystem::canFinish(CPSLocatedBindable **lastingForeverObj /*= NULL*/) const
1681 NL_PS_FUNC_MAIN(CParticleSystem_canFinish)
1682 if (hasLoop(lastingForeverObj)) return false;
1683 for(uint k = 0; k < _ProcessVect.size(); ++k)
1685 if (_ProcessVect[k]->isLocated())
1687 CPSLocated *loc = static_cast<CPSLocated *>(_ProcessVect[k]);
1688 if (loc->getLastForever())
1690 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
1692 CPSEmitter *em = dynamic_cast<CPSEmitter *>(loc->getBoundObject(l));
1693 if (em && em->testEmitForever())
1695 if (lastingForeverObj) *lastingForeverObj = em;
1696 return false;
1698 CPSParticle *p = dynamic_cast<CPSParticle *>(loc->getBoundObject(l));
1699 if (p)
1701 if (lastingForeverObj) *lastingForeverObj = p;
1702 return false; // particles shouldn't live forever, too
1708 return true;
1711 ///=======================================================================================
1712 bool CParticleSystem::hasLoop(CPSLocatedBindable **loopingObj /*= NULL*/) const
1714 NL_PS_FUNC_MAIN(CParticleSystem_hasLoop)
1715 // we want to check for loop like A emit B emit A
1716 // NB : there's room for a smarter algo here, but should not be useful for now
1717 for(uint k = 0; k < _ProcessVect.size(); ++k)
1719 if (_ProcessVect[k]->isLocated())
1721 CPSLocated *loc = static_cast<CPSLocated *>(_ProcessVect[k]);
1722 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
1724 CPSEmitter *em = dynamic_cast<CPSEmitter *>(loc->getBoundObject(l));
1725 if (em)
1727 if (em->checkLoop())
1729 if (loopingObj) *loopingObj = em;
1730 return true;
1736 return false;
1739 ///=======================================================================================
1740 void CParticleSystem::systemDurationChanged()
1742 NL_PS_FUNC_MAIN(CParticleSystem_systemDurationChanged)
1743 if (getAutoComputeDelayBeforeDeathConditionTest())
1745 setDelayBeforeDeathConditionTest(-1.f);
1749 ///=======================================================================================
1750 void CParticleSystem::setAutoComputeDelayBeforeDeathConditionTest(bool computeAuto)
1752 NL_PS_FUNC_MAIN(CParticleSystem_setAutoComputeDelayBeforeDeathConditionTest)
1753 if (computeAuto == _AutoComputeDelayBeforeDeathTest) return;
1754 _AutoComputeDelayBeforeDeathTest = computeAuto;
1755 if (computeAuto) setDelayBeforeDeathConditionTest(-1.f);
1758 ///=======================================================================================
1759 TAnimationTime CParticleSystem::getDelayBeforeDeathConditionTest() const
1761 NL_PS_FUNC_MAIN(CParticleSystem_getDelayBeforeDeathConditionTest)
1762 if (_DelayBeforeDieTest < 0.f)
1764 _DelayBeforeDieTest = evalDuration();
1766 return std::max(PS_MIN_TIMEOUT, _DelayBeforeDieTest);
1769 ///=======================================================================================
1770 // struct to eval duration of an emitter chain
1771 struct CToVisitEmitter
1773 float Duration; // cumuled duration of this emitter parent emitters
1774 const CPSLocated *Located;
1777 ///=======================================================================================
1778 float CParticleSystem::evalDuration() const
1780 NL_PS_FUNC_MAIN(CParticleSystem_evalDuration)
1781 std::vector<const CPSLocated *> visitedEmitter;
1782 std::vector<CToVisitEmitter> toVisitEmitter;
1783 float maxDuration = 0.f;
1784 for(uint k = 0; k < _ProcessVect.size(); ++k)
1786 if (_ProcessVect[k]->isLocated())
1788 bool emitterFound = false;
1789 const CPSLocated *loc = static_cast<const CPSLocated *>(_ProcessVect[k]);
1790 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
1792 const CPSLocatedBindable *bind = loc->getBoundObject(l);
1793 if (loc->getSize() > 0)
1795 switch(bind->getType())
1797 case PSParticle:
1799 if (loc->getLastForever())
1801 return -1;
1803 else
1805 maxDuration = std::max(maxDuration, loc->evalMaxDuration());
1808 break;
1809 case PSEmitter:
1811 if (!emitterFound)
1813 CToVisitEmitter tve;
1814 tve.Located = loc;
1815 tve.Duration = 0.f;
1816 toVisitEmitter.push_back(tve);
1817 emitterFound = true;
1820 break;
1824 visitedEmitter.clear();
1825 while (!toVisitEmitter.empty())
1827 const CPSLocated *loc = toVisitEmitter.back().Located;
1828 float duration = toVisitEmitter.back().Duration;
1829 toVisitEmitter.pop_back();
1830 visitedEmitter.push_back(loc);
1831 bool emitterFound = false;
1832 for(uint m = 0; m < loc->getNbBoundObjects(); ++m)
1834 const CPSLocatedBindable *bind = loc->getBoundObject(m);
1835 if (bind->getType() == PSEmitter)
1837 const CPSEmitter *em = NLMISC::safe_cast<const CPSEmitter *>(loc->getBoundObject(m));
1838 const CPSLocated *emittedType = em->getEmittedType();
1839 // continue if there's no loop
1840 if (std::find(visitedEmitter.begin(), visitedEmitter.end(), emittedType) == visitedEmitter.end())
1842 if (emittedType != NULL)
1844 emitterFound = true;
1845 CToVisitEmitter tve;
1846 tve.Located = emittedType;
1847 // if emitter has limited lifetime, use it
1848 if (!loc->getLastForever())
1850 tve.Duration = duration + loc->evalMaxDuration();
1852 else
1854 // try to eval duration depending on type
1855 switch(em->getEmissionType())
1857 case CPSEmitter::regular:
1859 if (em->getMaxEmissionCount() != 0)
1861 float period = em->getPeriodScheme() ? em->getPeriodScheme()->getMaxValue() : em->getPeriod();
1862 tve.Duration = duration + em->getEmitDelay() + period * em->getMaxEmissionCount();
1864 else
1866 tve.Duration = duration + em->getEmitDelay();
1869 break;
1870 case CPSEmitter::onDeath:
1871 case CPSEmitter::once:
1872 case CPSEmitter::onBounce:
1873 case CPSEmitter::externEmit:
1874 tve.Duration = duration; // can't eval duration ..
1875 break;
1876 default:
1877 break;
1880 toVisitEmitter.push_back(tve);
1885 if (!emitterFound)
1887 if (!loc->getLastForever())
1889 duration += loc->evalMaxDuration();
1891 maxDuration = std::max(maxDuration, duration);
1896 return maxDuration;
1899 ///=======================================================================================
1900 bool CParticleSystem::isDestroyConditionVerified() const
1902 NL_PS_FUNC_MAIN(CParticleSystem_isDestroyConditionVerified)
1903 if (getDestroyCondition() != CParticleSystem::none)
1905 if (getSystemDate() > getDelayBeforeDeathConditionTest())
1907 switch (getDestroyCondition())
1909 case CParticleSystem::noMoreParticles: return !hasParticles();
1910 case CParticleSystem::noMoreParticlesAndEmitters: return !hasParticles() && !hasEmitters();
1911 default: nlassert(0); return false;
1915 return false;
1918 ///=======================================================================================
1919 void CParticleSystem::setSystemDate(float date)
1921 NL_PS_FUNC_MAIN(CParticleSystem_setSystemDate)
1922 if (date == _SystemDate) return;
1923 _SystemDate = date;
1924 for(uint k = 0; k < _ProcessVect.size(); ++k)
1926 _ProcessVect[k]->systemDateChanged();
1930 ///=======================================================================================
1931 void CParticleSystem::registerSoundServer(UPSSoundServer *soundServer)
1933 NL_PS_FUNC(CParticleSystem_registerSoundServer)
1934 if (soundServer == _SoundServer) return;
1935 if (_SoundServer)
1937 CParticleSystemManager::stopSoundForAllManagers();
1939 _SoundServer = soundServer;
1940 if (_SoundServer)
1942 CParticleSystemManager::reactivateSoundForAllManagers();
1946 ///=======================================================================================
1947 void CParticleSystem::activateEmitters(bool active)
1949 NL_PS_FUNC_MAIN(CParticleSystem_activateEmitters)
1950 for(uint k = 0; k < getNbProcess(); ++k)
1952 if (getProcess(k)->isLocated())
1954 CPSLocated *loc = static_cast<CPSLocated *>(getProcess(k));
1955 if (loc)
1957 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
1959 if (loc->getBoundObject(l)->getType() == PSEmitter)
1960 loc->getBoundObject(l)->setActive(active);
1967 ///=======================================================================================
1968 bool CParticleSystem::hasActiveEmitters() const
1970 NL_PS_FUNC_MAIN(CParticleSystem_hasActiveEmitters)
1971 for(uint k = 0; k < getNbProcess(); ++k)
1973 if (getProcess(k)->isLocated())
1975 const CPSLocated *loc = static_cast<const CPSLocated *>(getProcess(k));
1976 if (loc)
1978 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
1980 if (loc->getBoundObject(l)->getType() == PSEmitter)
1982 if (loc->getBoundObject(l)->isActive()) return true;
1988 return false;
1991 ///=======================================================================================
1992 bool CParticleSystem::hasEmittersTemplates() const
1994 NL_PS_FUNC_MAIN(CParticleSystem_hasEmittersTemplates)
1995 for(uint k = 0; k < getNbProcess(); ++k)
1997 if (getProcess(k)->isLocated())
1999 const CPSLocated *loc = static_cast<const CPSLocated *>(getProcess(k));
2000 if (loc)
2002 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
2004 if (loc->getBoundObject(l)->getType() == PSEmitter)
2006 return true;
2012 return false;
2015 ///=======================================================================================
2016 void CParticleSystem::matchArraySize()
2018 NL_PS_FUNC_MAIN(CParticleSystem_matchArraySize)
2019 for(uint k = 0; k < getNbProcess(); ++k)
2021 if (getProcess(k)->isLocated())
2023 CPSLocated *loc = static_cast<CPSLocated *>(getProcess(k));
2024 loc->resize(loc->getSize()); // match the max size with the number of instances
2029 ///=======================================================================================
2030 uint CParticleSystem::getMaxNumParticles() const
2032 NL_PS_FUNC_MAIN(CParticleSystem_getMaxNumParticles)
2033 uint numParts = 0;
2034 for(uint k = 0; k < getNbProcess(); ++k)
2036 if (getProcess(k)->isLocated())
2038 const CPSLocated *loc = static_cast<const CPSLocated *>(getProcess(k));
2039 if (loc)
2041 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
2043 if (loc->getBoundObject(l)->getType() == PSParticle)
2045 numParts += loc->getMaxSize();
2051 return numParts;
2054 ///=======================================================================================
2055 uint CParticleSystem::getCurrNumParticles() const
2057 NL_PS_FUNC_MAIN(CParticleSystem_getCurrNumParticles)
2058 uint numParts = 0;
2059 for(uint k = 0; k < getNbProcess(); ++k)
2061 if (getProcess(k)->isLocated())
2063 const CPSLocated *loc = static_cast<const CPSLocated *>(getProcess(k));
2064 if (loc)
2066 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
2068 if (loc->getBoundObject(l)->getType() == PSParticle)
2070 numParts += loc->getSize();
2076 return numParts;
2079 ///=======================================================================================
2080 void CParticleSystem::getTargeters(const CPSLocated *target, std::vector<CPSTargetLocatedBindable *> &targeters)
2082 NL_PS_FUNC_MAIN(CParticleSystem_getTargeters)
2083 nlassert(target);
2084 nlassert(isProcess(target));
2085 targeters.clear();
2086 for(uint k = 0; k < getNbProcess(); ++k)
2088 if (getProcess(k)->isLocated())
2090 CPSLocated *loc = static_cast<CPSLocated *>(getProcess(k));
2091 if (loc)
2093 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
2095 CPSTargetLocatedBindable *targeter = dynamic_cast<CPSTargetLocatedBindable *>(loc->getBoundObject(l));
2096 if (targeter)
2098 for(uint m = 0; m < targeter->getNbTargets(); ++m)
2100 if (targeter->getTarget(m) == target)
2102 targeters.push_back(targeter);
2103 break;
2113 ///=======================================================================================
2114 void CParticleSystem::matrixModeChanged(CParticleSystemProcess *proc, TPSMatrixMode oldMode, TPSMatrixMode newMode)
2116 NL_PS_FUNC_MAIN(CParticleSystem_matrixModeChanged)
2117 nlassert(proc);
2118 // check that the located belong to that system
2119 nlassert(isProcess(proc));
2120 if (oldMode != PSUserMatrix && newMode == PSUserMatrix)
2122 addRefForUserSysCoordInfo();
2124 else if (oldMode == PSUserMatrix && newMode != PSUserMatrix)
2126 releaseRefForUserSysCoordInfo();
2130 ///=======================================================================================
2131 void CParticleSystem::addRefForUserSysCoordInfo(uint numRefs)
2133 NL_PS_FUNC_MAIN(CParticleSystem_addRefForUserSysCoordInfo)
2134 if (!numRefs) return;
2135 if (!_UserCoordSystemInfo)
2137 _UserCoordSystemInfo = new CUserCoordSystemInfo;
2139 nlassert(_UserCoordSystemInfo);
2140 _UserCoordSystemInfo->NumRef += numRefs;
2144 ///=======================================================================================
2145 void CParticleSystem::releaseRefForUserSysCoordInfo(uint numRefs)
2147 NL_PS_FUNC_MAIN(CParticleSystem_releaseRefForUserSysCoordInfo)
2148 if (!numRefs) return;
2149 nlassert(_UserCoordSystemInfo);
2150 nlassert(numRefs <= _UserCoordSystemInfo->NumRef);
2151 _UserCoordSystemInfo->NumRef -= numRefs;
2152 if (_UserCoordSystemInfo->NumRef == 0)
2154 delete _UserCoordSystemInfo;
2155 _UserCoordSystemInfo = NULL;
2159 ///=======================================================================================
2160 void CParticleSystem::checkIntegrity()
2162 NL_PS_FUNC_MAIN(CParticleSystem_checkIntegrity)
2163 // do some checks
2164 uint userMatrixUsageCount = 0;
2165 for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
2167 userMatrixUsageCount += (*it)->getUserMatrixUsageCount();
2169 if (userMatrixUsageCount == 0)
2171 nlassert(_UserCoordSystemInfo == NULL);
2173 else
2175 nlassert(_UserCoordSystemInfo != NULL);
2176 nlassert(_UserCoordSystemInfo->NumRef == userMatrixUsageCount);
2178 for(uint k = 0; k < _ProcessVect.size(); ++k)
2180 nlassert(_ProcessVect[k]->getOwner() == this);
2184 ///=======================================================================================
2185 void CParticleSystem::enumTexs(std::vector<NLMISC::CSmartPtr<ITexture> > &dest, IDriver &drv)
2187 NL_PS_FUNC_MAIN(CParticleSystem_enumTexs)
2188 for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
2190 (*it)->enumTexs(dest, drv);
2194 ///=======================================================================================
2195 void CParticleSystem::setZBias(float value)
2197 NL_PS_FUNC_MAIN(CParticleSystem_setZBias)
2198 for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
2200 (*it)->setZBias(value);
2204 ///=======================================================================================
2205 void CParticleSystem::getSortingByEmitterPrecedence(std::vector<uint> &result) const
2207 NL_PS_FUNC_MAIN(CParticleSystem_getSortingByEmitterPrecedence)
2208 #ifdef NL_DEBUG
2209 nlassert(!hasLoop()); // should be an acyclic graph, otherwise big problem....
2210 #endif
2211 typedef std::list<CParticleSystemProcess *> TProcessList;
2212 std::vector<TProcessList> degreeToNodes;
2213 std::vector<TProcessList::iterator> nodeToIterator(_ProcessVect.size());
2215 std::vector<uint> inDegree(_ProcessVect.size(), 0); // degree for each node
2216 for(uint k = 0; k < _ProcessVect.size(); ++k)
2218 if (_ProcessVect[k]->isLocated())
2220 CPSLocated *loc = static_cast<CPSLocated *>(_ProcessVect[k]);
2221 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
2223 if (loc->getBoundObject(l)->getType() == PSEmitter)
2225 CPSEmitter *pEmit = NLMISC::safe_cast<CPSEmitter *>(loc->getBoundObject(l));
2226 if (pEmit->getEmittedType())
2228 ++ inDegree[getIndexOf(*pEmit->getEmittedType())];
2234 // make enough room in degreeToNodes.
2235 for(uint k = 0; k < inDegree.size(); ++k)
2237 if (degreeToNodes.size() <= inDegree[k])
2239 degreeToNodes.resize(inDegree[k] + 1);
2242 // sort nodes by degree
2243 for(uint k = 0; k < inDegree.size(); ++k)
2245 // NB : could not do resize there because we keep iterators in the list, so it's done in the previous loop
2246 degreeToNodes[inDegree[k]].push_front(_ProcessVect[k]);
2247 nodeToIterator[k] = degreeToNodes[inDegree[k]].begin();
2250 #ifdef NL_DEBUG
2251 #define DO_SBEP_CHECK \
2252 { for(uint k = 0; k < degreeToNodes.size(); ++k) \
2254 for(TProcessList::const_iterator it = degreeToNodes[k].begin(); it != degreeToNodes[k].end(); ++it) \
2256 nlassert(inDegree[(*it)->getIndex()] == k); \
2259 #else
2260 #define DO_SBEP_CHECK
2261 #endif
2263 DO_SBEP_CHECK
2264 result.reserve(_ProcessVect.size());
2265 result.clear();
2266 if (degreeToNodes.empty()) return;
2267 // now, do the sort -> add each node with a degree of 0, and removes arc to their son (and insert in new good list according to their degree)
2268 while (!degreeToNodes[0].empty())
2270 DO_SBEP_CHECK
2271 CParticleSystemProcess *pr = degreeToNodes[0].front();
2272 degreeToNodes[0].pop_front();
2273 result.push_back(getIndexOf(*pr));
2274 if (pr->isLocated())
2276 CPSLocated *loc = static_cast<CPSLocated *>(pr);
2277 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
2279 if (loc->getBoundObject(l)->getType() == PSEmitter)
2281 CPSEmitter *pEmit = NLMISC::safe_cast<CPSEmitter *>(loc->getBoundObject(l));
2282 // update degree of node
2283 if (pEmit->getEmittedType())
2285 uint emittedLocIndex = getIndexOf(*pEmit->getEmittedType());
2286 uint degree = inDegree[emittedLocIndex];
2287 nlassert(degree != 0);
2288 degreeToNodes[degree - 1].splice(degreeToNodes[degree - 1].begin(), degreeToNodes[degree], nodeToIterator[emittedLocIndex]);
2289 nodeToIterator[emittedLocIndex] = degreeToNodes[degree - 1].begin(); // update iterator
2290 -- inDegree[emittedLocIndex];
2291 DO_SBEP_CHECK
2299 ///=======================================================================================
2300 void CParticleSystem::updateProcessIndices()
2302 NL_PS_FUNC_MAIN(CParticleSystem_updateProcessIndices)
2303 for(uint k = 0; k < _ProcessVect.size(); ++k)
2305 _ProcessVect[k]->setIndex(k);
2310 ///=======================================================================================
2311 void CParticleSystem::dumpHierarchy()
2313 NL_PS_FUNC_MAIN(CParticleSystem_dumpHierarchy)
2314 for(uint k = 0; k < _ProcessVect.size(); ++k)
2316 CPSLocated *loc = dynamic_cast<CPSLocated *>(_ProcessVect[k]);
2317 if (loc)
2319 nlinfo("Located k : %s @%p", loc->getName().c_str(), loc);
2320 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
2322 CPSEmitter *emitter = dynamic_cast<CPSEmitter *>(loc->getBoundObject(l));
2323 if (emitter)
2325 nlinfo(" emitter %s : emit %s @%p", emitter->getName().c_str(), emitter->getEmittedType() ? emitter->getEmittedType()->getName().c_str() : "none", emitter->getEmittedType());
2332 ///=======================================================================================
2333 void CParticleSystem::onShow(bool shown)
2335 NL_PS_FUNC_MAIN(CParticleSystem_onShow)
2336 for(uint k = 0; k < _ProcessVect.size(); ++k)
2338 _ProcessVect[k]->onShow(shown);
2343 } // NL3D