Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / particle_system_shape.cpp
blob1f48fdd25b9f7c57993959e6236c2fdc8b00689e
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) 2012 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"
22 #include "nel/3d/particle_system.h"
23 #include "nel/3d/particle_system_shape.h"
24 #include "nel/3d/particle_system_model.h"
25 #include "nel/3d/scene.h"
26 #include "nel/3d/driver.h"
27 #include "nel/3d/skeleton_model.h"
28 #include "nel/3d/texture_file.h"
29 #include "nel/3d/ps_allocator.h"
30 #include "nel/misc/file.h"
31 #include "nel/misc/mem_stream.h"
32 #include "nel/misc/hierarchical_timer.h"
33 #include "nel/misc/contiguous_block_allocator.h"
35 // tmp
36 #include "nel/3d/ps_face_look_at.h"
37 #include "nel/3d/ps_force.h"
39 #ifdef DEBUG_NEW
40 #define new DEBUG_NEW
41 #endif
43 namespace NL3D {
46 using NLMISC::CIFile;
49 namespace {
51 NLMISC::CMutex s_PSSMutex;
53 } /* anonymous namespace */
55 // private usage : macro to check the memory integrity
56 #if defined(NL_DEBUG) && defined(NL_OS_WINDOWS)
57 // #define PARTICLES_CHECK_MEM NLMEMORY::CheckHeap(true);
58 #define PARTICLES_CHECK_MEM
59 #else
60 #define PARTICLES_CHECK_MEM
61 #endif
63 // ***************************************************************************
64 // A singleton to define the TextureCategory of Particle system
65 class CPSTextureCategory
67 public:
68 static NLMISC::CSmartPtr<ITexture::CTextureCategory> &get()
70 if(!_Instance)
71 _Instance= new CPSTextureCategory();
72 return _Instance->_TextureCategory;
74 // release memory
75 static void releaseInstance()
77 if( _Instance )
78 delete _Instance;
79 _Instance = NULL;
82 private:
83 NLMISC::CSmartPtr<ITexture::CTextureCategory> _TextureCategory;
84 CPSTextureCategory()
86 _TextureCategory= new ITexture::CTextureCategory("PARTICLE SYSTEM");
88 static CPSTextureCategory *_Instance;
90 CPSTextureCategory *CPSTextureCategory::_Instance= NULL;
92 ///===========================================================================
93 void CParticleSystemShape::releaseInstance()
95 CPSTextureCategory::releaseInstance();
98 ///===========================================================================
99 CParticleSystemShape::CParticleSystemShape() : _MaxViewDist(100.f),
100 _DestroyWhenOutOfFrustum(false),
101 _DestroyModelWhenOutOfRange(false),
102 _UsePrecomputedBBox(false),
103 _Sharing(false),
104 _NumBytesWanted(0)
106 /* ***********************************************
107 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
108 * It can be loaded/called through CAsyncFileManager for instance
109 * ***********************************************/
111 for (uint k = 0; k < 4; ++k)
113 _UserParamDefaultTrack[k].setDefaultValue(0);
115 _DefaultPos.setDefaultValue(CVector::Null);
116 _DefaultScale.setDefaultValue( CVector(1, 1, 1) );
117 _DefaultRotQuat.setDefaultValue(CQuat());
118 _DefaultTriggerTrack.setDefaultValue(true); // by default, system start as soon as they are instanciated
122 ///===========================================================================
123 void CParticleSystemShape::serial(NLMISC::IStream &f)
125 /* ***********************************************
126 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
127 * It can be loaded/called through CAsyncFileManager for instance
128 * ***********************************************/
130 sint ver = f.serialVersion(6);
131 /// version 6 : added sharing flag
132 //NLMISC::CVector8 &buf = _ParticleSystemProto.bufferAsVector();
133 //f.serialCont(buf);
135 if (f.isReading ())
137 std::vector<uint8> buf;
138 f.serialCont(buf);
139 _ParticleSystemProto.fill(&buf[0], (uint32)buf.size());
141 else
143 f.serialBufferWithSize ((uint8*)_ParticleSystemProto.buffer(), _ParticleSystemProto.length());
146 if (ver > 1)
148 // serial default tracks
149 for (uint k = 0; k < 4; ++k)
151 f.serial(_UserParamDefaultTrack[k]);
154 if ( ver > 2)
156 f.serial (_DefaultPos);
157 f.serial (_DefaultScale);
158 f.serial (_DefaultRotQuat);
160 if ( ver > 3)
162 f.serial(_MaxViewDist);
163 f.serial(_DestroyWhenOutOfFrustum);
164 f.serial(_DestroyModelWhenOutOfRange);
166 if ( ver > 4)
168 f.serial(_UsePrecomputedBBox);
169 if (_UsePrecomputedBBox)
171 f.serial(_PrecomputedBBox);
174 if ( ver > 5)
176 f.serial(_Sharing);
180 ///===========================================================================
181 void CParticleSystemShape::buildFromPS(const CParticleSystem &ps)
183 // must be sure that we are writing in the stream
184 if (_ParticleSystemProto.isReading())
186 _ParticleSystemProto.invert();
189 // to have const correctness in the prototype, we must do this...
190 CParticleSystem *myPs = const_cast<CParticleSystem *>(&ps);
191 nlassert(myPs);
192 // build the prototype
193 _ParticleSystemProto.serialPtr(myPs);
195 // mirror some system values
196 _MaxViewDist = myPs->getMaxViewDist();
197 _DestroyWhenOutOfFrustum = myPs->doesDestroyWhenOutOfFrustum();
198 _DestroyModelWhenOutOfRange = myPs->getDestroyModelWhenOutOfRange();
199 if (!myPs->getAutoComputeBBox())
201 _UsePrecomputedBBox = true;
202 myPs->computeBBox(_PrecomputedBBox);
204 else
206 _UsePrecomputedBBox = false;
208 _Sharing = myPs->isSharingEnabled();
211 ///===========================================================================
212 void CParticleSystemShape::getAABBox(NLMISC::CAABBox &bbox) const
214 if (!_UsePrecomputedBBox)
216 bbox.setCenter(NLMISC::CVector::Null);
217 bbox.setHalfSize(NLMISC::CVector(1, 1, 1));
219 else
221 bbox = _PrecomputedBBox;
226 ///===========================================================================
227 CParticleSystem *CParticleSystemShape::instanciatePS(CScene &scene, NLMISC::CContiguousBlockAllocator *blockAllocator /*= NULL*/)
229 if (_Sharing && _SharedSystem != NULL) // is sharing enabled, and is a system already instanciated
231 return _SharedSystem;
234 // avoid prb with concurrent thread (may happen if an instance group containing ps is loaded in background)
235 s_PSSMutex.enter();
238 #ifdef PS_FAST_ALLOC
239 nlassert(PSBlockAllocator == NULL);
240 if (blockAllocator)
242 // set new allocator for particle system memory
243 PSBlockAllocator = blockAllocator;
244 blockAllocator->init(_NumBytesWanted); // if size wanted is already known, set it
246 #endif
248 //NLMISC::TTicks start = NLMISC::CTime::getPerformanceTime();
249 // copy the datas
250 CParticleSystem *myInstance = NULL;
252 // serialize from the memory stream
253 if (!_ParticleSystemProto.isReading()) // we must be sure that we are reading the stream
255 _ParticleSystemProto.invert();
258 _ParticleSystemProto.resetPtrTable();
259 _ParticleSystemProto.seek(0, NLMISC::IStream::begin);
261 // NLMISC::TTicks start = NLMISC::CTime::getPerformanceTime();
262 _ParticleSystemProto.serialPtr(myInstance); // instanciate the system
263 /* NLMISC::TTicks end = NLMISC::CTime::getPerformanceTime();
264 nlinfo("instanciation time = %.2f", (float) (1000 * NLMISC::CTime::ticksToSecond(end - start)));
267 myInstance->setScene(&scene);
269 if (_CachedTex.empty() && scene.getDriver())
271 //nlinfo("flushing texs");
272 // load && cache textures
273 myInstance->enumTexs(_CachedTex, *scene.getDriver());
274 for(uint k = 0; k < _CachedTex.size(); ++k)
276 if (_CachedTex[k])
278 _CachedTex[k]->setTextureCategory(CPSTextureCategory::get());
279 scene.getDriver()->setupTexture (*(ITexture *)_CachedTex[k]);
283 else
286 for(uint k = 0; k < _CachedTex.size(); ++k)
288 nlinfo(_CachedTex[k]->getShareName().c_str());
293 // tmp
295 if (_Sharing)
297 _SharedSystem = myInstance; // set this as the first shared instance
300 #ifdef PS_FAST_ALLOC
301 if (blockAllocator)
303 _NumBytesWanted = blockAllocator->getNumAllocatedBytes(); // now we know the number of wanted bytes, subsequent alloc can be much faster
304 PSBlockAllocator = NULL;
306 #endif
308 s_PSSMutex.leave();
310 /*NLMISC::TTicks end = NLMISC::CTime::getPerformanceTime();
311 nlinfo("instanciation time = %.2f", (float) (1000 * NLMISC::CTime::ticksToSecond(end - start))); */
312 return myInstance;
315 ///===========================================================================
316 CTransformShape *CParticleSystemShape::createInstance(CScene &scene)
318 CParticleSystemModel *psm = NLMISC::safe_cast<CParticleSystemModel *>(scene.createModel(NL3D::ParticleSystemModelId) );
319 psm->Shape = this;
320 psm->_Scene = &scene; // the model needs the scene to recreate the particle system he holds
321 // by default, we don't instanciate the system. It will be instanciated only if visible and triggered
322 // psm->_ParticleSystem = instanciatePS(scene);
324 // Setup position with the default value
325 psm->ITransformable::setPos( _DefaultPos.getDefaultValue() );
326 psm->ITransformable::setRotQuat( _DefaultRotQuat.getDefaultValue() );
327 psm->ITransformable::setScale( _DefaultScale.getDefaultValue() );
329 // ParticleSystems are added to the "Fx" Load Balancing Group.
330 psm->setLoadBalancingGroup("Fx");
332 return psm;
335 ///===========================================================================
336 void CParticleSystemShape::render(IDriver *drv, CTransformShape *trans, bool passOpaque)
338 H_AUTO ( NL3D_Particles_Render );
339 nlassert(drv);
340 CParticleSystemModel *psm = NLMISC::safe_cast<CParticleSystemModel *>(trans);
341 if (psm->_Invalidated) return;
342 CParticleSystem *ps = psm->getPS();
343 /// has the system been triggered yet ?
344 if (!ps) return;
346 TAnimationTime delay = psm->getEllapsedTime();
347 nlassert(ps->getScene());
349 ///////////////////////
350 // render particles //
351 ///////////////////////
353 /// if sharing is enabled, we should resetup the system matrix
354 if (ps->isSharingEnabled())
356 ps->setSysMat(&(psm->getWorldMatrix()));
357 ps->setUserMatrix(&(psm->getUserMatrix()));
360 // Setup the matrix.
361 /// drv->setupModelMatrix(trans->getWorldMatrix());
363 ps->setDriver(drv);
364 // draw particle
365 PARTICLES_CHECK_MEM;
366 if (passOpaque)
368 PSLookAtRenderTime = 0;
369 //NLMISC::TTicks start = NLMISC::CTime::getPerformanceTime();
370 ps->step(CParticleSystem::SolidRender, delay, *this, *psm);
371 /*NLMISC::TTicks end = NLMISC::CTime::getPerformanceTime();
372 nlinfo("Solid render time time = %.2f", (float) (1000 * NLMISC::CTime::ticksToSecond(end - start)));
373 nlinfo("LookAt Render time = %.2f", (float) (1000 * NLMISC::CTime::ticksToSecond(PSLookAtRenderTime))); */
375 else
377 //PSLookAtRenderTime = 0;
378 //NLMISC::TTicks start = NLMISC::CTime::getPerformanceTime();
379 ps->step(CParticleSystem::BlendRender, delay, *this, *psm);
380 /*NLMISC::TTicks end = NLMISC::CTime::getPerformanceTime();
381 nlinfo("Blend render time time = %.2f", (float) (1000 * NLMISC::CTime::ticksToSecond(end - start)));
382 nlinfo("LookAt Render time = %.2f", (float) (1000 * NLMISC::CTime::ticksToSecond(PSLookAtRenderTime))); */
384 PARTICLES_CHECK_MEM;
385 if (psm->isToolDisplayEnabled())
387 ps->step(CParticleSystem::ToolRender, delay, *this, *psm);
388 PARTICLES_CHECK_MEM;
392 ///===========================================================================
393 void CParticleSystemShape::flushTextures(IDriver &driver, uint selectedTexture)
395 // if textures are already flushed, no-op
396 if (!_CachedTex.empty()) return;
397 if (_SharedSystem)
399 _SharedSystem->enumTexs(_CachedTex, driver);
401 else
403 s_PSSMutex.enter();
405 // must create an instance just to flush the textures
406 CParticleSystem *myInstance = NULL;
408 #ifdef PS_FAST_ALLOC
409 nlassert(PSBlockAllocator == NULL);
410 NLMISC::CContiguousBlockAllocator blockAllocator;
411 PSBlockAllocator = &blockAllocator;
412 blockAllocator.init(300000); // we release memory just after, and we don't want to fragment the memory, so provide large enough mem
413 #endif
414 // serialize from the memory stream
415 if (!_ParticleSystemProto.isReading()) // we must be sure that we are reading the stream
417 _ParticleSystemProto.invert();
419 _ParticleSystemProto.resetPtrTable();
420 _ParticleSystemProto.seek(0, NLMISC::IStream::begin);
421 _ParticleSystemProto.serialPtr(myInstance); // instanciate the system
422 #ifdef PS_FAST_ALLOC
423 _NumBytesWanted = blockAllocator.getNumAllocatedBytes(); // next allocation will be fast because we know how much memory to allocate
424 #endif
425 myInstance->enumTexs(_CachedTex, driver);
426 // tmp
428 #ifdef NL_DEBUG
429 for(uint k = 0; k < myInstance->getNbProcess(); ++k)
431 CPSLocated *loc = (CPSLocated *) myInstance->getProcess(k);
432 for(uint l = 0; l < loc->getNbBoundObjects(); ++l)
434 if (dynamic_cast<CPSCentralGravity *>(loc->getBoundObject(l)))
436 nlwarning("PS %s uses central gravity", myInstance->getName().c_str());
437 break;
441 #endif */
442 // sort the process inside the fx
443 myInstance->getSortingByEmitterPrecedence(_ProcessOrder);
444 delete myInstance;
445 #ifdef PS_FAST_ALLOC
446 PSBlockAllocator = NULL;
447 #endif
448 s_PSSMutex.leave();
450 for(uint k = 0; k < _CachedTex.size(); ++k)
452 //nlinfo(_CachedTex[k]->getShareName().c_str());
453 if (_CachedTex[k])
455 _CachedTex[k]->setTextureCategory(CPSTextureCategory::get());
456 driver.setupTexture(*_CachedTex[k]);
462 } // NL3D