Merge branch '164-crash-on-patching-and-possibly-right-after-login' into main/gingo...
[ryzomcore.git] / ryzom / client / src / ground_fx_manager.cpp
blob41e6895a557cee32f24bcc0e6ec9ea2b3cb592f5
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
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>
7 //
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/>.
23 #include "stdpch.h"
24 #include "ground_fx_manager.h"
25 #include "pacs_client.h"
26 #include "sheet_manager.h"
27 #include "misc.h"
28 #include "entities.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"
42 #ifdef NL_DEBUG
43 #define DEBUG_FX
44 #endif
46 #ifdef DEBUG_FX
47 #define CHECK_INTEGRITY checkIntegrity();
48 #else
49 #define CHECK_INTEGRITY
50 #endif
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() :
68 _MinSpeed(1.5f),
69 _MaxSpeed(6.f),
70 _SpeedWaterWalkFast(3.f),
71 _SpeedWaterSwimFast(2.f),
72 _MaxDist(50.f),
73 _MaxNumFX(10),
74 _NumFX(0),
75 _MaxNumCachedFX(10),
76 _NumCachedFX(0),
77 _NumInstances(0),
78 _Scene(NULL)
80 H_AUTO_USE(RZ_GroundFXManager)
81 // Construct
84 // *****************************************************************************
85 CGroundFXManager::~CGroundFXManager()
87 H_AUTO_USE(RZ_GroundFXManager)
88 reset();
89 CHECK_INTEGRITY
92 // *****************************************************************************
93 void CGroundFXManager::reset()
95 H_AUTO_USE(RZ_GroundFXManager)
96 CHECK_INTEGRITY
97 if (_Scene)
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);
120 _ActiveFXs.clear();
121 _InactiveFXs.clear();
122 _CachedFXs.clear();
123 _InstancesList.clear();
124 _SortedInstances.clear();
125 _NumFX = 0;
126 _NumCachedFX = 0;
127 _NumInstances = 0;
128 _Scene = NULL;
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
136 _Scene = scene;
137 _MaxDist = maxDist;
138 _MaxNumFX = maxNumFX;
139 _MaxNumCachedFX = fxCacheSize;
142 // *****************************************************************************
143 CGroundFXManager::TEntityHandle CGroundFXManager::add(CEntityCL *entity)
145 H_AUTO_USE(RZ_GroundFXManager)
146 CHECK_INTEGRITY
147 nlassert(entity);
148 #ifdef NL_DEBUG
149 for(TInstanceList::iterator it = _InstancesList.begin(); it != _InstancesList.end(); ++it)
151 if (it->Entity == entity)
153 nlerror("entity added twice"); //
156 #endif
157 _InstancesList.push_front(CInstance());
158 CInstance &i = _InstancesList.front();
159 i.Entity = entity;
160 i.HasFX = false;
161 i.EmittersActive = false;
162 i.EmittersUnderWaterActive = false;
163 i.InstanciateDelay = 2;
164 ++ _NumInstances;
165 CHECK_INTEGRITY
166 return _InstancesList.begin();
169 // *****************************************************************************
170 void CGroundFXManager::remove(TEntityHandle handle)
172 H_AUTO_USE(RZ_GroundFXManager)
173 CHECK_INTEGRITY
174 if (handle->HasFX)
176 // shutdown fx
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);
190 -- _NumFX;
191 handle->EmittersActive = false;
192 handle->EmittersUnderWaterActive = false;
194 _InstancesList.erase(handle);
195 nlassert(_NumInstances != 0);
196 -- _NumInstances;
197 CHECK_INTEGRITY
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)
216 fxName.clear();
217 if (!Entity) return;
218 const CEntitySheet *es;
219 es = SheetMngr.get(Entity->sheetId());
220 if (!es) return;
221 const std::vector<CGroundFXSheet> *gfx = Entity->getGroundFX();
222 if (!gfx) return;
223 CGroundFXSheet tmp;
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;
263 if (present)
265 // put in inactive list
266 _InactiveFXs.splice(_InactiveFXs.begin(), _ActiveFXs, instance->FXHandle);
267 nlassert(_NumFX > 0);
268 -- _NumFX;
270 else
272 // directly put in cache without (fx already shut down)
273 moveFXInCache(_ActiveFXs, instance->FXHandle);
274 -- _NumFX;
279 // *****************************************************************************
280 void CGroundFXManager::moveFXInCache(TGroundFXList &ownerList, TGroundFXHandle fx)
282 H_AUTO_USE(RZ_GroundFXManager)
283 if (!fx->FX.empty()) fx->FX.hide();
284 // cache full ?
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();
293 else
295 ++ _NumCachedFX;
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)
313 if (!it->FX.empty())
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)
341 if (!_Scene) return;
342 CHECK_INTEGRITY
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();)
347 nextIt = currIt;
348 ++ nextIt;
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);
362 currIt = nextIt;
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;
375 continue;
377 if (instanceIt->Entity->getLastClip())
379 // entity is clipped
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);
389 else
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);
401 else
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();
410 if (cont)
412 bool splashEnabled;
413 if (cont->WaterMap.getWaterHeight(CVector2f((float) entityPos.x, (float) entityPos.y), instanceIt->WaterHeight, splashEnabled))
415 if (splashEnabled)
417 // put in candidate list, even if not moving
418 _SortedInstances.push_back(instanceIt);
420 else
422 // there's water, but with no splashs
423 instanceIt->Mode = CInstance::Ground;
426 else
428 // water height not retrieved ? -> set ground mode
429 instanceIt->Mode = CInstance::Ground;
433 else
435 // check if entity walking in water
436 CContinent *cont = ContinentMngr.cur();
437 if (cont)
439 bool splashEnabled;
440 if (cont->WaterMap.getWaterHeight(CVector2f((float) entityPos.x, (float) entityPos.y), instanceIt->WaterHeight, splashEnabled))
442 if (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)
461 // put in sort list
462 _SortedInstances.push_back(instanceIt);
464 else
466 if (instanceIt->HasFX)
468 invalidateFX(instanceIt);
475 CHECK_INTEGRITY
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()));
479 uint k;
480 for(k = 0; k < numValidInstances; ++k)
482 std::string fxName;
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;
499 break;
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";
507 else
509 fxName = "StepSwim.ps";
510 fxNameUnderWater = "StepSwimUnderWater.ps";
512 fxZ = _SortedInstances[k]->WaterHeight;
513 break;
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;
520 break;
521 default:
522 nlassert(0);
523 break;
525 if (!fxName.empty())
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)
534 // name is the same
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;
552 // underwater fx
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;
570 else
572 // name has changed
573 // put in inactive list
574 invalidateFX(_SortedInstances[k]);
575 if (!fxName.empty())
577 // must create a new fx
578 createNewFx = true;
582 else
584 if (!fxName.empty())
586 // must create a new fx
587 createNewFx = true;
591 if (createNewFx)
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);
607 ++ _NumFX;
608 _SortedInstances[k]->HasFX = true;
609 nlassert(!_SortedInstances[k]->EmittersActive);
610 nlassert(!_SortedInstances[k]->EmittersUnderWaterActive);
611 _SortedInstances[k]->FXHandle = _ActiveFXs.begin();
612 instanciate = false;
613 break;
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);
632 -- _NumCachedFX;
633 ++ _NumFX;
635 _SortedInstances[k]->HasFX = true;
636 nlassert(!_SortedInstances[k]->EmittersActive);
637 nlassert(!_SortedInstances[k]->EmittersUnderWaterActive);
638 _SortedInstances[k]->FXHandle = _ActiveFXs.begin();
639 instanciate = false;
640 break;
646 if (instanciate)
648 // must create new fx
649 _ActiveFXs.push_front(CGroundFX());
650 ++ _NumFX;
651 CGroundFX &gfx = _ActiveFXs.front();
652 gfx.FX = NULL;
653 gfx.FXUnderWater = NULL;
654 NL3D::UInstance fxInstance = _Scene->createInstance(fxName);
655 if (!fxInstance.empty())
657 gfx.FX.cast (fxInstance);
658 if (gfx.FX.empty())
660 // not a particle system instance
661 _Scene->deleteInstance(fxInstance);
663 else
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);
683 else
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();
694 if (!gfx.FX.empty())
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)
723 float intensity;
724 if (_MinSpeed == _MaxSpeed)
726 intensity = 0.f;
728 else
730 intensity = (float) ((speed - _MinSpeed) / (_MaxSpeed - _MinSpeed));
732 clamp(intensity, 0.f, 1.f);
733 _SortedInstances[k]->FXHandle->FX.setUserParam(0, intensity);
736 else
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]);
752 CHECK_INTEGRITY
755 //////////////////////////////////
756 // tmp tmp : test of ground fxs //
757 //////////////////////////////////
758 #if !FINAL_VERSION
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)
785 if (MoveAll)
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();
792 else
794 Entities[k].Move = false;
803 // ***********************************************************************************************
804 void CTestGroundFX::displayFXBoxes() const
806 H_AUTO_USE(RZ_GroundFXManager)
807 Driver->setViewMatrix(Scene->getCam().getMatrix().inverted());
808 NL3D::CFrustum fr;
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);
817 float size = 0.4f;
818 float textSize = 2.5f;
819 // display fx to add
820 for(uint k = 0; k < TestGroundFX.Entities.size(); ++k)
822 NLMISC::CMatrix mat;
823 mat.identity();
824 CVector pos = (CVector) TestGroundFX.Entities[k].Entity->pos();
825 mat.setPos(pos);
826 Driver->setModelMatrix(mat);
827 drawBox(CVector(- size, - size, - size),
828 CVector(size, size, size),
829 CRGBA::Magenta);
830 mat.identity();
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 // *******************************************************************************************
875 // temp, for debug
877 // add an entity for test
878 NLMISC_COMMAND(gfxAdd, "gfxAdd", "<>")
880 uint slot;
881 for(slot = 0; slot < EntitiesMngr.nbEntitiesAllocated(); ++slot)
883 if (EntitiesMngr.entity(slot) == NULL)
885 break;
888 sint category = 0;
889 uint32 form = 0;
890 if (args.size() == 1)
892 if (fromString(args[0], category))
894 switch(category)
896 case 1:
898 NLMISC::CSheetId sheet("dag_for_lvl_01.creature");
899 form = sheet.asInt();
901 break;
902 default:
904 NLMISC::CSheetId sheet("fyros.race_stats");
905 form = sheet.asInt();
907 break;
910 else
912 NLMISC::CSheetId sheet(args[0]);
913 form = sheet.asInt();
917 TNewEntityInfo emptyEntityInfo;
918 emptyEntityInfo.reset();
919 CEntityCL *entity = EntitiesMngr.create(slot, form, emptyEntityInfo);
920 if(entity)
922 sint64 *prop = 0;
923 CCDBNodeLeaf *node = 0;
925 CInterfaceManager *IM = CInterfaceManager::getInstance();
926 // Set Position
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();
930 if(node)
932 sint64 x = (sint64)(pos.x * 1000.0);
933 sint64 y = (sint64)(pos.y * 1000.0);
934 sint64 z = (sint64)(pos.z * 1000.0);
935 node->setValue64(x);
936 node = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+NLMISC::toString("%d", slot)+":P"+NLMISC::toString("%d", CLFECOMMON::PROPERTY_POSY), false);
937 if(node)
939 node->setValue64(y);
940 node = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+NLMISC::toString("%d", slot)+":P"+NLMISC::toString("%d", CLFECOMMON::PROPERTY_POSZ), false);
941 if(node)
943 node->setValue64(z);
944 EntitiesMngr.updateVisualProperty(0, slot, CLFECOMMON::PROPERTY_POSITION);
948 // Set Direction
949 entity->front(UserEntity->front());
950 entity->dir(UserEntity->front());
951 // Set Mode
952 node = NLGUI::CDBManager::getInstance()->getDbProp("SERVER:Entities:E"+NLMISC::toString("%d", slot)+":P"+NLMISC::toString("%d", CLFECOMMON::PROPERTY_MODE), false);
953 if(node)
955 MBEHAV::EMode m = MBEHAV::NORMAL;
956 prop = (sint64 *)&m;
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;
973 // Set The Database
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);
980 // Apply Changes.
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);
993 return true;
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();
1005 return true;
1008 // move one entity
1009 NLMISC_COMMAND(gfxMove, "gfxMove", "<>")
1011 if (args.size() != 1) return false;
1012 uint index;
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;
1019 return true;
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;
1032 return 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;
1043 return true;
1046 #endif