1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2014-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "ground_fx_manager.h"
25 #include "pacs_client.h"
26 #include "sheet_manager.h"
29 #include "user_entity.h"
30 #include "time_client.h"
31 #include "nel/3d/u_particle_system_instance.h"
32 #include "nel/3d/u_text_context.h"
33 #include "nel/3d/u_scene.h"
34 #include "nel/3d/u_camera.h"
35 #include "nel/misc/vector.h"
36 #include "client_sheets/race_stats_sheet.h"
37 #include "client_sheets/character_sheet.h"
38 #include "client_sheets/ground_fx_sheet.h"
39 #include "continent_manager.h"
40 #include "network_connection.h"
47 #define CHECK_INTEGRITY checkIntegrity();
49 #define CHECK_INTEGRITY
53 H_AUTO_DECL(RZ_GroundFXManager
)
55 extern NL3D::UTextContext
*TextContext
;
56 extern NL3D::UDriver
*Driver
;
57 extern NL3D::UScene
*Scene
;
58 extern CContinentManager ContinentMngr
;
61 // max dist to reuse an old fx
62 static const float MAX_DIST_TO_REUSE_OLD_FX
= 1.5f
;
64 using namespace NLMISC
;
66 // *****************************************************************************
67 CGroundFXManager::CGroundFXManager() :
70 _SpeedWaterWalkFast(3.f
),
71 _SpeedWaterSwimFast(2.f
),
80 H_AUTO_USE(RZ_GroundFXManager
)
84 // *****************************************************************************
85 CGroundFXManager::~CGroundFXManager()
87 H_AUTO_USE(RZ_GroundFXManager
)
92 // *****************************************************************************
93 void CGroundFXManager::reset()
95 H_AUTO_USE(RZ_GroundFXManager
)
99 TGroundFXList::iterator it
;
100 // release all active FXs
101 for(it
= _ActiveFXs
.begin(); it
!= _ActiveFXs
.end(); ++it
)
103 if (!it
->FX
.empty()) _Scene
->deleteInstance(it
->FX
);
104 if (!it
->FXUnderWater
.empty()) _Scene
->deleteInstance(it
->FXUnderWater
);
106 // release all inactive FXs
107 for(it
= _InactiveFXs
.begin(); it
!= _InactiveFXs
.end(); ++it
)
109 if (!it
->FX
.empty()) _Scene
->deleteInstance(it
->FX
);
110 if (!it
->FXUnderWater
.empty()) _Scene
->deleteInstance(it
->FXUnderWater
);
113 // release all cached FXs
114 for(it
= _CachedFXs
.begin(); it
!= _CachedFXs
.end(); ++it
)
116 if (!it
->FX
.empty()) _Scene
->deleteInstance(it
->FX
);
117 if (!it
->FXUnderWater
.empty()) _Scene
->deleteInstance(it
->FXUnderWater
);
121 _InactiveFXs
.clear();
123 _InstancesList
.clear();
124 _SortedInstances
.clear();
131 // *****************************************************************************
132 void CGroundFXManager::init(NL3D::UScene
*scene
, float maxDist
, uint maxNumFX
, uint fxCacheSize
)
134 H_AUTO_USE(RZ_GroundFXManager
)
135 nlassert(_Scene
== NULL
); // init already called
138 _MaxNumFX
= maxNumFX
;
139 _MaxNumCachedFX
= fxCacheSize
;
142 // *****************************************************************************
143 CGroundFXManager::TEntityHandle
CGroundFXManager::add(CEntityCL
*entity
)
145 H_AUTO_USE(RZ_GroundFXManager
)
149 for(TInstanceList::iterator it
= _InstancesList
.begin(); it
!= _InstancesList
.end(); ++it
)
151 if (it
->Entity
== entity
)
153 nlerror("entity added twice"); //
157 _InstancesList
.push_front(CInstance());
158 CInstance
&i
= _InstancesList
.front();
161 i
.EmittersActive
= false;
162 i
.EmittersUnderWaterActive
= false;
163 i
.InstanciateDelay
= 2;
166 return _InstancesList
.begin();
169 // *****************************************************************************
170 void CGroundFXManager::remove(TEntityHandle handle
)
172 H_AUTO_USE(RZ_GroundFXManager
)
177 if (!handle
->FXHandle
->FX
.empty())
179 handle
->FXHandle
->FX
.activateEmitters(false);
180 handle
->FXHandle
->FX
.stopSound();
182 if (!handle
->FXHandle
->FXUnderWater
.empty())
184 handle
->FXHandle
->FXUnderWater
.activateEmitters(false);
185 handle
->FXHandle
->FXUnderWater
.stopSound();
187 // put in inactive list
188 _InactiveFXs
.splice(_InactiveFXs
.begin(), _ActiveFXs
, handle
->FXHandle
);
189 nlassert(_NumFX
> 0);
191 handle
->EmittersActive
= false;
192 handle
->EmittersUnderWaterActive
= false;
194 _InstancesList
.erase(handle
);
195 nlassert(_NumInstances
!= 0);
200 // predicate to test a ground id
202 // *****************************************************************************
203 // Predicate for binary search in a vector of sorted ground fx sheets
204 struct CCmpGroundIDPred
206 inline bool operator()(const CGroundFXSheet
&lhs
, const CGroundFXSheet
&rhs
) const
208 return lhs
.GroundID
< rhs
.GroundID
;
212 // *****************************************************************************
213 void CGroundFXManager::CInstance::getFXNameFromGroundType(uint32 groundID
, std::string
&fxName
) const
215 H_AUTO_USE(RZ_GroundFXManager
)
218 const CEntitySheet
*es
;
219 es
= SheetMngr
.get(Entity
->sheetId());
221 const std::vector
<CGroundFXSheet
> *gfx
= Entity
->getGroundFX();
224 tmp
.GroundID
= groundID
;
225 std::vector
<CGroundFXSheet
>::const_iterator it
= std::lower_bound(gfx
->begin(), gfx
->end(), tmp
, CCmpGroundIDPred());
226 if (it
==gfx
->end()) return;
227 if (it
->GroundID
!= groundID
) return;
228 fxName
= it
->getFXName();
231 // *****************************************************************************
232 /** Predicate to sort instances by distances.
234 struct CSortInstancePred
236 bool operator()(CGroundFXManager::TEntityHandle lhs
, CGroundFXManager::TEntityHandle rhs
) const
238 return lhs
->Dist2
< rhs
->Dist2
;
242 // *****************************************************************************
243 void CGroundFXManager::invalidateFX(TEntityHandle instance
)
245 H_AUTO_USE(RZ_GroundFXManager
)
246 bool present
= false;
247 if (!instance
->FXHandle
->FX
.empty())
249 present
= instance
->FXHandle
->FX
.isSystemPresent();
250 instance
->FXHandle
->FX
.activateEmitters(false);
251 instance
->FXHandle
->FX
.stopSound();
253 if (!instance
->FXHandle
->FXUnderWater
.empty())
255 present
&= instance
->FXHandle
->FXUnderWater
.isSystemPresent();
256 instance
->FXHandle
->FXUnderWater
.activateEmitters(false);
257 instance
->FXHandle
->FXUnderWater
.stopSound();
260 instance
->EmittersActive
= false;
261 instance
->EmittersUnderWaterActive
= false;
262 instance
->HasFX
= false;
265 // put in inactive list
266 _InactiveFXs
.splice(_InactiveFXs
.begin(), _ActiveFXs
, instance
->FXHandle
);
267 nlassert(_NumFX
> 0);
272 // directly put in cache without (fx already shut down)
273 moveFXInCache(_ActiveFXs
, instance
->FXHandle
);
279 // *****************************************************************************
280 void CGroundFXManager::moveFXInCache(TGroundFXList
&ownerList
, TGroundFXHandle fx
)
282 H_AUTO_USE(RZ_GroundFXManager
)
283 if (!fx
->FX
.empty()) fx
->FX
.hide();
285 nlassert(_NumCachedFX
<= _MaxNumCachedFX
);
286 if (_NumCachedFX
== _MaxNumCachedFX
)
288 // remove least recently added fx
289 if (!_CachedFXs
.back().FX
.empty()) _Scene
->deleteInstance(_CachedFXs
.back().FX
);
290 if (!_CachedFXs
.back().FXUnderWater
.empty()) _Scene
->deleteInstance(_CachedFXs
.back().FXUnderWater
);
291 _CachedFXs
.pop_back();
297 // move in cache front
298 _CachedFXs
.splice(_CachedFXs
.begin(), ownerList
, fx
);
302 // *******************************************************************************************
303 void CGroundFXManager::checkIntegrity()
305 H_AUTO_USE(RZ_GroundFXManager
)
306 for(TGroundFXList::iterator it
= _InactiveFXs
.begin(); it
!= _InactiveFXs
.end(); ++it
)
308 if (!it
->FX
.empty()) nlassert(!it
->FX
.hasActiveEmitters());
309 if (!it
->FXUnderWater
.empty()) nlassert(!it
->FXUnderWater
.hasActiveEmitters());
311 for(TGroundFXList::iterator it
= _CachedFXs
.begin(); it
!= _CachedFXs
.end(); ++it
)
315 nlassert(!it
->FX
.hasActiveEmitters());
316 nlassert(!it
->FX
.hasParticles());
318 if (!it
->FXUnderWater
.empty())
320 nlassert(!it
->FXUnderWater
.hasActiveEmitters());
321 nlassert(!it
->FXUnderWater
.hasParticles());
322 if (it
->FXUnderWater
.hasParticles())
324 nlinfo(it
->FXName
.c_str());
328 uint numFX
= (uint
)_ActiveFXs
.size();
329 nlassert(numFX
== _NumFX
);
330 nlassert(numFX
<= _MaxNumFX
);
331 uint numCachedFX
= (uint
)_CachedFXs
.size();
332 nlassert(numCachedFX
== _NumCachedFX
);
333 nlassert(numCachedFX
<= _MaxNumCachedFX
);
337 // *****************************************************************************
338 void CGroundFXManager::update(const NLMISC::CVectorD
&camPos
)
340 H_AUTO_USE(RZ_GroundFXManager
)
343 // check all inactive fxs, and move in cache those that have no more particles
344 TGroundFXList::iterator currIt
, nextIt
;
345 for(TGroundFXList::iterator currIt
= _InactiveFXs
.begin(); currIt
!= _InactiveFXs
.end();)
349 bool somethingVisible
= false;
350 if (!currIt
->FX
.empty() && currIt
->FX
.isSystemPresent() && currIt
->FX
.hasParticles())
352 somethingVisible
= true;
354 if (!currIt
->FXUnderWater
.empty() && currIt
->FXUnderWater
.isSystemPresent() && currIt
->FXUnderWater
.hasParticles())
356 somethingVisible
= true;
358 if (!somethingVisible
)
360 moveFXInCache(_InactiveFXs
, currIt
);
364 float maxDist2
= _MaxDist
* _MaxDist
;
366 _SortedInstances
.clear();
367 _SortedInstances
.reserve(_NumInstances
);
368 // check all candidates entities
369 for(TInstanceList::iterator instanceIt
= _InstancesList
.begin(); instanceIt
!= _InstancesList
.end(); ++instanceIt
)
371 const NLMISC::CVectorD
&entityPos
= instanceIt
->Entity
->pos();
372 if (instanceIt
->InstanciateDelay
!= 0)
374 -- instanceIt
->InstanciateDelay
;
377 if (instanceIt
->Entity
->getLastClip())
380 // if a fx was attached, put it in the inactive list
381 // When the entity is not visible, we can't update its ground fx :
382 // As a matter of fact, snapToGround is not called at each frame for entities that are not visible, so this causes a
383 // very noticeable shift on z for the ground fx when the entity enters the view pyramid.
384 if (instanceIt
->HasFX
)
386 invalidateFX(instanceIt
);
391 // entity is not clipped
392 instanceIt
->Dist2
= (float) (entityPos
- camPos
).sqrnorm();
393 if (instanceIt
->Dist2
> maxDist2
)
395 // entity not eligible for fx (too far), remove any attached fx
396 if (instanceIt
->HasFX
)
398 invalidateFX(instanceIt
);
403 instanceIt
->Mode
= CInstance::Ground
;
404 // if entity is in water, fx can be generated even if the entity hasn't moved
405 if (instanceIt
->Entity
->mode() == MBEHAV::SWIM
|| instanceIt
->Entity
->mode() == MBEHAV::MOUNT_SWIM
)
407 instanceIt
->Mode
= CInstance::Swim
;
408 // retrieve water height
409 CContinent
*cont
= ContinentMngr
.cur();
413 if (cont
->WaterMap
.getWaterHeight(CVector2f((float) entityPos
.x
, (float) entityPos
.y
), instanceIt
->WaterHeight
, splashEnabled
))
417 // put in candidate list, even if not moving
418 _SortedInstances
.push_back(instanceIt
);
422 // there's water, but with no splashs
423 instanceIt
->Mode
= CInstance::Ground
;
428 // water height not retrieved ? -> set ground mode
429 instanceIt
->Mode
= CInstance::Ground
;
435 // check if entity walking in water
436 CContinent
*cont
= ContinentMngr
.cur();
440 if (cont
->WaterMap
.getWaterHeight(CVector2f((float) entityPos
.x
, (float) entityPos
.y
), instanceIt
->WaterHeight
, splashEnabled
))
444 // see if feet are underwater
445 if (entityPos
.z
< instanceIt
->WaterHeight
)
447 instanceIt
->Mode
= CInstance::Water
;
448 // put in candidate list, even if not moving
449 _SortedInstances
.push_back(instanceIt
);
456 if (instanceIt
->Mode
== CInstance::Ground
)
458 // entity not in water
459 if (instanceIt
->Entity
->getSpeed() >= _MinSpeed
)
462 _SortedInstances
.push_back(instanceIt
);
466 if (instanceIt
->HasFX
)
468 invalidateFX(instanceIt
);
476 // sort instances by distance (number of chosen instances is likely to be a lot less than 200, so quick sort is ok)
477 std::sort(_SortedInstances
.begin(), _SortedInstances
.end(), CSortInstancePred());
478 uint numValidInstances
= std::min(_MaxNumFX
, uint(_SortedInstances
.size()));
480 for(k
= 0; k
< numValidInstances
; ++k
)
483 std::string stdFXName
; // standardized name for retrieval in cache
484 std::string fxNameUnderWater
;
485 bool createNewFx
= false;
487 float fxZ
; // z at which the instance must be put
488 static float fxZBias
= 0.2f
;
490 double speed
= _SortedInstances
[k
]->Entity
->getSpeed();
492 switch(_SortedInstances
[k
]->Mode
)
494 case CInstance::Water
:
495 if (speed
== 0.f
) fxName
= "StepSwimIdle.ps";
496 else if (speed
> _SpeedWaterWalkFast
) fxName
= "StepSwimRun.ps";
497 else fxName
= "StepSwimWalk.ps";
498 fxZ
= _SortedInstances
[k
]->WaterHeight
;
500 case CInstance::Swim
:
501 if (speed
== 0.f
) fxName
= "StepSwimIdle.ps";
502 else if (speed
> _SpeedWaterSwimFast
)
504 fxName
= "StepSwimSpeed.ps";
505 fxNameUnderWater
= "StepSwimSpeedUnderWater.ps";
509 fxName
= "StepSwim.ps";
510 fxNameUnderWater
= "StepSwimUnderWater.ps";
512 fxZ
= _SortedInstances
[k
]->WaterHeight
;
514 case CInstance::Ground
:
516 uint32 groundId
= (uint32
) _SortedInstances
[k
]->Entity
->getGroundType();
517 _SortedInstances
[k
]->getFXNameFromGroundType(groundId
, fxName
);
518 fxZ
= (float) _SortedInstances
[k
]->Entity
->pos().z
;
527 stdFXName
= NLMISC::toLowerAscii(NLMISC::CFile::getFilenameWithoutExtension(fxName
));
529 // is an fx already attached to the entity ?
530 if (_SortedInstances
[k
]->HasFX
)
532 if (_SortedInstances
[k
]->FXHandle
->FXName
== stdFXName
)
535 // ok, activate emitters
536 if (!_SortedInstances
[k
]->EmittersActive
)
538 // NB : we dont activate emitters has soon as the fx is allocated by an entity, because of the way the fx works.
539 // As a matter of fact, if an object move from A to B, the fx may spawn several particles on [AB] in a single frame
540 // So if the FX was previously used by another entity, a trail of particles may appear between the 2 entities when
541 // fx is deallocated from first entity and allocated by the new one
542 if (!_SortedInstances
[k
]->FXHandle
->FX
.empty())
544 if (_SortedInstances
[k
]->FXHandle
->FX
.isSystemPresent())
546 _SortedInstances
[k
]->FXHandle
->FX
.activateEmitters(true);
547 _SortedInstances
[k
]->FXHandle
->FX
.reactivateSound();
548 _SortedInstances
[k
]->EmittersActive
= true;
553 if (!_SortedInstances
[k
]->EmittersUnderWaterActive
)
555 // NB : we dont activate emitters has soon as the fx is allocated by an entity, because of the way the fx works.
556 // As a matter of fact, if an object move from A to B, the fx may spawn several particles on [AB] in a single frame
557 // So if the FX was previously used by another entity, a trail of particles may appear between the 2 entities when
558 // fx is deallocated from first entity and allocated by the new one
559 if (!_SortedInstances
[k
]->FXHandle
->FXUnderWater
.empty())
561 if (_SortedInstances
[k
]->FXHandle
->FXUnderWater
.isSystemPresent())
563 _SortedInstances
[k
]->FXHandle
->FXUnderWater
.activateEmitters(true);
564 _SortedInstances
[k
]->FXHandle
->FXUnderWater
.reactivateSound();
565 _SortedInstances
[k
]->EmittersUnderWaterActive
= true;
573 // put in inactive list
574 invalidateFX(_SortedInstances
[k
]);
577 // must create a new fx
586 // must create a new fx
593 nlassert(!fxName
.empty());
594 bool instanciate
= true; // should we instanciate a new fx ?
595 if (!_InactiveFXs
.empty())
597 // simple linear search should suffice
598 for(TGroundFXList::iterator it
= _InactiveFXs
.begin(); it
!= _InactiveFXs
.end(); ++it
)
600 if (it
->FXName
== stdFXName
)
602 // fx must be not too far because of transparancy sorting issues
603 if ((_SortedInstances
[k
]->Entity
->pos() - it
->FX
.getMatrix().getPos()).sqrnorm() < MAX_DIST_TO_REUSE_OLD_FX
* MAX_DIST_TO_REUSE_OLD_FX
)
605 // put fx in active list
606 _ActiveFXs
.splice(_ActiveFXs
.begin(), _InactiveFXs
, it
);
608 _SortedInstances
[k
]->HasFX
= true;
609 nlassert(!_SortedInstances
[k
]->EmittersActive
);
610 nlassert(!_SortedInstances
[k
]->EmittersUnderWaterActive
);
611 _SortedInstances
[k
]->FXHandle
= _ActiveFXs
.begin();
619 // Look for a matching fx in the cache.
620 // We can't reuse inactive fx (those that are shuttingdown), because we wait for all particles to disappear, so that the bbox is null
621 if (instanciate
&& !_CachedFXs
.empty())
623 for(TGroundFXList::iterator it
= _CachedFXs
.begin(); it
!= _CachedFXs
.end(); ++it
)
625 if (it
->FXName
== stdFXName
)
628 if (!it
->FX
.empty()) it
->FX
.show();
629 // put fx in active list
630 _ActiveFXs
.splice(_ActiveFXs
.begin(), _CachedFXs
, it
);
631 nlassert(_NumCachedFX
> 0);
635 _SortedInstances
[k
]->HasFX
= true;
636 nlassert(!_SortedInstances
[k
]->EmittersActive
);
637 nlassert(!_SortedInstances
[k
]->EmittersUnderWaterActive
);
638 _SortedInstances
[k
]->FXHandle
= _ActiveFXs
.begin();
648 // must create new fx
649 _ActiveFXs
.push_front(CGroundFX());
651 CGroundFX
&gfx
= _ActiveFXs
.front();
653 gfx
.FXUnderWater
= NULL
;
654 NL3D::UInstance fxInstance
= _Scene
->createInstance(fxName
);
655 if (!fxInstance
.empty())
657 gfx
.FX
.cast (fxInstance
);
660 // not a particle system instance
661 _Scene
->deleteInstance(fxInstance
);
665 if (_SortedInstances
[k
]->Mode
!= CInstance::Ground
)
667 // add z-bias for fx in water
668 gfx
.FX
.setZBias(-fxZBias
);
672 if (!fxNameUnderWater
.empty())
674 fxInstance
= _Scene
->createInstance(fxNameUnderWater
);
675 if (!fxInstance
.empty())
677 gfx
.FXUnderWater
.cast (fxInstance
);
678 if (gfx
.FXUnderWater
.empty())
680 // not a particle system instance
681 _Scene
->deleteInstance(fxInstance
);
685 gfx
.FXUnderWater
.setZBias(fxZBias
);
689 gfx
.FXName
= stdFXName
;
690 _SortedInstances
[k
]->HasFX
= true;
691 nlassert(!_SortedInstances
[k
]->EmittersActive
);
692 nlassert(!_SortedInstances
[k
]->EmittersUnderWaterActive
);
693 _SortedInstances
[k
]->FXHandle
= _ActiveFXs
.begin();
696 gfx
.FX
.setTransformMode(NL3D::UTransform::DirectMatrix
);
697 gfx
.FX
.setOrderingLayer(2);
699 if (!gfx
.FXUnderWater
.empty())
701 gfx
.FXUnderWater
.setTransformMode(NL3D::UTransform::DirectMatrix
);
702 gfx
.FXUnderWater
.setOrderingLayer(0);
706 // update Pos & matrix
707 if (_SortedInstances
[k
]->HasFX
&& !_SortedInstances
[k
]->FXHandle
->FX
.empty())
709 NLMISC::CMatrix mat
= _SortedInstances
[k
]->Entity
->dirMatrix();
710 const NLMISC::CVector
&front
= _SortedInstances
[k
]->Entity
->front();
711 mat
.setRot(CVector::K
^ front
, - front
, CVector::K
);
712 const NLMISC::CVector
&entityPos
= _SortedInstances
[k
]->Entity
->pos();
713 mat
.setPos(NLMISC::CVector(entityPos
.x
, entityPos
.y
, (float) fxZ
));
714 _SortedInstances
[k
]->FXHandle
->FX
.setMatrix(mat
);
715 _SortedInstances
[k
]->FXHandle
->FX
.setClusterSystem(_SortedInstances
[k
]->Entity
->getClusterSystem());
716 if (!_SortedInstances
[k
]->FXHandle
->FXUnderWater
.empty())
718 _SortedInstances
[k
]->FXHandle
->FXUnderWater
.setMatrix(mat
);
719 _SortedInstances
[k
]->FXHandle
->FXUnderWater
.setClusterSystem(_SortedInstances
[k
]->Entity
->getClusterSystem());
721 if (_SortedInstances
[k
]->Mode
== CInstance::Ground
)
724 if (_MinSpeed
== _MaxSpeed
)
730 intensity
= (float) ((speed
- _MinSpeed
) / (_MaxSpeed
- _MinSpeed
));
732 clamp(intensity
, 0.f
, 1.f
);
733 _SortedInstances
[k
]->FXHandle
->FX
.setUserParam(0, intensity
);
738 // adjust z-bias (tmp)
739 if (_SortedInstances[k]->FXHandle->FXUnderWater) _SortedInstances[k]->FXHandle->FXUnderWater.setZBias(-fxZBias);
740 if (_SortedInstances[k]->FXHandle->FX) _SortedInstances[k]->FXHandle->FX.setZBias(fxZBias);
744 // remove fx for instances that are too far / not taken in account
745 for (; k
< _SortedInstances
.size(); ++k
)
747 if (_SortedInstances
[k
]->HasFX
)
749 invalidateFX(_SortedInstances
[k
]);
755 //////////////////////////////////
756 // tmp tmp : test of ground fxs //
757 //////////////////////////////////
760 #include "interface_v3/interface_manager.h"
762 using NLMISC::toString
;
764 CTestGroundFX TestGroundFX
;
766 // ***********************************************************************************************
767 void CTestGroundFX::update()
769 H_AUTO_USE(RZ_GroundFXManager
)
770 for(uint k
= 0; k
< Entities
.size(); ++k
)
772 if (MoveAll
|| Entities
[k
].Move
)
774 Entities
[k
].Entity
->snapToGround();
775 NLMISC::CVectorD newPos
= NLMISC::CVectorD(DT
* Entities
[k
].Dir
) + Entities
[k
].Entity
->pos();
776 NLMISC::CVectorD startToPos
= newPos
- NLMISC::CVectorD(Entities
[k
].StartPos
);
777 if (startToPos
.norm() > 10.0)
779 newPos
= NLMISC::CVectorD(Entities
[k
].StartPos
) + 10.0 * startToPos
.normed();
781 Entities
[k
].Entity
->pacsPos(newPos
);
782 Entities
[k
].Duration
-= DT
;
783 if (Entities
[k
].Duration
<= 0.f
)
787 Entities
[k
].Move
= true;
788 Entities
[k
].Duration
= 5.f
;
789 Entities
[k
].Dir
.set(NLMISC::frand(1.f
), NLMISC::frand(1.f
), 0.f
);
790 Entities
[k
].Dir
.normalize();
794 Entities
[k
].Move
= false;
803 // ***********************************************************************************************
804 void CTestGroundFX::displayFXBoxes() const
806 H_AUTO_USE(RZ_GroundFXManager
)
807 Driver
->setViewMatrix(Scene
->getCam().getMatrix().inverted());
809 Scene
->getCam().getFrustum(fr
.Left
, fr
.Right
, fr
.Bottom
, fr
.Top
, fr
.Near
, fr
.Far
);
810 fr
.Perspective
= true;
811 Driver
->setFrustum(fr
);
812 TextContext
->setColor(CRGBA::Green
);
813 TextContext
->setShaded(false);
814 TextContext
->setShadeOutline(false);
815 TextContext
->setFontSize(12);
818 float textSize
= 2.5f
;
820 for(uint k
= 0; k
< TestGroundFX
.Entities
.size(); ++k
)
824 CVector pos
= (CVector
) TestGroundFX
.Entities
[k
].Entity
->pos();
826 Driver
->setModelMatrix(mat
);
827 drawBox(CVector(- size
, - size
, - size
),
828 CVector(size
, size
, size
),
831 mat
.setRot(Scene
->getCam().getRotQuat());
832 mat
.setPos(pos
+ 5.f
* size
* CVector::K
);
833 CVector distVect
= pos
- Scene
->getCam().getPos();
834 mat
.scale(textSize
* distVect
.norm());
835 TextContext
->render3D(mat
, NLMISC::toString("gfx %d, dist = %.1f", (int) k
, (pos
- Scene
->getCam().getMatrix().getPos()).norm()));
840 // *******************************************************************************************
841 void CGroundFXManager::setMinSpeed(float minSpeed
)
843 H_AUTO_USE(RZ_GroundFXManager
)
844 nlassert(minSpeed
>= 0.f
);
845 _MinSpeed
= minSpeed
;
848 // *******************************************************************************************
849 void CGroundFXManager::setMaxSpeed(float maxSpeed
)
851 H_AUTO_USE(RZ_GroundFXManager
)
852 nlassert(maxSpeed
>= 0.f
);
853 _MaxSpeed
= maxSpeed
;
856 // *******************************************************************************************
857 void CGroundFXManager::setSpeedWaterWalkFast(float speed
)
859 H_AUTO_USE(RZ_GroundFXManager
)
860 nlassert(speed
>= 0.f
);
861 _SpeedWaterWalkFast
= speed
;
864 // *******************************************************************************************
865 void CGroundFXManager::setSpeedWaterSwimFast(float speed
)
867 H_AUTO_USE(RZ_GroundFXManager
)
868 nlassert(speed
>= 0.f
);
869 _SpeedWaterSwimFast
= speed
;
874 // *******************************************************************************************
877 // add an entity for test
878 NLMISC_COMMAND(gfxAdd
, "gfxAdd", "<>")
881 for(slot
= 0; slot
< EntitiesMngr
.nbEntitiesAllocated(); ++slot
)
883 if (EntitiesMngr
.entity(slot
) == NULL
)
890 if (args
.size() == 1)
892 if (fromString(args
[0], category
))
898 NLMISC::CSheetId
sheet("dag_for_lvl_01.creature");
899 form
= sheet
.asInt();
904 NLMISC::CSheetId
sheet("fyros.race_stats");
905 form
= sheet
.asInt();
912 NLMISC::CSheetId
sheet(args
[0]);
913 form
= sheet
.asInt();
917 TNewEntityInfo emptyEntityInfo
;
918 emptyEntityInfo
.reset();
919 CEntityCL
*entity
= EntitiesMngr
.create(slot
, form
, emptyEntityInfo
);
923 CCDBNodeLeaf
*node
= 0;
925 CInterfaceManager
*IM
= CInterfaceManager::getInstance();
927 node
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+NLMISC::toString("%d", slot
)+":P"+NLMISC::toString("%d", CLFECOMMON::PROPERTY_POSX
), false);
928 NLMISC::CVectorD pos
;
929 pos
= UserEntity
->pos() + 2.f
* UserEntity
->front();
932 sint64 x
= (sint64
)(pos
.x
* 1000.0);
933 sint64 y
= (sint64
)(pos
.y
* 1000.0);
934 sint64 z
= (sint64
)(pos
.z
* 1000.0);
936 node
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+NLMISC::toString("%d", slot
)+":P"+NLMISC::toString("%d", CLFECOMMON::PROPERTY_POSY
), false);
940 node
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+NLMISC::toString("%d", slot
)+":P"+NLMISC::toString("%d", CLFECOMMON::PROPERTY_POSZ
), false);
944 EntitiesMngr
.updateVisualProperty(0, slot
, CLFECOMMON::PROPERTY_POSITION
);
949 entity
->front(UserEntity
->front());
950 entity
->dir(UserEntity
->front());
952 node
= NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+NLMISC::toString("%d", slot
)+":P"+NLMISC::toString("%d", CLFECOMMON::PROPERTY_MODE
), false);
955 MBEHAV::EMode m
= MBEHAV::NORMAL
;
957 node
->setValue64(*prop
);
958 EntitiesMngr
.updateVisualProperty(0, slot
, CLFECOMMON::PROPERTY_MODE
);
960 // Set Visual Properties
961 if(dynamic_cast<CPlayerCL
*>(entity
))
963 SPropVisualA visualA
;
964 visualA
.PropertySubData
.Sex
= ClientCfg
.Sex
;
965 SPropVisualB visualB
;
966 // Initialize the Visual Property C (Default parameters).
967 SPropVisualC visualC
;
968 visualC
.PropertySubData
.CharacterHeight
= 7;
969 visualC
.PropertySubData
.ArmsWidth
= 7;
970 visualC
.PropertySubData
.LegsWidth
= 7;
971 visualC
.PropertySubData
.TorsoWidth
= 7;
972 visualC
.PropertySubData
.BreastSize
= 7;
974 prop
= (sint64
*)&visualB
;
975 NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+NLMISC::toString("%d", slot
)+":P"+NLMISC::toString("%d", CLFECOMMON::PROPERTY_VPB
))->setValue64(*prop
);
976 prop
= (sint64
*)&visualC
;
977 NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+NLMISC::toString("%d", slot
)+":P"+NLMISC::toString("%d", CLFECOMMON::PROPERTY_VPC
))->setValue64(*prop
);
978 prop
= (sint64
*)&visualA
;
979 NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+NLMISC::toString("%d", slot
)+":P"+NLMISC::toString("%d", CLFECOMMON::PROPERTY_VPA
))->setValue64(*prop
);
981 EntitiesMngr
.updateVisualProperty(0, slot
, CLFECOMMON::PROPERTY_VPA
);
983 TestGroundFX
.Entities
.push_back(CTestGroundFX::CEntity());
984 TestGroundFX
.Entities
.back().Entity
= entity
;
985 TestGroundFX
.Entities
.back().Slot
= slot
;
986 TestGroundFX
.Entities
.back().Move
= false;
987 TestGroundFX
.Entities
.back().Dir
.set(0.f
, 0.f
, 0.f
);
988 TestGroundFX
.Entities
.back().Duration
= 0;
989 TestGroundFX
.Entities
.back().StartPos
= pos
;
991 entity
->pacsPos(pos
);
997 // remove all test entities
998 NLMISC_COMMAND(gfxReset
, "gfxReset", "<>")
1000 for(uint k
= 0; k
< TestGroundFX
.Entities
.size(); ++k
)
1002 EntitiesMngr
.remove(TestGroundFX
.Entities
[k
].Slot
, false);
1004 TestGroundFX
.Entities
.clear();
1009 NLMISC_COMMAND(gfxMove
, "gfxMove", "<>")
1011 if (args
.size() != 1) return false;
1013 fromString(args
[0], index
);
1014 if (index
> TestGroundFX
.Entities
.size()) return false;
1015 TestGroundFX
.Entities
[index
].Move
= true;
1016 TestGroundFX
.Entities
[index
].Dir
.set(NLMISC::frand(1.f
), NLMISC::frand(1.f
), 0.f
);
1017 TestGroundFX
.Entities
[index
].Dir
.normalize();
1018 TestGroundFX
.Entities
[index
].Duration
= 5.f
;
1022 // move all entities
1023 NLMISC_COMMAND(gfxMoveAll
, "gfxMoveAll", "<>")
1025 for(uint k
= 0; k
< TestGroundFX
.Entities
.size(); ++k
)
1027 TestGroundFX
.Entities
[k
].Dir
.set(NLMISC::frand(1.f
), NLMISC::frand(1.f
), 0.f
);
1028 TestGroundFX
.Entities
[k
].Dir
.normalize();
1029 TestGroundFX
.Entities
[k
].Duration
= 5.f
;
1031 TestGroundFX
.MoveAll
= true;
1035 // stop all entities
1036 NLMISC_COMMAND(gfxStopAll
, "gfxStopAll", "<>")
1038 for(uint k
= 0; k
< TestGroundFX
.Entities
.size(); ++k
)
1040 TestGroundFX
.Entities
[k
].Move
= false;
1042 TestGroundFX
.MoveAll
= false;