particle_system.cpp

Go to the documentation of this file.
00001 
00005 /* Copyright, 2001 Nevrax Ltd.
00006  *
00007  * This file is part of NEVRAX NEL.
00008  * NEVRAX NEL is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2, or (at your option)
00011  * any later version.
00012 
00013  * NEVRAX NEL is distributed in the hope that it will be useful, but
00014  * WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00016  * General Public License for more details.
00017 
00018  * You should have received a copy of the GNU General Public License
00019  * along with NEVRAX NEL; see the file COPYING. If not, write to the
00020  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00021  * MA 02111-1307, USA.
00022  */
00023 
00024 #include "std3d.h"
00025 
00026 
00027 
00028 #include "nel/3d/particle_system.h"
00029 #include "nel/3d/ps_located.h"
00030 #include "nel/3d/driver.h"
00031 #include "nel/3d/vertex_buffer.h"
00032 #include "nel/3d/material.h"
00033 #include "nel/3d/index_buffer.h"
00034 #include "nel/3d/nelu.h"
00035 #include "nel/3d/ps_util.h"
00036 #include "nel/3d/ps_particle.h"
00037 #include "nel/3d/ps_emitter.h"
00038 #include "nel/3d/ps_sound.h"
00039 #include "nel/3d/particle_system_shape.h"
00040 #include "nel/3d/ps_located.h"
00041 #include "nel/misc/aabbox.h"
00042 #include "nel/misc/file.h"
00043 #include "nel/misc/stream.h"
00044 
00045 // tmp
00046 #include "nel/3d/particle_system_model.h"
00047 
00048 
00049 #ifdef NL_DEBUG
00050     #define CHECK_INTEGRITY checkIntegrity();
00051 #else
00052     #define CHECK_INTEGRITY
00053 #endif
00054 
00055 
00056 
00057 
00058 namespace NL3D
00059 {
00060 
00061 uint32                                      CParticleSystem::NbParticlesDrawn = 0;
00062 UPSSoundServer *                            CParticleSystem::_SoundServer = NULL;
00063 CParticleSystem::TGlobalValuesMap           CParticleSystem::_GlobalValuesMap;
00064 CParticleSystem::TGlobalVectorValuesMap     CParticleSystem::_GlobalVectorValuesMap;
00065 
00066 // sim step infos
00067 TAnimationTime CParticleSystem::EllapsedTime = 0.f;
00068 TAnimationTime CParticleSystem::InverseTotalEllapsedTime = 0.f;
00069 TAnimationTime CParticleSystem::RealEllapsedTime = 0.f;
00070 float CParticleSystem::RealEllapsedTimeRatio = 1.f;
00071 bool CParticleSystem::InsideSimLoop = false;
00072 bool CParticleSystem::InsideRemoveLoop = false;
00073 bool CParticleSystem::InsideNewElementsLoop = false;;
00074 std::vector<NLMISC::CVector> CParticleSystem::_SpawnPos;
00075 
00076 
00077 
00078 //bool FilterPS[10] = { false, false, false, false, false, false, false, false, false, false };
00079 
00080 
00081 #ifdef NL_DEBUG
00082 uint    CParticleSystem::_NumInstances = 0;
00083 #endif
00084 
00085 
00086 static const float PS_MIN_TIMEOUT = 1.f; // the test that check if there are no particles left
00087 #if defined(NL_DEBUG) ||  defined(NL_PS_DEBUG)
00088     bool CParticleSystem::_SerialIdentifiers = true;
00089 #else
00090     bool CParticleSystem::_SerialIdentifiers = false;
00091 #endif
00092 bool CParticleSystem::_ForceDisplayBBox = false;
00093 CParticleSystemModel *CParticleSystem::OwnerModel = NULL;
00094 
00095 
00096 
00097 
00098 
00100 // CPaticleSystem implementation //
00102 
00103 
00105 const float PSDefaultMaxViewDist = 300.f;
00106 
00107 /*
00108  * Constructor
00109  */
00110 CParticleSystem::CParticleSystem() : _Driver(NULL),
00111                                      _FontGenerator(NULL),
00112                                      _FontManager(NULL),
00113                                      _UserCoordSystemInfo(NULL),
00114                                      _Date(0),
00115                                      _LastUpdateDate(-1),
00116                                      _CurrEditedElementLocated(NULL),
00117                                      _CurrEditedElementIndex(0),
00118                                      _Scene(NULL),
00119                                      _TimeThreshold(0.15f),
00120                                      _SystemDate(0.f),
00121                                      _MaxNbIntegrations(2),
00122                                      _LODRatio(0.5f),
00123                                      _OneMinusCurrentLODRatio(0),
00124                                      _MaxViewDist(PSDefaultMaxViewDist),
00125                                      _MaxDistLODBias(0.05f),
00126                                      _InvMaxViewDist(1.f / PSDefaultMaxViewDist),
00127                                      _InvCurrentViewDist(1.f / PSDefaultMaxViewDist),
00128                                      _AutoLODEmitRatio(0.f),
00129                                      _DieCondition(none),
00130                                      _DelayBeforeDieTest(-1.f),
00131                                      _NumWantedTris(0),
00132                                      _AnimType(AnimInCluster),
00133                                      _UserParamGlobalValue(NULL),
00134                                      _BypassGlobalUserParam(0),
00135                                      _PresetBehaviour(UserBehaviour),
00136                                      _AutoLODStartDistPercent(0.1f),
00137                                      _AutoLODDegradationExponent(1),
00138                                      _ColorAttenuationScheme(NULL),
00139                                      _GlobalColor(NLMISC::CRGBA::White),
00140                                      _GlobalColorLighted(NLMISC::CRGBA::White),
00141                                      _LightingColor(NLMISC::CRGBA::White),
00142                                      _UserColor(NLMISC::CRGBA::White),
00143                                      _ComputeBBox(true),
00144                                      _BBoxTouched(true),
00145                                      _AccurateIntegration(true),
00146                                      _CanSlowDown(true),
00147                                      _DestroyModelWhenOutOfRange(false),
00148                                      _DestroyWhenOutOfFrustum(false),
00149                                      _Sharing(false),
00150                                      _AutoLOD(false),
00151                                      _KeepEllapsedTimeForLifeUpdate(false),
00152                                      _AutoLODSkipParticles(false),
00153                                      _EnableLoadBalancing(true),
00154                                      _EmitThreshold(true),
00155                                      _BypassIntegrationStepLimit(false),
00156                                      _ForceGlobalColorLighting(false),
00157                                      _AutoComputeDelayBeforeDeathTest(true),
00158                                      _AutoCount(false),
00159                                      _HiddenAtCurrentFrame(true),
00160                                      _HiddenAtPreviousFrame(true)
00161 
00162 {
00163     NL_PS_FUNC_MAIN(CParticleSystem_CParticleSystem)
00164     std::fill(_UserParam, _UserParam + MaxPSUserParam, 0.0f);
00165     #ifdef NL_DEBUG
00166         ++_NumInstances;
00167     #endif
00168 
00169 }
00170 
00171 
00172 std::vector<NLMISC::CSmartPtr<CParticleSystem::CSpawnVect> > CParticleSystem::_Spawns; // spawns for the current system being processed
00173 std::vector<uint> CParticleSystem::_ParticleToRemove;
00174 std::vector<sint> CParticleSystem::_ParticleRemoveListIndex;
00175 
00176 
00177 
00178 
00181 void CParticleSystem::stopSound()
00182 {
00183     NL_PS_FUNC_MAIN(CParticleSystem_stopSound)
00184     for (uint k = 0; k < this->getNbProcess(); ++k)
00185     {
00186         CPSLocated *psl = dynamic_cast<NL3D::CPSLocated *>(this->getProcess(k));
00187         if (psl)
00188         {
00189             for (uint l = 0; l < psl->getNbBoundObjects(); ++l)
00190             {
00191                 if (psl->getBoundObject(l)->getType() == PSSound)
00192                 {
00193                     static_cast<CPSSound *>(psl->getBoundObject(l))->stopSound();
00194                 }
00195             }
00196         }
00197     }
00198 }
00199 
00201 void CParticleSystem::reactivateSound()
00202 {
00203     NL_PS_FUNC_MAIN(CParticleSystem_reactivateSound)
00204     for (uint k = 0; k < this->getNbProcess(); ++k)
00205     {
00206         CPSLocated *psl = dynamic_cast<NL3D::CPSLocated *>(this->getProcess(k));
00207         if (psl)
00208         {
00209             for (uint l = 0; l < psl->getNbBoundObjects(); ++l)
00210             {
00211                 if (psl->getBoundObject(l)->getType() == PSSound)
00212                 {
00213                     static_cast<CPSSound *>(psl->getBoundObject(l))->reactivateSound();
00214                 }
00215             }
00216         }
00217     }
00218 }
00219 
00220 
00222 void CParticleSystem::enableLoadBalancing(bool enabled /*=true*/)
00223 {
00224     NL_PS_FUNC_MAIN(CParticleSystem_enableLoadBalancing)
00225     if (enabled)
00226     {
00227         //notifyMaxNumFacesChanged();
00228     }
00229     _EnableLoadBalancing = enabled;
00230 }
00231 
00233 /*
00234 void CParticleSystem::notifyMaxNumFacesChanged(void)
00235 {
00236     if (!_EnableLoadBalancing) return;
00237     _MaxNumFacesWanted = 0;
00238     for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
00239     {
00240         _MaxNumFacesWanted += (*it)->querryMaxWantedNumFaces();
00241     }
00242 }
00243 */
00244 
00246 void CParticleSystem::updateNumWantedTris()
00247 {
00248     NL_PS_FUNC_MAIN(CParticleSystem_updateNumWantedTris)
00249     _NumWantedTris = 0;
00250     for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
00251     {
00252         _NumWantedTris += (*it)->getNumWantedTris();
00253     }
00254 }
00255 
00257 float CParticleSystem::getWantedNumTris(float dist)
00258 {
00259     NL_PS_FUNC_MAIN(CParticleSystem_getWantedNumTris)
00260     if (!_EnableLoadBalancing) return 0; // no contribution to the load balancing
00261     if (dist > _MaxViewDist) return 0;
00262     float retValue = ((1.f - dist * _InvMaxViewDist) * _NumWantedTris);
00264     return retValue;
00265 }
00266 
00267 
00269 void CParticleSystem::setNumTris(uint numFaces)
00270 {
00271     NL_PS_FUNC_MAIN(CParticleSystem_setNumTris)
00272     if (_EnableLoadBalancing)
00273     {
00274         float modelDist = (getSysMat().getPos() - _InvertedViewMat.getPos()).norm();
00275         /*uint numFaceWanted = (uint) getWantedNumTris(modelDist);*/
00276 
00277         const float epsilon = 10E-5f;
00278 
00279 
00280         uint wantedNumTri = (uint) getWantedNumTris(modelDist);
00281         if (numFaces >= wantedNumTri || wantedNumTri == 0 || _NumWantedTris == 0 || modelDist < epsilon)
00282         {
00283             _InvCurrentViewDist = _InvMaxViewDist;
00284         }
00285         else
00286         {
00287 
00288             _InvCurrentViewDist = (_NumWantedTris - numFaces) / (_NumWantedTris * modelDist);
00289         }
00290     }
00291     else
00292     {
00293         // always take full detail when there's no load balancing
00294         _InvCurrentViewDist = _InvMaxViewDist;
00295     }
00296 }
00297 
00298 
00301 CParticleSystem::~CParticleSystem()
00302 {
00303     NL_PS_FUNC_MAIN(CParticleSystem_CParticleSystemDtor)
00304     for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
00305     {
00306         delete *it;
00307     }
00308     if (_ColorAttenuationScheme)
00309         delete _ColorAttenuationScheme;
00310     if (_UserParamGlobalValue)
00311         delete _UserParamGlobalValue;
00312     delete _UserCoordSystemInfo;
00313     #ifdef NL_DEBUG
00314         --_NumInstances;
00315     #endif
00316 }
00317 
00319 void CParticleSystem::setViewMat(const NLMISC::CMatrix &m)
00320 {
00321     NL_PS_FUNC_MAIN(CParticleSystem_setViewMat)
00322     _ViewMat = m;
00323     _InvertedViewMat = m.inverted();
00324 }
00325 
00327 bool CParticleSystem::hasEmitters(void) const
00328 {
00329     NL_PS_FUNC_MAIN(CParticleSystem_hasEmitters)
00330     for (TProcessVect::const_iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
00331     {
00332         if ((*it)->hasEmitters()) return true;
00333     }
00334     return false;
00335 }
00336 
00338 bool CParticleSystem::hasParticles() const
00339 {
00340     NL_PS_FUNC_MAIN(CParticleSystem_hasParticles)
00341     for (TProcessVect::const_iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
00342     {
00343         if ((*it)->hasParticles()) return true;
00344     }
00345     return false;
00346 }
00347 
00349 bool CParticleSystem::hasTemporaryParticles() const
00350 {
00351     NL_PS_FUNC_MAIN(CParticleSystem_hasTemporaryParticles)
00352     for (TProcessVect::const_iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
00353     {
00354         if ((*it)->isLocated())
00355         {
00356             CPSLocated *loc = static_cast<CPSLocated *>(*it);
00357             if (loc->hasParticles()) return true;
00358         }
00359     }
00360     return false;
00361 }
00362 
00364 void CParticleSystem::stepLocated(TPSProcessPass pass)
00365 {
00366     NL_PS_FUNC_MAIN(CParticleSystem_stepLocated)
00367     for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
00368     {
00369         (*it)->step(pass);
00370     }
00371 }
00372 
00374 #ifndef NL_DEBUG
00375     inline
00376 #endif
00377 float CParticleSystem::getDistFromViewer() const
00378 {
00379     NL_PS_FUNC_MAIN(CParticleSystem_getDistFromViewer)
00380     const CVector d = getSysMat().getPos() - _InvertedViewMat.getPos();
00381     return d.norm();
00382 }
00383 
00385 #ifndef NL_DEBUG
00386     inline
00387 #endif
00388 float CParticleSystem::updateLODRatio()
00389 {
00390     NL_PS_FUNC_MAIN(CParticleSystem_updateLODRatio)
00391     float dist = getDistFromViewer();
00392     _OneMinusCurrentLODRatio = 1.f - (dist * _InvCurrentViewDist);
00393     NLMISC::clamp(_OneMinusCurrentLODRatio, 0.f, 1.f);
00394     return dist;
00395 }
00396 
00398 inline void CParticleSystem::updateColor(float distFromViewer)
00399 {
00400     NL_PS_FUNC_MAIN(CParticleSystem_updateColor)
00401     if (_ColorAttenuationScheme)
00402     {
00403         float ratio = distFromViewer * _InvMaxViewDist;
00404         NLMISC::clamp(ratio, 0.f, 1.f);
00405         _GlobalColor =  _ColorAttenuationScheme->get(ratio);
00406     }
00407     else
00408     {
00409         _GlobalColor = NLMISC::CRGBA::White;
00410     }
00411     _GlobalColor.modulateFromColor(_GlobalColor, _UserColor);
00412     _GlobalColorLighted.modulateFromColor(_GlobalColor, _LightingColor);
00413 }
00414 
00415 
00416 /*
00417 static void displaySysPos(IDriver *drv, const CVector &pos, CRGBA col)
00418 {
00419     if (!drv) return;
00420     drv->setupModelMatrix(CMatrix::Identity);
00421     CPSUtil::displayArrow(drv, pos, CVector::K, 1.f, CRGBA::White, col);
00422 }
00423 */
00424 
00425 
00427 void CParticleSystem::step(TPass pass, TAnimationTime ellapsedTime, CParticleSystemShape &shape, CParticleSystemModel &model)
00428 {
00429     NL_PS_FUNC_MAIN(CParticleSystem_step)
00430     CHECK_INTEGRITY
00431     OwnerModel = &model;
00432     nlassert(_CoordSystemInfo.Matrix); // matrix not set for position of system
00433     if (_UserCoordSystemInfo)
00434     {
00435         nlassert(_CoordSystemInfo.Matrix);
00436     }
00437     switch (pass)
00438     {
00439         case SolidRender:
00440             EllapsedTime = RealEllapsedTime = ellapsedTime;
00441             RealEllapsedTimeRatio = 1.f;
00443             if (_Sharing)
00444             {
00445                 float dist = updateLODRatio();
00446                 updateColor(dist);
00447             }
00448             else
00449             {
00450                 updateColor(getDistFromViewer());
00451             }
00452             // update time
00453             ++_Date;
00454             // update global color
00455             stepLocated(PSSolidRender);
00456 
00457         break;
00458         case BlendRender:
00459             EllapsedTime = RealEllapsedTime = ellapsedTime;
00460             RealEllapsedTimeRatio = 1.f;
00463             if (_Sharing)
00464             {
00465                 float dist = updateLODRatio();
00466                 updateColor(dist);
00467             }
00468             else
00469             {
00470                 updateColor(getDistFromViewer());
00471             }
00472             // update time
00473             ++_Date;
00474             // update global color
00475             stepLocated(PSBlendRender);
00476             if (_ForceDisplayBBox)
00477             {
00478                 NLMISC::CAABBox box;
00479                 computeBBox(box);
00480                 getDriver()->setupModelMatrix(*_CoordSystemInfo.Matrix);
00481                 CPSUtil::displayBBox(getDriver(), box);
00482             }
00483         break;
00484         case ToolRender:
00485             EllapsedTime = RealEllapsedTime = ellapsedTime;
00486             RealEllapsedTimeRatio = 1.f;
00487             stepLocated(PSToolRender);
00488         break;
00489         case Anim:
00490         {
00491             if (ellapsedTime <= 0.f) return;
00492             // update user param from global value if needed, unless this behaviour is bypassed has indicated by a flag in _BypassGlobalUserParam
00493             if (_UserParamGlobalValue)
00494             {
00495                 nlctassert(MaxPSUserParam < 8); // there should be less than 8 parameters because of mask stored in a byte
00496                 uint8 bypassMask = 1;
00497                 for(uint k = 0; k < MaxPSUserParam; ++k)
00498                 {
00499                     if (_UserParamGlobalValue[k] && !(_BypassGlobalUserParam & bypassMask)) // if there is a global value for this param and if the update is not bypassed
00500                     {
00501                         _UserParam[k] = _UserParamGlobalValue[k]->second;
00502                     }
00503                     bypassMask <<= 1;
00504                 }
00505             }
00506             //
00507             uint nbPass = 1;
00508             EllapsedTime = ellapsedTime;
00509             _BBoxTouched = true;
00510             if (_AccurateIntegration)
00511             {
00512                 if (EllapsedTime > _TimeThreshold)
00513                 {
00514                     nbPass = (uint32) ceilf(EllapsedTime / _TimeThreshold);
00515                     if (!_BypassIntegrationStepLimit && nbPass > _MaxNbIntegrations)
00516                     {
00517                         nbPass = _MaxNbIntegrations;
00518                         if (_CanSlowDown)
00519                         {
00520                             EllapsedTime = _TimeThreshold;
00521                             nlassert(_TimeThreshold != 0);
00522                             InverseTotalEllapsedTime = 1.f / (_TimeThreshold * nbPass);
00523                         }
00524                         else
00525                         {
00526                             EllapsedTime = ellapsedTime / nbPass;
00527                             InverseTotalEllapsedTime = ellapsedTime != 0 ? 1.f / ellapsedTime : 0.f;
00528                         }
00529                     }
00530                     else
00531                     {
00532                         EllapsedTime = ellapsedTime / nbPass;
00533                         InverseTotalEllapsedTime = ellapsedTime != 0 ? 1.f / ellapsedTime : 0.f;
00534                     }
00535                 }
00536                 else
00537                 {
00538                     InverseTotalEllapsedTime = ellapsedTime != 0 ? 1.f / ellapsedTime : 0.f;
00539                 }
00540             }
00541             else
00542             {
00543                 InverseTotalEllapsedTime = ellapsedTime != 0 ? 1.f / ellapsedTime : 0.f;
00544             }
00545             updateLODRatio();
00546             {
00547                 MINI_TIMER(PSAnim3)
00548                 if (_AutoLOD && !_Sharing)
00549                 {
00550                     float currLODRatio = 1.f - _OneMinusCurrentLODRatio;
00551                     if (currLODRatio <= _AutoLODStartDistPercent)
00552                     {
00553                         _AutoLODEmitRatio = 1.f; // no LOD applied
00554                     }
00555                     else
00556                     {
00557                         float lodValue = (currLODRatio - 1.f) / (_AutoLODStartDistPercent - 1.f);
00558                         NLMISC::clamp(lodValue, 0.f, 1.f);
00559                         float finalValue = lodValue;
00560                         for(uint l = 1; l < _AutoLODDegradationExponent; ++l)
00561                         {
00562                             finalValue *= lodValue;
00563                         }
00564                         _AutoLODEmitRatio = (1.f - _MaxDistLODBias) * finalValue + _MaxDistLODBias;
00565                         if (_AutoLODEmitRatio < 0.f) _AutoLODEmitRatio = 0.f;
00566                     }
00567                 }
00568             }
00569             {
00570                 MINI_TIMER(PSAnim4)
00571                 // set start position. Used by emitters that emit from Local basis to world
00572                 if (!_HiddenAtPreviousFrame && !_HiddenAtCurrentFrame)
00573                 {
00574                     _CoordSystemInfo.CurrentDeltaPos = _CoordSystemInfo.OldPos - _CoordSystemInfo.Matrix->getPos();
00575                     if (_UserCoordSystemInfo)
00576                     {
00577                         CCoordSystemInfo &csi = _UserCoordSystemInfo->CoordSystemInfo;
00578                         csi.CurrentDeltaPos = csi.OldPos - csi.Matrix->getPos();
00579                     }
00580                 }
00581                 else
00582                 {
00583                     _CoordSystemInfo.CurrentDeltaPos = NLMISC::CVector::Null;
00584                     _CoordSystemInfo.OldPos = _CoordSystemInfo.Matrix->getPos();
00585                     if (_UserCoordSystemInfo)
00586                     {
00587                         CCoordSystemInfo &csi = _UserCoordSystemInfo->CoordSystemInfo;
00588                         csi.CurrentDeltaPos = NLMISC::CVector::Null;
00589                         csi.OldPos = csi.Matrix->getPos();
00590                     }
00591                 }
00592                 //displaySysPos(_Driver, _CurrentDeltaPos + _OldSysMat.getPos(), CRGBA::Red);
00593                 // process passes
00594             }
00595             RealEllapsedTime = _KeepEllapsedTimeForLifeUpdate ? (ellapsedTime / nbPass)
00596                                                           : EllapsedTime;
00597             RealEllapsedTimeRatio = RealEllapsedTime / EllapsedTime;
00598             /*PSMaxET = std::max(PSMaxET, ellapsedTime);
00599             PSMaxNBPass = std::max(PSMaxNBPass, nbPass);*/
00600 
00601 
00602 
00603             // Sort by emission depth. We assume that the ps is a directed acyclic graph (so no loop will be encountered)
00604             if (shape._ProcessOrder.empty())
00605             {
00606                 getSortingByEmitterPrecedence(shape._ProcessOrder);
00607             }
00608             // nodes sorted by degree
00609             InsideSimLoop = true;
00610             // make enough room for spawns
00611             uint numProcess = _ProcessVect.size();
00612             if (numProcess > _Spawns.size())
00613             {
00614                 uint oldSize = _Spawns.size();
00615                 _Spawns.resize(numProcess);
00616                 for(uint k = oldSize; k < numProcess; ++k)
00617                 {
00618                     _Spawns[k] = new CSpawnVect;
00619                 }
00620             }
00621             for(uint k = 0; k < numProcess; ++k)
00622             {
00623                 if (!_ProcessVect[k]->isLocated()) continue;
00624                 CPSLocated *loc = static_cast<CPSLocated *>(_ProcessVect[k]);
00625                 if (loc->hasLODDegradation())
00626                 {
00627                     loc->doLODDegradation();
00628                 }
00629                 _Spawns[k]->MaxNumSpawns = loc->getMaxSize();
00630             }
00631             do
00632             {
00633                 {
00634                     MINI_TIMER(PSAnim5)
00635                     // position of the system at the end of the integration
00636                     _CoordSystemInfo.CurrentDeltaPos += (_CoordSystemInfo.Matrix->getPos() - _CoordSystemInfo.OldPos) * (EllapsedTime * InverseTotalEllapsedTime);
00637                     if (_UserCoordSystemInfo)
00638                     {
00639                         CCoordSystemInfo &csi = _UserCoordSystemInfo->CoordSystemInfo;
00640                         csi.CurrentDeltaPos += (csi.Matrix->getPos() - csi.OldPos) * (EllapsedTime * InverseTotalEllapsedTime);
00641                     }
00642                 }
00643                 for(uint k = 0; k < shape._ProcessOrder.size(); ++k)
00644                 {
00645                     if (!_ProcessVect[shape._ProcessOrder[k]]->isLocated()) continue;
00646                     CPSLocated *loc = static_cast<CPSLocated *>(_ProcessVect[shape._ProcessOrder[k]]);
00647                     if (_ParticleRemoveListIndex.size() < loc->getMaxSize())
00648                     {
00649                         _ParticleRemoveListIndex.resize(loc->getMaxSize(), -1);
00650                     }
00651                     if (loc->getSize() != 0)
00652                     {
00653                         #ifdef NL_DEBUG
00654                             loc->checkLife();
00655                         #endif
00656                         if (loc->hasCollisionInfos())
00657                         {
00658                             loc->resetCollisions(loc->getSize());
00659                         }
00660                         // compute motion (including collisions)
00661                         if (!loc->isParametricMotionEnabled()) loc->computeMotion();
00662                         // Update life and mark particles that must be removed
00663                         loc->updateLife();
00664                         // Spawn particles. Emitters date is updated only after so we check in CPSLocated::postNewElement
00665                         // if the emitter was still alive at this date, otherwise we discard the post
00666                         loc->computeSpawns(0, false);
00667                         if (loc->hasCollisionInfos()) loc->updateCollisions();
00668                         // Remove too old particles, making room for new ones
00669                         if (!_ParticleToRemove.empty())
00670                         {
00671                             loc->removeOldParticles();
00672                         }
00673                         #ifdef NL_DEBUG
00674                             loc->checkLife();
00675                         #endif
00676                     }
00677                     if (!_Spawns[shape._ProcessOrder[k]]->SpawnInfos.empty())
00678                     {
00679                         uint insertionIndex = loc->getSize(); // index at which new particles will be inserted
00680                         // add new particles that where posted by ancestor emitters, and also mark those that must already be deleted
00681                         loc->addNewlySpawnedParticles();
00682                         if (insertionIndex != loc->getSize())
00683                         {
00684                             // Compute motion for spawned particles. This is useful only if particle can collide because
00685                             if (loc->hasCollisionInfos())
00686                             {
00687                                 loc->resetCollisions(loc->getSize());
00688                                 loc->computeNewParticleMotion(insertionIndex);
00689                             }
00690                             loc->computeSpawns(insertionIndex, true);
00691                             if (loc->hasCollisionInfos()) loc->updateCollisions();
00692                             // Remove too old particles among the newly created ones.
00693                             if (!_ParticleToRemove.empty())
00694                             {
00695                                 loc->removeOldParticles();
00696                             }
00697                             #ifdef NL_DEBUG
00698                                 loc->checkLife();
00699                             #endif
00700                         }
00701                     }
00702                     if (!loc->isParametricMotionEnabled()) loc->computeForces();
00703                 }
00704                 _SystemDate += RealEllapsedTime;
00705                 for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
00706                 {
00707                     #ifdef NL_DEBUG
00708                         if ((*it)->isLocated())
00709                         {
00710                             CPSLocated *loc = static_cast<CPSLocated *>(*it);
00711                             loc->checkLife();
00712                         }
00713                     #endif
00714                     {
00715                         MINI_TIMER(PSAnim2)
00716                         (*it)->step(PSMotion);
00717                     }
00718                 }
00719             }
00720             while (--nbPass);
00721             {
00722                 MINI_TIMER(PSAnim10)
00723                 // perform parametric motion if present
00724                 for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
00725                 {
00726                     if ((*it)->isParametricMotionEnabled()) (*it)->performParametricMotion(_SystemDate);
00727                 }
00728             }
00729 
00730             updateNumWantedTris();
00731 
00732             InsideSimLoop = false;
00733 
00734             {
00735                 MINI_TIMER(PSAnim11)
00736                 // memorize position of matrix for next frame (becomes old position)
00737                 _CoordSystemInfo.OldPos = _CoordSystemInfo.Matrix->getPos();
00738                 if (_UserCoordSystemInfo)
00739                 {
00740                     CCoordSystemInfo &csi = _UserCoordSystemInfo->CoordSystemInfo;
00741                     csi.OldPos =  csi.Matrix->getPos();
00742                 }
00743 
00744                 _HiddenAtPreviousFrame = _HiddenAtCurrentFrame;
00745             }
00746         }
00747     }
00748     RealEllapsedTimeRatio = 0.f;
00749     CHECK_INTEGRITY
00750 }
00751 
00752 
00754 void CParticleSystem::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
00755 {
00756     CHECK_INTEGRITY
00757     NL_PS_FUNC_MAIN(CParticleSystem_serial)
00758     sint version =  f.serialVersion(19);
00759 
00760     // version 19: sysmat no more serialized (useless)
00761     // version 18: _AutoComputeDelayBeforeDeathTest
00762     // version 17: _ForceGlobalColorLighting flag
00763     // version 16: _BypassIntegrationStepLimit flag
00764     // version 14: emit threshold
00765     // version 13: max dist lod bias for auto-LOD
00766     // version 12: global userParams
00767     // version 11: enable load balancing flag
00768     // version 9: Sharing flag added
00769     //            Auto-lod parameters
00770     //            New integration flag
00771     //            Global color attenuation
00772     // version 8: Replaced the attribute '_PerformMotionWhenOutOfFrustum' by a _AnimType field which allow more precise control
00773 
00774     //f.serial(_ViewMat);
00775 
00776     // patch for old fx : force to recompute duration when fx is saved to avoid prbs
00777     if (!f.isReading())
00778     {
00779         if (_AutoComputeDelayBeforeDeathTest)
00780         {
00781             _DelayBeforeDieTest = evalDuration();
00782         }
00783     }
00784 
00785     if (version < 19)
00786     {
00787         NLMISC::CMatrix dummy;
00788         f.serial(dummy);
00789     }
00790     f.serial(_Date);
00791     if (f.isReading())
00792     {
00793         delete _ColorAttenuationScheme;
00794         // delete previous multimap
00795         _LBMap.clear();
00796         // delete previously attached process
00797         TProcessVect::iterator it;
00798         for (it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
00799         {
00800             delete (*it);
00801         }
00802 
00803         _ProcessVect.clear();
00804 
00805         f.serialContPolyPtr(_ProcessVect);
00806 
00807         _FontGenerator = NULL;
00808         _FontManager = NULL;
00809         if (_UserParamGlobalValue)
00810             delete _UserParamGlobalValue;
00811         _UserParamGlobalValue = NULL;
00812         _BypassGlobalUserParam = 0;
00813         // see if some process need to access the user matrix
00814         delete _UserCoordSystemInfo;
00815         _UserCoordSystemInfo = NULL;
00816         for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
00817         {
00818             addRefForUserSysCoordInfo((*it)->getUserMatrixUsageCount());
00819         }
00820     }
00821     else
00822     {
00823         f.serialContPolyPtr(_ProcessVect);
00824     }
00825 
00826     if (version > 1) // name of the system
00827     {
00828         if (f.isReading() && !getSerializeIdentifierFlag())
00829         {
00830             // just skip the name
00831             sint32 len;
00832             f.serial(len);
00833             f.seek(len, NLMISC::IStream::current);
00834         }
00835         else
00836         {
00837             f.serial(_Name);
00838         }
00839     }
00840 
00841     if (version > 2) // infos about integration, and LOD
00842     {
00843         bool accurateIntegration = _AccurateIntegration; // read from bitfield
00844         f.serial(accurateIntegration);
00845         _AccurateIntegration = accurateIntegration;
00846         if (_AccurateIntegration)
00847         {
00848             bool canSlowDown = _CanSlowDown;
00849             f.serial(canSlowDown);
00850             _CanSlowDown = canSlowDown;
00851             f.serial(_TimeThreshold, _MaxNbIntegrations);
00852         }
00853         f.serial(_InvMaxViewDist, _LODRatio);
00854         _MaxViewDist = 1.f / _InvMaxViewDist;
00855         _InvCurrentViewDist = _InvMaxViewDist;
00856     }
00857 
00858     if (version > 3) // tell whether the system must compute his bbox, hold a precomputed bbox
00859     {
00860         bool computeBBox = _ComputeBBox;
00861         f.serial(computeBBox);
00862         _ComputeBBox = computeBBox;
00863         if (!computeBBox)
00864         {
00865             f.serial(_PreComputedBBox);
00866         }
00867     }
00868 
00869     if (version > 4) // lifetime informations
00870     {
00871         bool destroyModelWhenOutOfRange = _DestroyModelWhenOutOfRange; // read from bitfield
00872         f.serial(destroyModelWhenOutOfRange);
00873         _DestroyModelWhenOutOfRange = destroyModelWhenOutOfRange;
00874         f.serialEnum(_DieCondition);
00875         if (_DieCondition != none)
00876         {
00877             f.serial(_DelayBeforeDieTest);
00878         }
00879     }
00880 
00881     if (version > 5)
00882     {
00883         bool destroyWhenOutOfFrustum = _DestroyWhenOutOfFrustum; // read from bitfield
00884         f.serial(destroyWhenOutOfFrustum);
00885         _DestroyWhenOutOfFrustum = destroyWhenOutOfFrustum;
00886     }
00887 
00888     if (version > 6 && version < 8)
00889     {
00890         bool performMotionWOOF;
00891         if (f.isReading())
00892         {
00893             f.serial(performMotionWOOF);
00894             performMotionWhenOutOfFrustum(performMotionWOOF);
00895         }
00896         else
00897         {
00898             performMotionWOOF = doesPerformMotionWhenOutOfFrustum();
00899             f.serial(performMotionWOOF);
00900         }
00901     }
00902 
00903     if (version > 7)
00904     {
00905         f.serialEnum(_AnimType);
00906         f.serialEnum(_PresetBehaviour);
00907     }
00908 
00909     if (version > 8)
00910     {
00911         bool sharing = _Sharing; // read from bitfield
00912         f.serial(sharing);
00913         _Sharing = sharing;
00914         bool autoLOD = _AutoLOD; // read from bitfield
00915         f.serial(autoLOD);
00916         _AutoLOD = autoLOD;
00917 
00918         if (_AutoLOD)
00919         {
00920             f.serial(_AutoLODStartDistPercent, _AutoLODDegradationExponent);
00921             bool autoLODSkipParticles = _AutoLODSkipParticles; // read from bitfield
00922             f.serial(autoLODSkipParticles);
00923             _AutoLODSkipParticles = autoLODSkipParticles;
00924         }
00925         bool keepEllapsedTimeForLifeUpdate = _KeepEllapsedTimeForLifeUpdate;
00926         f.serial(keepEllapsedTimeForLifeUpdate);
00927         _KeepEllapsedTimeForLifeUpdate = keepEllapsedTimeForLifeUpdate;
00928         f.serialPolyPtr(_ColorAttenuationScheme);
00929     }
00930 
00931     if (version >= 11)
00932     {
00933         bool enableLoadBalancing = _EnableLoadBalancing; // read from bitfield
00934         f.serial(enableLoadBalancing);
00935         _EnableLoadBalancing = enableLoadBalancing;
00936     }
00937 
00938     if (version >= 12)
00939     {
00940         // serial infos about global user params
00941         nlctassert(MaxPSUserParam < 8); // In this version mask of used global user params are stored in a byte..
00942         if (f.isReading())
00943         {
00944             uint8 mask;
00945             f.serial(mask);
00946             if (mask)
00947             {
00948                 std::string globalValueName;
00949                 uint8 testMask = 1;
00950                 for(uint k = 0; k < MaxPSUserParam; ++k)
00951                 {
00952                     if (mask & testMask)
00953                     {
00954                         f.serial(globalValueName);
00955                         bindGlobalValueToUserParam(globalValueName.c_str(), k);
00956                     }
00957                     testMask <<= 1;
00958                 }
00959             }
00960         }
00961         else
00962         {
00963             uint8 mask = 0;
00964             if (_UserParamGlobalValue)
00965             {
00966                 for(uint k = 0; k < MaxPSUserParam; ++k)
00967                 {
00968                     if (_UserParamGlobalValue[k]) mask |= (1 << k);
00969                 }
00970             }
00971             f.serial(mask);
00972             if (_UserParamGlobalValue)
00973             {
00974                 for(uint k = 0; k < MaxPSUserParam; ++k)
00975                 {
00976                     if (_UserParamGlobalValue[k])
00977                     {
00978                         std::string valueName = _UserParamGlobalValue[k]->first;
00979                         f.serial(valueName);
00980                     }
00981                 }
00982             }
00983         }
00984     }
00985     if (version >= 13)
00986     {
00987         if (_AutoLOD && !_Sharing)
00988         {
00989             f.serial(_MaxDistLODBias);
00990         }
00991     }
00992     if (version >= 14)
00993     {
00994         bool emitThreshold = _EmitThreshold; // read from bitfiled
00995         f.serial(emitThreshold);
00996         _EmitThreshold = emitThreshold;
00997     }
00998 
00999     if (version >= 15)
01000     {
01001         bool bypassIntegrationStepLimit = _BypassIntegrationStepLimit; // read from bitfield
01002         f.serial(bypassIntegrationStepLimit);
01003         _BypassIntegrationStepLimit = bypassIntegrationStepLimit;
01004     }
01005 
01006     if (version >= 17)
01007     {
01008         bool forceGlobalColorLighting = _ForceGlobalColorLighting; // read from bitfield
01009         f.serial(forceGlobalColorLighting);
01010         _ForceGlobalColorLighting = forceGlobalColorLighting;
01011     }
01012 
01013     if (version >= 18)
01014     {
01015         bool autoComputeDelayBeforeDeathTest = _AutoComputeDelayBeforeDeathTest; // read from bitfield
01016         f.serial(autoComputeDelayBeforeDeathTest);
01017         _AutoComputeDelayBeforeDeathTest = autoComputeDelayBeforeDeathTest;
01018     }
01019     else
01020     {
01021         nlassert(f.isReading());
01022         // for all previously created system, force to eval the system duration in an automatyic way
01023         setDelayBeforeDeathConditionTest(-1.f);
01024         _AutoComputeDelayBeforeDeathTest = true;
01025     }
01026 
01027     if (f.isReading())
01028     {
01029         //notifyMaxNumFacesChanged();
01030         updateNumWantedTris();
01031         activatePresetBehaviour(_PresetBehaviour); // apply behaviour changes
01032         updateProcessIndices();
01033     }
01034     CHECK_INTEGRITY
01035     // tmp
01036     //if (f.isReading())
01037     //  {
01038     //      dumpHierarchy();
01039     //  }
01040 
01041 }
01042 
01044 bool CParticleSystem::attach(CParticleSystemProcess *ptr)
01045 {
01046     NL_PS_FUNC_MAIN(CParticleSystem_attach)
01047     nlassert(ptr);
01048     nlassert(std::find(_ProcessVect.begin(), _ProcessVect.end(), ptr) == _ProcessVect.end() ); // can't attach twice
01049     //nlassert(ptr->getOwner() == NULL);
01050     _ProcessVect.push_back(ptr);
01051     ptr->setOwner(this);
01052     ptr->setIndex(_ProcessVect.size() - 1);
01053     //notifyMaxNumFacesChanged();
01054     if (getBypassMaxNumIntegrationSteps())
01055     {
01056         if (!canFinish())
01057         {
01058             remove(ptr);
01059             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");
01060             return false;
01061         }
01062     }
01063     systemDurationChanged();
01064     return true;
01065 }
01066 
01068 void CParticleSystem::remove(CParticleSystemProcess *ptr)
01069 {
01070     NL_PS_FUNC_MAIN(CParticleSystem_remove)
01071     TProcessVect::iterator it = std::find(_ProcessVect.begin(), _ProcessVect.end(), ptr);
01072     nlassert(it != _ProcessVect.end() );
01073     ptr->setOwner(NULL);
01074     _ProcessVect.erase(it);
01075     delete ptr;
01076     systemDurationChanged();
01077     updateProcessIndices();
01078 }
01079 
01081 void CParticleSystem::forceComputeBBox(NLMISC::CAABBox &aabbox)
01082 {
01083     NL_PS_FUNC_MAIN(CParticleSystem_forceComputeBBox)
01084     bool foundOne = false;
01085     NLMISC::CAABBox tmpBox;
01086     for (TProcessVect::const_iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
01087     {
01088         if ((*it)->computeBBox(tmpBox))
01089         {
01090             // rotate the aabbox so that it is in the correct basis
01091             const CMatrix &convMat = CPSLocated::getConversionMatrix(*this, PSFXWorldMatrix, (*it)->getMatrixMode());
01092             tmpBox = NLMISC::CAABBox::transformAABBox(convMat, tmpBox);
01093 
01094             if (foundOne)
01095             {
01096                 aabbox = NLMISC::CAABBox::computeAABBoxUnion(aabbox, tmpBox);
01097             }
01098             else
01099             {
01100                 aabbox = tmpBox;
01101                 foundOne = true;
01102             }
01103         }
01104     }
01105 
01106     if (!foundOne)
01107     {
01108         aabbox.setCenter(NLMISC::CVector::Null);
01109         aabbox.setHalfSize(NLMISC::CVector::Null);
01110     }
01111 }
01112 
01114 void CParticleSystem::computeBBox(NLMISC::CAABBox &aabbox)
01115 {
01116     NL_PS_FUNC_MAIN(CParticleSystem_computeBBox)
01117     if (!_ComputeBBox || !_BBoxTouched)
01118     {
01119         aabbox = _PreComputedBBox;
01120         return;
01121     }
01122     forceComputeBBox(aabbox);
01123     _BBoxTouched = false;
01124     _PreComputedBBox = aabbox;
01125 }
01126 
01128 void CParticleSystem::setSysMat(const CMatrix *m)
01129 {
01130     NL_PS_FUNC_MAIN(CParticleSystem_setSysMat)
01131     _CoordSystemInfo.Matrix = m;
01132     if (_SystemDate == 0.f)
01133     {
01134         _CoordSystemInfo.OldPos = m ? m->getPos() : CVector::Null;
01135     }
01136     if (!m) return;
01137     _CoordSystemInfo.InvMatrix = _CoordSystemInfo.Matrix->inverted();
01138 }
01139 
01141 void CParticleSystem::setUserMatrix(const NLMISC::CMatrix *m)
01142 {
01143     NL_PS_FUNC_MAIN(CParticleSystem_setUserMatrix)
01144     if (!_UserCoordSystemInfo) return; // no process in the system references the user matrix
01145     CCoordSystemInfo &csi = _UserCoordSystemInfo->CoordSystemInfo;
01146     csi.Matrix = m;
01147     if (_SystemDate == 0.f)
01148     {
01149         csi.OldPos = m ? m->getPos() : getSysMat().getPos(); // _CoordSystemInfo.Matrix is relevant if at least one call to setSysMat has been performed before
01150     }
01151     if (!m) return;
01152     csi.InvMatrix = csi.Matrix->inverted();
01153     // build conversion matrix between father user matrix & fx matrix
01154     // TODO : lazy evaluation for this ?
01155     _UserCoordSystemInfo->UserBasisToFXBasis = _CoordSystemInfo.InvMatrix * *(csi.Matrix);
01156     _UserCoordSystemInfo->FXBasisToUserBasis =  csi.InvMatrix * getSysMat();
01157 }
01158 
01160 bool CParticleSystem::hasOpaqueObjects(void) const
01161 {
01162     NL_PS_FUNC_MAIN(CParticleSystem_hasOpaqueObjects)
01164     for (TProcessVect::const_iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
01165     {
01166         if ((*it)->isLocated())
01167         {
01168             CPSLocated *loc = static_cast<CPSLocated *>(*it);
01169             for (uint k = 0; k < loc->getNbBoundObjects(); ++k)
01170             {
01171                 CPSLocatedBindable *lb = loc->getBoundObject(k);
01172                 if (lb->getType() == PSParticle)
01173                 {
01174                     if (((CPSParticle *) lb)->hasOpaqueFaces()) return true;
01175                 }
01176             }
01177         }
01178     }
01179     return false;
01180 }
01181 
01183 bool CParticleSystem::hasTransparentObjects(void) const
01184 {
01185     NL_PS_FUNC_MAIN(CParticleSystem_hasTransparentObjects)
01187     for (TProcessVect::const_iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
01188     {
01189         if ((*it)->isLocated())
01190         {
01191             CPSLocated *loc = static_cast<CPSLocated *>(*it);
01192             for (uint k = 0; k < loc->getNbBoundObjects(); ++k)
01193             {
01194                 CPSLocatedBindable *lb = loc->getBoundObject(k);
01195                 if (lb->getType() == PSParticle)
01196                 {
01197                     if (((CPSParticle *) lb)->hasTransparentFaces()) return true;
01198                 }
01199             }
01200         }
01201     }
01202     return false;
01203 }
01204 
01206 bool CParticleSystem::hasLightableObjects() const
01207 {
01208     NL_PS_FUNC_MAIN(CParticleSystem_hasLightableObjects)
01209     if (_ForceGlobalColorLighting) return true;
01211     for (TProcessVect::const_iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
01212     {
01213         if ((*it)->isLocated())
01214         {
01215             CPSLocated *loc = static_cast<CPSLocated *>(*it);
01216             for (uint k = 0; k < loc->getNbBoundObjects(); ++k)
01217             {
01218                 CPSLocatedBindable *lb = loc->getBoundObject(k);
01219                 if (lb->getType() == PSParticle)
01220                 {
01221                     if (((CPSParticle *) lb)->hasLightableFaces()) return true;
01222                     if (((CPSParticle *) lb)->usesGlobalColorLighting()) return true;
01223                 }
01224             }
01225         }
01226     }
01227     return false;
01228 }
01229 
01231 void CParticleSystem::getLODVect(NLMISC::CVector &v, float &offset,  TPSMatrixMode matrixMode)
01232 {
01233     NL_PS_FUNC_MAIN(CParticleSystem_getLODVect)
01234     switch(matrixMode)
01235     {
01236         case PSFXWorldMatrix:
01237         {
01238             const CVector tv = getInvertedSysMat().mulVector(_InvertedViewMat.getJ());
01239             const CVector org = getInvertedSysMat() * _InvertedViewMat.getPos();
01240             v = _InvCurrentViewDist * tv;
01241             offset = - org * v;
01242         }
01243         break;
01244         case PSIdentityMatrix:
01245         {
01246             v = _InvCurrentViewDist * _InvertedViewMat.getJ();
01247             offset = - _InvertedViewMat.getPos() * v;
01248         }
01249         break;
01250         case PSUserMatrix:
01251         {
01252             const CVector tv = getInvertedUserMatrix().mulVector(_InvertedViewMat.getJ());
01253             const CVector org = getInvertedUserMatrix() * _InvertedViewMat.getPos();
01254             v = _InvCurrentViewDist * tv;
01255             offset = - org * v;
01256         }
01257         break;
01258         default:
01259             nlassert(0);
01260         break;
01261     }
01262 }
01263 
01265 TPSLod CParticleSystem::getLOD(void) const
01266 {
01267     NL_PS_FUNC_MAIN(CParticleSystem_getLOD)
01268     const float dist = fabsf(_InvCurrentViewDist * (getSysMat().getPos() - _InvertedViewMat.getPos()) * _InvertedViewMat.getJ());
01269     return dist > _LODRatio ? PSLod2 : PSLod1;
01270 }
01271 
01272 
01274 void CParticleSystem::registerLocatedBindableExternID(uint32 id, CPSLocatedBindable *lb)
01275 {
01276     NL_PS_FUNC_MAIN(CParticleSystem_registerLocatedBindableExternID)
01277     nlassert(lb);
01278     nlassert(lb->getOwner() && lb->getOwner()->getOwner() == this); // the located bindable must belong to that system
01279     #ifdef NL_DEBUG
01280         // check that this lb hasn't been inserted yet
01281         TLBMap::iterator lbd = _LBMap.lower_bound(id), ubd = _LBMap.upper_bound(id);
01282         nlassert(std::find(lbd, ubd, TLBMap::value_type (id, lb)) == ubd);
01283         nlassert(std::find(lbd, ubd, TLBMap::value_type (id, lb)) == ubd );
01284 
01285     #endif
01286         _LBMap.insert(TLBMap::value_type (id, lb) );
01287 }
01288 
01290 void CParticleSystem::unregisterLocatedBindableExternID(CPSLocatedBindable *lb)
01291 {
01292     NL_PS_FUNC_MAIN(CParticleSystem_unregisterLocatedBindableExternID)
01293     nlassert(lb);
01294     nlassert(lb->getOwner() && lb->getOwner()->getOwner() == this); // the located bindable must belong to that system
01295     uint32 id = lb->getExternID();
01296     if (!id) return;
01297     TLBMap::iterator lbd = _LBMap.lower_bound(id), ubd = _LBMap.upper_bound(id);
01298     TLBMap::iterator el = std::find(lbd, ubd, TLBMap::value_type (id, lb));
01299     nlassert(el != ubd);
01300     _LBMap.erase(el);
01301 }
01302 
01304 uint CParticleSystem::getNumLocatedBindableByExternID(uint32 id) const
01305 {
01306     NL_PS_FUNC_MAIN(CParticleSystem_getNumLocatedBindableByExternID)
01307     return _LBMap.count(id);
01308 }
01309 
01311 CPSLocatedBindable *CParticleSystem::getLocatedBindableByExternID(uint32 id, uint index)
01312 {
01313     NL_PS_FUNC_MAIN(CParticleSystem_getLocatedBindableByExternID)
01314     if (index >= _LBMap.count(id))
01315     {
01316         return NULL;
01317     }
01318     TLBMap::const_iterator el = _LBMap.lower_bound(id);
01319     uint left = index;
01320     while (left--) ++el;
01321     return  el->second;
01322 
01323 }
01324 
01326 const CPSLocatedBindable *CParticleSystem::getLocatedBindableByExternID(uint32 id, uint index) const
01327 {
01328     NL_PS_FUNC_MAIN(CParticleSystem_getLocatedBindableByExternID)
01329     if (index >= _LBMap.count(id))
01330     {
01331         return NULL;
01332     }
01333     TLBMap::const_iterator el = _LBMap.lower_bound(id);
01334     uint left = index;
01335     while (left--) ++el;
01336     return  el->second;
01337 }
01338 
01340 bool CParticleSystem::merge(CParticleSystemShape *pss)
01341 {
01342     NL_PS_FUNC_MAIN(CParticleSystem_merge)
01343     nlassert(pss);
01344     nlassert(_Scene);
01345     CParticleSystem *duplicate = pss->instanciatePS(*this->_Scene); // duplicate the p.s. to merge
01346     // now we transfer the located of the duplicated ps to this object...
01347     for (TProcessVect::iterator it = duplicate->_ProcessVect.begin(); it != duplicate->_ProcessVect.end(); ++it)
01348     {
01349         if (!attach(*it))
01350         {
01351             for (TProcessVect::iterator clearIt = duplicate->_ProcessVect.begin(); clearIt != it; ++it)
01352             {
01353                 detach(getIndexOf(**it));
01354             }
01355             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.");
01356             return false;
01357         }
01358     }
01359     //
01360     if (getBypassMaxNumIntegrationSteps())
01361     {
01362         if (!canFinish())
01363         {
01364             for (TProcessVect::iterator it = duplicate->_ProcessVect.begin(); it != duplicate->_ProcessVect.end(); ++it)
01365             {
01366                 detach(getIndexOf(**it));
01367             }
01368             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.");
01369             return false;
01370         }
01371     }
01372     //
01373     duplicate->_ProcessVect.clear();
01374     delete duplicate;
01375     systemDurationChanged();
01376     CHECK_INTEGRITY
01377     return true;
01378 }
01379 
01381 void CParticleSystem::activatePresetBehaviour(TPresetBehaviour behaviour)
01382 {
01383     NL_PS_FUNC_MAIN(CParticleSystem_activatePresetBehaviour)
01384     switch(behaviour)
01385     {
01386         case EnvironmentFX:
01387             setDestroyModelWhenOutOfRange(false);
01388             setDestroyCondition(none);
01389             destroyWhenOutOfFrustum(false);
01390             setAnimType(AnimVisible);
01391             setBypassMaxNumIntegrationSteps(false);
01392             _KeepEllapsedTimeForLifeUpdate = false;
01393         break;
01394         case RunningEnvironmentFX:
01395             setDestroyModelWhenOutOfRange(false);
01396             setDestroyCondition(none);
01397             destroyWhenOutOfFrustum(false);
01398             setAnimType(AnimInCluster);
01399             setBypassMaxNumIntegrationSteps(false);
01400             _KeepEllapsedTimeForLifeUpdate = false;
01401         break;
01402         case SpellFX:
01403             setDestroyModelWhenOutOfRange(true);
01404             setDestroyCondition(noMoreParticles);
01405             destroyWhenOutOfFrustum(false);
01406             setAnimType(AnimAlways);
01407             setBypassMaxNumIntegrationSteps(true);
01408             _KeepEllapsedTimeForLifeUpdate = false;
01409         break;
01410         case LoopingSpellFX:
01411             setDestroyModelWhenOutOfRange(false);
01412             setDestroyCondition(noMoreParticles);
01413             destroyWhenOutOfFrustum(false);
01414             // setAnimType(AnimInCluster); // TODO : AnimAlways could be better ?
01415             setAnimType(AnimVisible);
01416             setBypassMaxNumIntegrationSteps(false);
01417             _KeepEllapsedTimeForLifeUpdate = false;
01418         break;
01419         case MinorFX:
01420             setDestroyModelWhenOutOfRange(true);
01421             setDestroyCondition(noMoreParticles);
01422             destroyWhenOutOfFrustum(true);
01423             setAnimType(AnimVisible);
01424             setBypassMaxNumIntegrationSteps(false);
01425             _KeepEllapsedTimeForLifeUpdate = false;
01426         break;
01427         case MovingLoopingFX:
01428             setDestroyModelWhenOutOfRange(false);
01429             setDestroyCondition(none);
01430             destroyWhenOutOfFrustum(false);
01431             setAnimType(AnimVisible);
01432             setBypassMaxNumIntegrationSteps(false);
01433             _KeepEllapsedTimeForLifeUpdate = true;
01434         break;
01435         case SpawnedEnvironmentFX:
01436             setDestroyModelWhenOutOfRange(true);
01437             setDestroyCondition(noMoreParticles);
01438             destroyWhenOutOfFrustum(false);
01439             setAnimType(AnimAlways);
01440             setBypassMaxNumIntegrationSteps(false);
01441             _KeepEllapsedTimeForLifeUpdate = false;
01442         break;
01443         case GroundFX:
01444             setDestroyModelWhenOutOfRange(false);
01445             setDestroyCondition(none);
01446             destroyWhenOutOfFrustum(false);
01447             setAnimType(AnimAlways);
01448             setBypassMaxNumIntegrationSteps(false);
01449             _KeepEllapsedTimeForLifeUpdate = true;
01450         break;
01451         case Projectile:
01452             setDestroyModelWhenOutOfRange(false);
01453             setDestroyCondition(noMoreParticles);
01454             destroyWhenOutOfFrustum(false);
01455             setAnimType(AnimVisible);
01456             setBypassMaxNumIntegrationSteps(false);
01457             _KeepEllapsedTimeForLifeUpdate = true;
01458         break;
01459         default: break;
01460     }
01461     _PresetBehaviour = behaviour;
01462 }
01463 
01464 
01466 CParticleSystemProcess *CParticleSystem::detach(uint index)
01467 {
01468     NL_PS_FUNC_MAIN(CParticleSystem_detach)
01469     nlassert(index < _ProcessVect.size());
01470     CParticleSystemProcess *proc = _ProcessVect[index];
01471     // release references other process may have to this system
01472     for(TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
01473     {
01474         (*it)->releaseRefTo(proc);
01475     }
01476     // erase from the vector
01477     _ProcessVect.erase(_ProcessVect.begin() + index);
01478     proc->setOwner(NULL);
01479     //
01480     systemDurationChanged();
01481     // not part of this system any more
01482     return proc;
01483 }
01484 
01486 bool CParticleSystem::isProcess(const CParticleSystemProcess *process) const
01487 {
01488     NL_PS_FUNC_MAIN(CParticleSystem_isProcess)
01489     for(TProcessVect::const_iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
01490     {
01491         if (*it == process) return true;
01492     }
01493     return false;
01494 }
01495 
01497 uint CParticleSystem::getIndexOf(const CParticleSystemProcess &process) const
01498 {
01499     NL_PS_FUNC_MAIN(CParticleSystem_getIndexOf)
01500     #ifdef NL_DEBUG
01501         nlassert(isProcess(&process));
01502     #endif
01503         return process.getIndex();
01504 }
01505 
01507 uint CParticleSystem::getNumID() const
01508 {
01509     NL_PS_FUNC_MAIN(CParticleSystem_getNumID)
01510     return _LBMap.size();
01511 }
01512 
01514 uint32 CParticleSystem::getID(uint index) const
01515 {
01516     NL_PS_FUNC_MAIN(CParticleSystem_getID)
01517     TLBMap::const_iterator it = _LBMap.begin();
01518     for(uint k = 0; k < index; ++k)
01519     {
01520         if (it == _LBMap.end()) return 0;
01521         ++it;
01522     }
01523     return it->first;
01524 }
01525 
01527 void CParticleSystem::getIDs(std::vector<uint32> &dest) const
01528 {
01529     NL_PS_FUNC_MAIN(CParticleSystem_getIDs)
01530     dest.resize(_LBMap.size());
01531     uint k = 0;
01532     for(TLBMap::const_iterator it = _LBMap.begin(); it != _LBMap.end(); ++it)
01533     {
01534         dest[k] = it->first;
01535         ++k;
01536     }
01537 }
01538 
01540 void CParticleSystem::interpolateFXPosDelta(NLMISC::CVector &dest, TAnimationTime deltaT)
01541 {
01542     NL_PS_FUNC_MAIN(CParticleSystem_interpolateFXPosDelta)
01543     nlassert(_CoordSystemInfo.Matrix);
01544     dest = _CoordSystemInfo.CurrentDeltaPos - (deltaT * InverseTotalEllapsedTime) * (_CoordSystemInfo.Matrix->getPos() - _CoordSystemInfo.OldPos);
01545 }
01546 
01548 void CParticleSystem::interpolateUserPosDelta(NLMISC::CVector &dest, TAnimationTime deltaT)
01549 {
01550     NL_PS_FUNC_MAIN(CParticleSystem_interpolateUserPosDelta)
01551     if (!_UserCoordSystemInfo)
01552     {
01553         interpolateFXPosDelta(dest, deltaT);
01554     }
01555     else
01556     {
01557         CCoordSystemInfo &csi = _UserCoordSystemInfo->CoordSystemInfo;
01558         dest = csi.CurrentDeltaPos - (deltaT * InverseTotalEllapsedTime) * (csi.Matrix->getPos() - csi.OldPos);
01559     }
01560 }
01561 
01563 void CParticleSystem::bindGlobalValueToUserParam(const std::string &globalValueName, uint userParamIndex)
01564 {
01565     NL_PS_FUNC_MAIN(CParticleSystem_bindGlobalValueToUserParam)
01566     nlassert(userParamIndex < MaxPSUserParam);
01567     if (globalValueName.empty()) // disable a user param global value
01568     {
01569         if (!_UserParamGlobalValue) return;
01570         _UserParamGlobalValue[userParamIndex] = NULL;
01571         for(uint k = 0; k < MaxPSUserParam; ++k)
01572         {
01573             if (_UserParamGlobalValue[k] != NULL) return;
01574         }
01575         // no more entry used
01576         delete _UserParamGlobalValue;
01577         _UserParamGlobalValue = NULL;
01578     }
01579     else // enable a user param global value
01580     {
01581         if (!_UserParamGlobalValue)
01582         {
01583             // no table has been allocated yet, so create one
01584             _UserParamGlobalValue = new const TGlobalValuesMap::value_type *[MaxPSUserParam];
01585             std::fill(_UserParamGlobalValue, _UserParamGlobalValue + MaxPSUserParam, (TGlobalValuesMap::value_type *) NULL);
01586         }
01587         // has the global value be created yet ?
01588         TGlobalValuesMap::const_iterator it = _GlobalValuesMap.find(globalValueName);
01589         if (it != _GlobalValuesMap.end())
01590         {
01591             // yes, make a reference on it
01592             _UserParamGlobalValue[userParamIndex] = &(*it);
01593         }
01594         else
01595         {
01596             // create a new entry
01597             std::pair<TGlobalValuesMap::iterator, bool> itPair = _GlobalValuesMap.insert(TGlobalValuesMap::value_type(globalValueName, 0.f));
01598             _UserParamGlobalValue[userParamIndex] = &(*(itPair.first));
01599         }
01600     }
01601 }
01602 
01604 void CParticleSystem::setGlobalValue(const std::string &name, float value)
01605 {
01606     NL_PS_FUNC(CParticleSystem_setGlobalValue)
01607     nlassert(!name.empty());
01608     NLMISC::clamp(value, 0.f, 1.f);
01609     _GlobalValuesMap[name] = value;
01610 }
01611 
01613 float CParticleSystem::getGlobalValue(const std::string &name)
01614 {
01615     NL_PS_FUNC(CParticleSystem_getGlobalValue)
01616     TGlobalValuesMap::const_iterator it = _GlobalValuesMap.find(name);
01617     if (it != _GlobalValuesMap.end()) return it->second;
01618     return 0.f; // not a known value
01619 }
01620 
01622 std::string CParticleSystem::getGlobalValueName(uint userParamIndex) const
01623 {
01624     NL_PS_FUNC_MAIN(CParticleSystem_getGlobalValueName)
01625     nlassert(userParamIndex < MaxPSUserParam);
01626     if (!_UserParamGlobalValue) return "";
01627     if (!_UserParamGlobalValue[userParamIndex]) return "";
01628     return _UserParamGlobalValue[userParamIndex]->first;
01629 }
01630 
01632 void CParticleSystem::setGlobalVectorValue(const std::string &name, const NLMISC::CVector &value)
01633 {
01634     NL_PS_FUNC(CParticleSystem_setGlobalVectorValue)
01635     nlassert(!name.empty());
01636     _GlobalVectorValuesMap[name] = value;
01637 }
01638 
01639 
01641 NLMISC::CVector CParticleSystem::getGlobalVectorValue(const std::string &name)
01642 {
01643     NL_PS_FUNC(CParticleSystem_getGlobalVectorValue)
01644     nlassert(!name.empty());
01645     TGlobalVectorValuesMap::const_iterator it = _GlobalVectorValuesMap.find(name);
01646     if (it != _GlobalVectorValuesMap.end()) return it->second;
01647     return NLMISC::CVector::Null; // not a known value
01648 }
01649 
01651 CParticleSystem::CGlobalVectorValueHandle CParticleSystem::getGlobalVectorValueHandle(const std::string &name)
01652 {
01653     NL_PS_FUNC(CParticleSystem_getGlobalVectorValueHandle)
01654     nlassert(!name.empty());
01655     TGlobalVectorValuesMap::iterator it = _GlobalVectorValuesMap.find(name);
01656     if (it == _GlobalVectorValuesMap.end())
01657     {
01658         it = _GlobalVectorValuesMap.insert(TGlobalVectorValuesMap::value_type(name, NLMISC::CVector::Null)).first;
01659     }
01660     CGlobalVectorValueHandle handle;
01661     handle._Value = &it->second;
01662     handle._Name = &it->first;
01663     return handle;
01664 }
01665 
01667 void CParticleSystem::setMaxDistLODBias(float lodBias)
01668 {
01669     NL_PS_FUNC_MAIN(CParticleSystem_setMaxDistLODBias)
01670     NLMISC::clamp(lodBias, 0.f, 1.f);
01671     _MaxDistLODBias = lodBias;
01672 }
01673 
01675 bool CParticleSystem::canFinish(CPSLocatedBindable **lastingForeverObj /*= NULL*/) const
01676 {
01677     NL_PS_FUNC_MAIN(CParticleSystem_canFinish)
01678     if (hasLoop(lastingForeverObj)) return false;
01679     for(uint k = 0; k < _ProcessVect.size(); ++k)
01680     {
01681         if (_ProcessVect[k]->isLocated())
01682         {
01683             CPSLocated *loc = static_cast<CPSLocated *>(_ProcessVect[k]);
01684             if (loc->getLastForever())
01685             {
01686                 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
01687                 {
01688                     CPSEmitter *em = dynamic_cast<CPSEmitter *>(loc->getBoundObject(l));
01689                     if (em && em->testEmitForever())
01690                     {
01691                         if (lastingForeverObj) *lastingForeverObj = em;
01692                         return false;
01693                     }
01694                     CPSParticle *p = dynamic_cast<CPSParticle *>(loc->getBoundObject(l));
01695                     if (p)
01696                     {
01697                         if (lastingForeverObj) *lastingForeverObj = p;
01698                         return false; // particles shouldn't live forever, too
01699                     }
01700                 }
01701             }
01702         }
01703     }
01704     return true;
01705 }
01706 
01708 bool CParticleSystem::hasLoop(CPSLocatedBindable **loopingObj /*= NULL*/) const
01709 {
01710     NL_PS_FUNC_MAIN(CParticleSystem_hasLoop)
01711     // we want to check for loop like A emit B emit A
01712     // NB : there's room for a smarter algo here, but should not be useful for now
01713     for(uint k = 0; k < _ProcessVect.size(); ++k)
01714     {
01715         if (_ProcessVect[k]->isLocated())
01716         {
01717             CPSLocated *loc = static_cast<CPSLocated *>(_ProcessVect[k]);
01718             for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
01719             {
01720                 CPSEmitter *em = dynamic_cast<CPSEmitter *>(loc->getBoundObject(l));
01721                 if (em)
01722                 {
01723                     if (em->checkLoop())
01724                     {
01725                         if (loopingObj) *loopingObj = em;
01726                         return true;
01727                     }
01728                 }
01729             }
01730         }
01731     }
01732     return false;
01733 }
01734 
01736 void CParticleSystem::systemDurationChanged()
01737 {
01738     NL_PS_FUNC_MAIN(CParticleSystem_systemDurationChanged)
01739     if (getAutoComputeDelayBeforeDeathConditionTest())
01740     {
01741         setDelayBeforeDeathConditionTest(-1.f);
01742     }
01743 }
01744 
01746 void CParticleSystem::setAutoComputeDelayBeforeDeathConditionTest(bool computeAuto)
01747 {
01748     NL_PS_FUNC_MAIN(CParticleSystem_setAutoComputeDelayBeforeDeathConditionTest)
01749     if (computeAuto == _AutoComputeDelayBeforeDeathTest) return;
01750     _AutoComputeDelayBeforeDeathTest = computeAuto;
01751     if (computeAuto) setDelayBeforeDeathConditionTest(-1.f);
01752 }
01753 
01755 TAnimationTime CParticleSystem::getDelayBeforeDeathConditionTest() const
01756 {
01757     NL_PS_FUNC_MAIN(CParticleSystem_getDelayBeforeDeathConditionTest)
01758     if (_DelayBeforeDieTest < 0.f)
01759     {
01760         _DelayBeforeDieTest = evalDuration();
01761     }
01762     return std::max(PS_MIN_TIMEOUT, _DelayBeforeDieTest);
01763 }
01764 
01766 // struct to eval duration of an emitter chain
01767 struct CToVisitEmitter
01768 {
01769     float       Duration; // cumuled duration of this emitter parent emitters
01770     const CPSLocated *Located;
01771 };
01772 
01774 float CParticleSystem::evalDuration() const
01775 {
01776     NL_PS_FUNC_MAIN(CParticleSystem_evalDuration)
01777     std::vector<const CPSLocated *> visitedEmitter;
01778     std::vector<CToVisitEmitter> toVisitEmitter;
01779     float maxDuration = 0.f;
01780     for(uint k = 0; k < _ProcessVect.size(); ++k)
01781     {
01782         if (_ProcessVect[k]->isLocated())
01783         {
01784             bool emitterFound = false;
01785             const CPSLocated *loc = static_cast<const CPSLocated *>(_ProcessVect[k]);
01786             for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
01787             {
01788                 const CPSLocatedBindable *bind = loc->getBoundObject(l);
01789                 if (loc->getSize() > 0)
01790                 {
01791                     switch(bind->getType())
01792                     {
01793                         case  PSParticle:
01794                         {
01795                             if (loc->getLastForever())
01796                             {
01797                                 return -1;
01798                             }
01799                             else
01800                             {
01801                                 maxDuration = std::max(maxDuration, loc->evalMaxDuration());
01802                             }
01803                         }
01804                         break;
01805                         case PSEmitter:
01806                         {
01807                             if (!emitterFound)
01808                             {
01809                                 CToVisitEmitter tve;
01810                                 tve.Located = loc;
01811                                 tve.Duration = 0.f;
01812                                 toVisitEmitter.push_back(tve);
01813                                 emitterFound = true;
01814                             }
01815                         }
01816                         break;
01817                     }
01818                 }
01819             }
01820             visitedEmitter.clear();
01821             while (!toVisitEmitter.empty())
01822             {
01823                 const CPSLocated *loc = toVisitEmitter.back().Located;
01824                 float duration = toVisitEmitter.back().Duration;
01825                 toVisitEmitter.pop_back();
01826                 visitedEmitter.push_back(loc);
01827                 bool emitterFound = false;
01828                 for(uint m = 0; m < loc->getNbBoundObjects(); ++m)
01829                 {
01830                     const CPSLocatedBindable *bind = loc->getBoundObject(m);
01831                     if (bind->getType() == PSEmitter)
01832                     {
01833                         const CPSEmitter *em = NLMISC::safe_cast<const CPSEmitter *>(loc->getBoundObject(m));
01834                         const CPSLocated *emittedType = em->getEmittedType();
01835                         // continue if there's no loop
01836                         if (std::find(visitedEmitter.begin(), visitedEmitter.end(), emittedType) == visitedEmitter.end())
01837                         {
01838                             if (emittedType != NULL)
01839                             {
01840                                 emitterFound = true;
01841                                 CToVisitEmitter tve;
01842                                 tve.Located = emittedType;
01843                                 // if emitter has limited lifetime, use it
01844                                 if (!loc->getLastForever())
01845                                 {
01846                                     tve.Duration = duration + loc->evalMaxDuration();
01847                                 }
01848                                 else
01849                                 {
01850                                     // try to eval duration depending on type
01851                                     switch(em->getEmissionType())
01852                                     {
01853                                         case CPSEmitter::regular:
01854                                         {
01855                                             if (em->getMaxEmissionCount() != 0)
01856                                             {
01857                                                 float period = em->getPeriodScheme() ? em->getPeriodScheme()->getMaxValue() : em->getPeriod();
01858                                                 tve.Duration = duration + em->getEmitDelay() +  period * em->getMaxEmissionCount();
01859                                             }
01860                                             else
01861                                             {
01862                                                 tve.Duration = duration + em->getEmitDelay();
01863                                             }
01864                                         }
01865                                         break;
01866                                         case CPSEmitter::onDeath:
01867                                         case CPSEmitter::once:
01868                                         case CPSEmitter::onBounce:
01869                                         case CPSEmitter::externEmit:
01870                                             tve.Duration = duration; // can't eval duration ..
01871                                         break;
01872                                         default:
01873                                             break;
01874                                     }
01875                                 }
01876                                 toVisitEmitter.push_back(tve);
01877                             }
01878                         }
01879                     }
01880                 }
01881                 if (!emitterFound)
01882                 {
01883                     if (!loc->getLastForever())
01884                     {
01885                         duration += loc->evalMaxDuration();
01886                     }
01887                     maxDuration = std::max(maxDuration, duration);
01888                 }
01889             }
01890         }
01891     }
01892     return maxDuration;
01893 }
01894 
01896 bool CParticleSystem::isDestroyConditionVerified() const
01897 {
01898     NL_PS_FUNC_MAIN(CParticleSystem_isDestroyConditionVerified)
01899     if (getDestroyCondition() != CParticleSystem::none)
01900     {
01901         if (getSystemDate() > getDelayBeforeDeathConditionTest())
01902         {
01903             switch (getDestroyCondition())
01904             {
01905                 case CParticleSystem::noMoreParticles: return !hasParticles();
01906                 case CParticleSystem::noMoreParticlesAndEmitters: return !hasParticles() && !hasEmitters();
01907                 default: nlassert(0); return false;
01908             }
01909         }
01910     }
01911     return false;
01912 }
01913 
01915 void CParticleSystem::setSystemDate(float date)
01916 {
01917     NL_PS_FUNC_MAIN(CParticleSystem_setSystemDate)
01918     if (date == _SystemDate) return;
01919     _SystemDate = date;
01920     for(uint k = 0; k < _ProcessVect.size(); ++k)
01921     {
01922         _ProcessVect[k]->systemDateChanged();
01923     }
01924 }
01925 
01927 void CParticleSystem::registerSoundServer(UPSSoundServer *soundServer)
01928 {
01929     NL_PS_FUNC(CParticleSystem_registerSoundServer)
01930     if (soundServer == _SoundServer) return;
01931     if (_SoundServer)
01932     {
01933         CParticleSystemManager::stopSoundForAllManagers();
01934     }
01935     _SoundServer = soundServer;
01936     if (_SoundServer)
01937     {
01938         CParticleSystemManager::reactivateSoundForAllManagers();
01939     }
01940 }
01941 
01943 void CParticleSystem::activateEmitters(bool active)
01944 {
01945     NL_PS_FUNC_MAIN(CParticleSystem_activateEmitters)
01946     for(uint k = 0; k < getNbProcess(); ++k)
01947     {
01948         if (getProcess(k)->isLocated())
01949         {
01950             CPSLocated *loc = static_cast<CPSLocated *>(getProcess(k));
01951             if (loc)
01952             {
01953                 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
01954                 {
01955                     if (loc->getBoundObject(l)->getType() == PSEmitter)
01956                         loc->getBoundObject(l)->setActive(active);
01957                 }
01958             }
01959         }
01960     }
01961 }
01962 
01964 bool CParticleSystem::hasActiveEmitters() const
01965 {
01966     NL_PS_FUNC_MAIN(CParticleSystem_hasActiveEmitters)
01967     for(uint k = 0; k < getNbProcess(); ++k)
01968     {
01969         if (getProcess(k)->isLocated())
01970         {
01971             const CPSLocated *loc = static_cast<const CPSLocated *>(getProcess(k));
01972             if (loc)
01973             {
01974                 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
01975                 {
01976                     if (loc->getBoundObject(l)->getType() == PSEmitter)
01977                     {
01978                         if (loc->getBoundObject(l)->isActive()) return true;
01979                     }
01980                 }
01981             }
01982         }
01983     }
01984     return false;
01985 }
01986 
01988 bool CParticleSystem::hasEmittersTemplates() const
01989 {
01990     NL_PS_FUNC_MAIN(CParticleSystem_hasEmittersTemplates)
01991     for(uint k = 0; k < getNbProcess(); ++k)
01992     {
01993         if (getProcess(k)->isLocated())
01994         {
01995             const CPSLocated *loc = static_cast<const CPSLocated *>(getProcess(k));
01996             if (loc)
01997             {
01998                 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
01999                 {
02000                     if (loc->getBoundObject(l)->getType() == PSEmitter)
02001                     {
02002                         return true;
02003                     }
02004                 }
02005             }
02006         }
02007     }
02008     return false;
02009 }
02010 
02012 void CParticleSystem::matchArraySize()
02013 {
02014     NL_PS_FUNC_MAIN(CParticleSystem_matchArraySize)
02015     for(uint k = 0; k < getNbProcess(); ++k)
02016     {
02017         if (getProcess(k)->isLocated())
02018         {
02019             CPSLocated *loc = static_cast<CPSLocated *>(getProcess(k));
02020             loc->resize(loc->getSize()); // match the max size with the number of instances
02021         }
02022     }
02023 }
02024 
02026 uint CParticleSystem::getMaxNumParticles() const
02027 {
02028     NL_PS_FUNC_MAIN(CParticleSystem_getMaxNumParticles)
02029     uint numParts = 0;
02030     for(uint k = 0; k < getNbProcess(); ++k)
02031     {
02032         if (getProcess(k)->isLocated())
02033         {
02034             const CPSLocated *loc = static_cast<const CPSLocated *>(getProcess(k));
02035             if (loc)
02036             {
02037                 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
02038                 {
02039                     if (loc->getBoundObject(l)->getType() == PSParticle)
02040                     {
02041                         numParts += loc->getMaxSize();
02042                     }
02043                 }
02044             }
02045         }
02046     }
02047     return numParts;
02048 }
02049 
02051 uint CParticleSystem::getCurrNumParticles() const
02052 {
02053     NL_PS_FUNC_MAIN(CParticleSystem_getCurrNumParticles)
02054     uint numParts = 0;
02055     for(uint k = 0; k < getNbProcess(); ++k)
02056     {
02057         if (getProcess(k)->isLocated())
02058         {
02059             const CPSLocated *loc = static_cast<const CPSLocated *>(getProcess(k));
02060             if (loc)
02061             {
02062                 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
02063                 {
02064                     if (loc->getBoundObject(l)->getType() == PSParticle)
02065                     {
02066                         numParts += loc->getSize();
02067                     }
02068                 }
02069             }
02070         }
02071     }
02072     return numParts;
02073 }
02074 
02076 void CParticleSystem::getTargeters(const CPSLocated *target, std::vector<CPSTargetLocatedBindable *> &targeters)
02077 {
02078     NL_PS_FUNC_MAIN(CParticleSystem_getTargeters)
02079     nlassert(target);
02080     nlassert(isProcess(target));
02081     targeters.clear();
02082     for(uint k = 0; k < getNbProcess(); ++k)
02083     {
02084         if (getProcess(k)->isLocated())
02085         {
02086             CPSLocated *loc = static_cast<CPSLocated *>(getProcess(k));
02087             if (loc)
02088             {
02089                 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
02090                 {
02091                     CPSTargetLocatedBindable *targeter = dynamic_cast<CPSTargetLocatedBindable *>(loc->getBoundObject(l));
02092                     if (targeter)
02093                     {
02094                         for(uint m = 0; m < targeter->getNbTargets(); ++m)
02095                         {
02096                             if (targeter->getTarget(m) == target)
02097                             {
02098                                 targeters.push_back(targeter);
02099                                 break;
02100                             }
02101                         }
02102                     }
02103                 }
02104             }
02105         }
02106     }
02107 }
02108 
02110 void CParticleSystem::matrixModeChanged(CParticleSystemProcess *proc, TPSMatrixMode oldMode, TPSMatrixMode newMode)
02111 {
02112     NL_PS_FUNC_MAIN(CParticleSystem_matrixModeChanged)
02113     nlassert(proc);
02114     // check that the located belong to that system
02115     nlassert(isProcess(proc));
02116     if (oldMode != PSUserMatrix && newMode == PSUserMatrix)
02117     {
02118         addRefForUserSysCoordInfo();
02119     }
02120     else if (oldMode == PSUserMatrix && newMode != PSUserMatrix)
02121     {
02122         releaseRefForUserSysCoordInfo();
02123     }
02124 }
02125 
02127 void CParticleSystem::addRefForUserSysCoordInfo(uint numRefs)
02128 {
02129     NL_PS_FUNC_MAIN(CParticleSystem_addRefForUserSysCoordInfo)
02130     if (!numRefs) return;
02131     if (!_UserCoordSystemInfo)
02132     {
02133         _UserCoordSystemInfo = new CUserCoordSystemInfo;
02134     }
02135     nlassert(_UserCoordSystemInfo)
02136     _UserCoordSystemInfo->NumRef += numRefs;
02137 
02138 }
02139 
02141 void CParticleSystem::releaseRefForUserSysCoordInfo(uint numRefs)
02142 {
02143     NL_PS_FUNC_MAIN(CParticleSystem_releaseRefForUserSysCoordInfo)
02144     if (!numRefs) return;
02145     nlassert(_UserCoordSystemInfo);
02146     nlassert(numRefs <= _UserCoordSystemInfo->NumRef)
02147     _UserCoordSystemInfo->NumRef -= numRefs;
02148     if (_UserCoordSystemInfo->NumRef == 0)
02149     {
02150         delete _UserCoordSystemInfo;
02151         _UserCoordSystemInfo = NULL;
02152     }
02153 }
02154 
02156 void CParticleSystem::checkIntegrity()
02157 {
02158     NL_PS_FUNC_MAIN(CParticleSystem_checkIntegrity)
02159     // do some checks
02160     uint userMatrixUsageCount = 0;
02161     for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
02162     {
02163         userMatrixUsageCount += (*it)->getUserMatrixUsageCount();
02164     }
02165     if (userMatrixUsageCount == 0)
02166     {
02167         nlassert(_UserCoordSystemInfo == NULL);
02168     }
02169     else
02170     {
02171         nlassert(_UserCoordSystemInfo != NULL);
02172         nlassert(_UserCoordSystemInfo->NumRef == userMatrixUsageCount);
02173     }
02174     for(uint k = 0; k < _ProcessVect.size(); ++k)
02175     {
02176         nlassert(_ProcessVect[k]->getOwner() == this);
02177     }
02178 }
02179 
02181 void CParticleSystem::enumTexs(std::vector<NLMISC::CSmartPtr<ITexture> > &dest, IDriver &drv)
02182 {
02183     NL_PS_FUNC_MAIN(CParticleSystem_enumTexs)
02184     for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
02185     {
02186         (*it)->enumTexs(dest, drv);
02187     }
02188 }
02189 
02191 void CParticleSystem::setZBias(float value)
02192 {
02193     NL_PS_FUNC_MAIN(CParticleSystem_setZBias)
02194     for (TProcessVect::iterator it = _ProcessVect.begin(); it != _ProcessVect.end(); ++it)
02195     {
02196         (*it)->setZBias(value);
02197     }
02198 }
02199 
02201 void CParticleSystem::getSortingByEmitterPrecedence(std::vector<uint> &result) const
02202 {
02203     NL_PS_FUNC_MAIN(CParticleSystem_getSortingByEmitterPrecedence)
02204     #ifdef NL_DEBUG
02205         nlassert(!hasLoop()); // should be an acyclic graph, otherwise big problem....
02206     #endif
02207     typedef std::list<CParticleSystemProcess *> TProcessList;
02208     std::vector<TProcessList> degreeToNodes;
02209     std::vector<TProcessList::iterator> nodeToIterator(_ProcessVect.size());
02210     //
02211     std::vector<uint> inDegree(_ProcessVect.size(), 0); // degree for each node
02212     for(uint k = 0; k < _ProcessVect.size(); ++k)
02213     {
02214         if (_ProcessVect[k]->isLocated())
02215         {
02216             CPSLocated *loc = static_cast<CPSLocated *>(_ProcessVect[k]);
02217             for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
02218             {
02219                 if (loc->getBoundObject(l)->getType() == PSEmitter)
02220                 {
02221                     CPSEmitter *pEmit = NLMISC::safe_cast<CPSEmitter *>(loc->getBoundObject(l));
02222                     if (pEmit->getEmittedType())
02223                     {
02224                         ++ inDegree[getIndexOf(*pEmit->getEmittedType())];
02225                     }
02226                 }
02227             }
02228         }
02229     }
02230     // make enough room in degreeToNodes.
02231     for(uint k = 0; k < inDegree.size(); ++k)
02232     {
02233         if (degreeToNodes.size() <= inDegree[k])
02234         {
02235             degreeToNodes.resize(inDegree[k] + 1);
02236         }
02237     }
02238     // sort nodes by degree
02239     for(uint k = 0; k < inDegree.size(); ++k)
02240     {
02241         // NB : could not do resize there because we keep iterators in the list, so it's done in the previous loop
02242         degreeToNodes[inDegree[k]].push_front(_ProcessVect[k]);
02243         nodeToIterator[k] = degreeToNodes[inDegree[k]].begin();
02244     }
02245     //
02246     #ifdef  NL_DEBUG
02247         #define DO_SBEP_CHECK  \
02248         {  for(uint k = 0; k < degreeToNodes.size(); ++k)                                                            \
02249         {                                                                                                            \
02250             for(TProcessList::const_iterator it = degreeToNodes[k].begin(); it != degreeToNodes[k].end(); ++it)      \
02251             {                                                                                                        \
02252                 nlassert(inDegree[(*it)->getIndex()] == k);                                                          \
02253             }                                                                                                        \
02254         }}
02255     #else
02256         #define DO_SBEP_CHECK
02257     #endif
02258     //
02259     DO_SBEP_CHECK
02260     result.reserve(_ProcessVect.size());
02261     result.clear();
02262     if (degreeToNodes.empty()) return;
02263     // 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)
02264     while (!degreeToNodes[0].empty())
02265     {
02266         DO_SBEP_CHECK
02267         CParticleSystemProcess *pr = degreeToNodes[0].front();
02268         degreeToNodes[0].pop_front();
02269         result.push_back(getIndexOf(*pr));
02270         if (pr->isLocated())
02271         {
02272             CPSLocated *loc = static_cast<CPSLocated *>(pr);
02273             for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
02274             {
02275                 if (loc->getBoundObject(l)->getType() == PSEmitter)
02276                 {
02277                     CPSEmitter *pEmit = NLMISC::safe_cast<CPSEmitter *>(loc->getBoundObject(l));
02278                     // update degree of node
02279                     if (pEmit->getEmittedType())
02280                     {
02281                         uint emittedLocIndex  = getIndexOf(*pEmit->getEmittedType());
02282                         uint degree = inDegree[emittedLocIndex];
02283                         nlassert(degree != 0);
02284                         degreeToNodes[degree - 1].splice(degreeToNodes[degree - 1].begin(), degreeToNodes[degree], nodeToIterator[emittedLocIndex]);
02285                         nodeToIterator[emittedLocIndex] = degreeToNodes[degree - 1].begin(); // update iterator
02286                         -- inDegree[emittedLocIndex];
02287                         DO_SBEP_CHECK
02288                     }
02289                 }
02290             }
02291         }
02292     }
02293 }
02294 
02296 void CParticleSystem::updateProcessIndices()
02297 {
02298     NL_PS_FUNC_MAIN(CParticleSystem_updateProcessIndices)
02299     for(uint k = 0; k < _ProcessVect.size(); ++k)
02300     {
02301         _ProcessVect[k]->setIndex(k);
02302     }
02303 }
02304 
02305 
02307 void CParticleSystem::dumpHierarchy()
02308 {
02309     NL_PS_FUNC_MAIN(CParticleSystem_dumpHierarchy)
02310     for(uint k = 0; k < _ProcessVect.size(); ++k)
02311     {
02312         CPSLocated *loc = dynamic_cast<CPSLocated *>(_ProcessVect[k]);
02313         if (loc)
02314         {
02315             nlinfo("Located k : %s @%x", loc->getName().c_str(), (ptrdiff_t) loc);
02316             for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
02317             {
02318                 CPSEmitter *emitter = dynamic_cast<CPSEmitter *>(loc->getBoundObject(l));
02319                 if (emitter)
02320                 {
02321                     nlinfo("    emitter %s : emit %s @%x", emitter->getName().c_str(), emitter->getEmittedType() ? emitter->getEmittedType()->getName().c_str() : "none", (ptrdiff_t) emitter->getEmittedType());
02322                 }
02323             }
02324         }
02325     }
02326 }
02327 
02329 void CParticleSystem::onShow(bool shown)
02330 {
02331     NL_PS_FUNC_MAIN(CParticleSystem_onShow)
02332     for(uint k = 0; k < _ProcessVect.size(); ++k)
02333     {
02334         _ProcessVect[k]->onShow(shown);
02335     }
02336 }
02337 
02338 
02339 } // NL3D

Generated on Thu Jan 7 08:26:28 2010 for NeL by  doxygen 1.6.1