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) 2014-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
23 #include "timed_fx_manager.h"
24 #include "ig_callback.h"
25 #include "client_sheets/plant_sheet.h"
26 #include "nel/3d/u_transform.h"
27 #include "nel/3d/u_particle_system_instance.h"
28 #include "nel/3d/u_driver.h"
29 #include "nel/3d/u_text_context.h"
30 #include "nel/3d/u_scene.h"
31 #include "nel/3d/u_camera.h"
32 #include "nel/misc/fast_floor.h"
34 #include "fx_manager.h"
35 #include "nel/misc/check_fpu.h"
42 using namespace std::rel_ops
;
43 using namespace NLMISC
;
45 extern NL3D::UTextContext
*TextContext
;
46 extern NL3D::UDriver
*Driver
;
47 extern NL3D::UScene
*Scene
;
55 #error Flag DEBUG_FX can only be used in debug mode
60 #define CHECK_INTEGRITY checkIntegrity();
62 #define CHECK_INTEGRITY
65 CTimedFXManager
&CTimedFXManager::getInstance()
68 static CTimedFXManager manager
;
72 const float DEFAULT_SPAWNED_FX_TIMED_OUT
= 100.f
;
74 H_AUTO_DECL(RZ_TimedFX
)
77 // *******************************************************************************************
78 NLMISC::CMatrix
CTimedFX::getInstanceMatrix() const
81 H_AUTO_USE(RZ_TimedFX
)
82 NLMISC::CMatrix result
;
84 result
.translate(SpawnPosition
);
85 if (FXSheet
&& FXSheet
->InheritRot
) result
.rotate(Rot
);
86 if (FXSheet
&& FXSheet
->InheritScale
) result
.scale(Scale
);
91 // *******************************************************************************************
92 CTimedFXManager::CTimedFXManager()
95 H_AUTO_USE(RZ_TimedFX
)
99 _InstanciatedFXs
= NULL
;
100 _CandidateFXListTouched
= false;
102 _MaxNumberOfFXInstances
= 0;
105 // *******************************************************************************************
106 CTimedFXManager::~CTimedFXManager()
109 H_AUTO_USE(RZ_TimedFX
)
113 // *******************************************************************************************
114 void CTimedFXManager::init(NL3D::UScene
*scene
,
115 const CClientDate
&startDate
,
116 float /* dayLength */,
117 float noiseFrequency
,
118 uint maxNumberOfFXInstances
,
119 float sortDistanceInterval
,
123 H_AUTO_USE(RZ_TimedFX
)
124 nlassert(!_InitDone
);
127 nlwarning("Scene is NULL");
130 _CurrDate
= startDate
;
131 _Noise
.Frequency
= noiseFrequency
;
136 nlassert(maxDist
> 0.f
);
137 nlassert(sortDistanceInterval
> 0.f
);
138 _CandidateFXListSortedByDist
.resize((int) ceilf(maxDist
/ sortDistanceInterval
));
139 _CandidateFXListSortedByDistTmp
.resize((int) ceilf(maxDist
/ sortDistanceInterval
));
140 _MaxNumberOfFXInstances
= maxNumberOfFXInstances
;
141 _SortDistance
= sortDistanceInterval
;
145 // *******************************************************************************************
146 void CTimedFXManager::reset()
149 H_AUTO_USE(RZ_TimedFX
)
154 while (!_FXGroups
.empty())
156 remove(_FXGroups
.begin());
159 _CurrDate
= CClientDate();
165 // *******************************************************************************************
166 void CTimedFXManager::setDate(const CClientDate
&date
)
169 H_AUTO_USE(RZ_TimedFX
)
173 // *******************************************************************************************
174 CTimedFXManager::TFXGroupHandle
CTimedFXManager::add(const std::vector
<CTimedFX
> &fxs
, EGSPD::CSeason::TSeason
/* season */)
177 H_AUTO_USE(RZ_TimedFX
)
180 _FXGroups
.push_front(CManagedFXGroup());
182 _FXGroups
.front().Season
= season
;
184 TManagedFXGroup
&newGroup
= _FXGroups
.begin()->Group
;
185 newGroup
.resize(fxs
.size());
186 for(uint k
= 0; k
< fxs
.size(); ++k
)
188 CManagedFX
&fi
= newGroup
[k
];
189 fi
.FXSheet
= fxs
[k
].FXSheet
;
190 fi
.SpawnPosition
= fxs
[k
].SpawnPosition
;
191 fi
.Scale
= fxs
[k
].Scale
;
195 fi
.FromIG
= fxs
[k
].FromIG
;
198 fi
.OwnerGroup
= &(_FXGroups
.front());
200 // compute start and end hour to see in which list insertion should occurs
201 bool instanciate
= false;
202 CClientDate startDate
;
205 if (!(fi
.FXSheet
&& fi
.FXSheet
->Mode
== CSeasonFXSheet::AlwaysStarted
))
207 if (fi
.FXSheet
&& fi
.FXSheet
->Mode
== CSeasonFXSheet::Spawn
)
209 // compute next spawn date
210 float cycleLength
= fi
.FXSheet
? fi
.FXSheet
->CycleDuration
: _DayLength
;
211 sint32 cycle
= dateToCycle(_CurrDate
, cycleLength
, _DayLength
);
212 fi
.computeStartHour(cycle
- 1, startDate
, cycleLength
, _DayLength
, _Noise
);
213 if (startDate
< _CurrDate
)
215 fi
.computeStartHour(cycle
, startDate
, cycleLength
, _DayLength
, _Noise
);
216 if (startDate
< _CurrDate
)
218 fi
.computeStartHour(cycle
+ 1, startDate
, cycleLength
, _DayLength
, _Noise
);
224 // the system is not always instanciated, so must check if it currently is.
225 float cycleLength
= fi
.FXSheet
? fi
.FXSheet
->CycleDuration
: _DayLength
;
226 sint32 cycle
= dateToCycle(_CurrDate
, cycleLength
, _DayLength
);
227 fi
.computeStartHour(cycle
, startDate
, cycleLength
, _DayLength
, _Noise
);
228 //debugDay = _CurrDate.Day;
229 if (startDate
> _CurrDate
)
231 // Let see if it did not start the previous cycle and then ended that cycle
232 fi
.computeEndHour(cycle
- 1, endDate
, cycleLength
, _DayLength
, _Noise
);
233 if (endDate
> _CurrDate
)
235 CClientDate prevStartDate
;
236 fi
.computeStartHour(cycle
- 1, prevStartDate
, cycleLength
, _DayLength
, _Noise
);
237 if (prevStartDate
<= _CurrDate
)
243 startDate
= prevStartDate
;
244 //debugDay = _CurrDate.Day - 1;
250 fi
.computeEndHour(cycle
, endDate
, cycleLength
, _DayLength
, _Noise
);
251 if (endDate
> _CurrDate
)
253 // the fx is currently running
258 fi
.computeStartHour(_CurrDate
.Day
+ 1, endDate
, cycleLength
, _DayLength
, _Noise
);
259 // the fx will start only during the next day
260 //debugDay = _CurrDate.Day + 1;
267 if (fi
.FXSheet
&& fi
.FXSheet
->Mode
== CSeasonFXSheet::AlwaysStarted
)
269 fi
.State
= CManagedFX::Permanent
;
275 if (!_Scene
|| !fi
.FXSheet
)
281 // insert in candidate fx list
282 float dist
= (fi
.SpawnPosition
- _LastCamPos
).norm();
283 linkCandidateFX(_CandidateFXListSortedByDist
, dist
, &fi
);
284 _CandidateFXListTouched
= true;
286 // Add the system to the queue of systems to be removed, only if it is not always started
287 if (!(fi
.FXSheet
&& fi
.FXSheet
->Mode
== CSeasonFXSheet::AlwaysStarted
))
289 // As the system is currenlty instanciated, we won't deals with it again until it shutdowns, so, we save it in the priority queue.
292 //tsf.DebugDay = 666;
293 tsf
.FX
= &fi
; // yes, we keep a pointer on a vector element, but we will never grow that vector
294 fi
.SetHandle
= _FXToRemove
.insert(tsf
).first
;
295 fi
.State
= CManagedFX::InRemoveList
;
300 // The system is not instanciated yet, but will be later, so add to the queue of systems that "will be instanciated later"
302 tsf
.Date
= startDate
;
303 tsf
.FX
= &fi
; // yes, we keep a pointer on a vector element, but we will never grow that vector
304 //tsf.DebugDay = debugDay;
305 nlassert(fi
.State
!= CManagedFX::InAddList
);
306 fi
.SetHandle
= _FXToAdd
.insert(tsf
).first
;
307 fi
.State
= CManagedFX::InAddList
;
309 nlinfo("==== added fx %s, start day = %day, start hour = %f, pos=(%f, %f, %f)", fi
.FXSheet
? fi
.FXSheet
->FXName
.c_str() : "???", (int) startDate
.Day
, startDate
.Hour
,
310 fi
.SpawnPosition
.x
, fi
.SpawnPosition
.y
, fi
.SpawnPosition
.z
);
315 return _FXGroups
.begin();
320 // *******************************************************************************************
321 void CTimedFXManager::remove(TFXGroupHandle handle
)
324 H_AUTO_USE(RZ_TimedFX
)
327 TManagedFXGroup
&group
= handle
->Group
;
328 for(uint k
= 0; k
< group
.size(); ++k
)
330 CManagedFX
&fi
= group
[k
];
331 if (!fi
.Instance
.empty())
334 _Scene
->deleteInstance(fi
.Instance
);
338 case CManagedFX::InAddList
: _FXToAdd
.erase(fi
.SetHandle
); break;
339 case CManagedFX::InRemoveList
: _FXToRemove
.erase(fi
.SetHandle
); break;
342 fi
.unlinkFromCandidateFXList();
343 fi
.unlinkFromInstanciatedFXList();
345 _FXGroups
.erase(handle
);
350 // *******************************************************************************************
351 void CTimedFXManager::update(const CClientDate
&date
, EGSPD::CSeason::TSeason
/* season */, const NLMISC::CVector
&camPos
)
354 H_AUTO_USE(RZ_TimedFX
)
365 // check for instanciated fxs that should be removed
366 while(!_FXToRemove
.empty())
369 const CTimeStampedFX
&tsf
= *_FXToRemove
.begin();
371 nlassert(tsf
.FX
->OwnerGroup
);
372 //nlassert(tsf.FX->OwnerGroup->Season == season);
374 nlassert(tsf
.FX
->State
== CManagedFX::InRemoveList
);
375 if (tsf
.Date
> date
) break; // all following fx will be remove in the future
376 // removes from candidate & instanciated list (this causes fx to be shutdown at the end of the pass)
377 tsf
.FX
->unlinkFromCandidateFXList();
378 _CandidateFXListTouched
= true;
380 // compute new activation date, and insert in _FXToAdd queue
381 CTimeStampedFX newTsf
;
383 float cycleLength
= newTsf
.FX
->FXSheet
? newTsf
.FX
->FXSheet
->CycleDuration
: _DayLength
;
384 sint32 cycle
= dateToCycle(tsf
.Date
, cycleLength
, _DayLength
);
385 tsf
.FX
->computeStartHour(cycle
, newTsf
.Date
, cycleLength
, _DayLength
, _Noise
);
386 if (newTsf
.Date
<= tsf
.Date
)
388 // already started for that day, so compute start hour for next day.
389 tsf
.FX
->computeStartHour(cycle
+ 1, newTsf
.Date
, cycleLength
, _DayLength
, _Noise
);
391 tsf
.FX
->SetHandle
= _FXToAdd
.insert(newTsf
).first
;
392 tsf
.FX
->State
= CManagedFX::InAddList
;
393 // remove from current queue
394 _FXToRemove
.erase(_FXToRemove
.begin());
397 // check for fxs that should be instanciated
398 while (!_FXToAdd
.empty())
401 const CTimeStampedFX
&tsf
= *_FXToAdd
.begin();
403 nlassert(tsf
.FX
->OwnerGroup
);
404 //nlassert(tsf.FX->OwnerGroup->Season == season);
406 nlassert(tsf
.FX
->State
== CManagedFX::InAddList
);
407 if (tsf
.Date
>= date
) break; // all following fx will be instancied in the future
408 // activate current fx
409 if (!_Scene
|| !tsf
.FX
->FXSheet
)
411 tsf
.FX
->Instance
= NULL
;
415 // insert in candidate fx list
416 float dist
= (tsf
.FX
->SpawnPosition
- _LastCamPos
).norm();
417 linkCandidateFX(_CandidateFXListSortedByDist
, dist
, tsf
.FX
);
418 _CandidateFXListTouched
= true;
420 // compute next shutdown date
422 nlinfo("===== compute end date for %s, debug day = %d", tsf
.FX
->FXSheet
? tsf
.FX
->FXSheet
->FXName
.c_str() : "???", /*tsf.DebugDay*/ 666);
424 CTimeStampedFX newTsf
;
426 if (tsf
.FX
->FXSheet
&& tsf
.FX
->FXSheet
->Mode
== CSeasonFXSheet::Spawn
)
428 // for a spawned fx, it is shutdown as soon the frame after it is created, so we add it to the remove list with the current date
434 nlinfo("start day = %d, start hour = %f", (int) tsf
.Date
.Day
, tsf
.Date
.Hour
);
436 float cycleLength
= tsf
.FX
->FXSheet
? tsf
.FX
->FXSheet
->CycleDuration
: _DayLength
;
437 sint32 cycle
= dateToCycle(tsf
.Date
, cycleLength
, _DayLength
);
438 tsf
.FX
->computeEndHour(cycle
- 1, newTsf
.Date
, cycleLength
, _DayLength
, _Noise
);
440 nlinfo("try 0 : end day = %d, end hour = %f", (int) newTsf
.Date
.Day
, newTsf
.Date
.Hour
);
442 if (newTsf
.Date
< tsf
.Date
)
444 tsf
.FX
->computeEndHour(cycle
, newTsf
.Date
, cycleLength
, _DayLength
, _Noise
);
446 nlinfo("try 1 : end day = %d, end hour = %f", (int) newTsf
.Date
.Day
, newTsf
.Date
.Hour
);
448 if (newTsf
.Date
< tsf
.Date
)
450 tsf
.FX
->computeEndHour(cycle
+ 1, newTsf
.Date
, cycleLength
, _DayLength
, _Noise
);
452 nlinfo("try 2 : end day = %d, end hour = %f", (int) newTsf
.Date
.Day
, newTsf
.Date
.Hour
);
457 newTsf
.FX
->SetHandle
= _FXToRemove
.insert(newTsf
).first
;
458 newTsf
.FX
->State
= CManagedFX::InRemoveList
;
459 // remove from current queue
460 _FXToAdd
.erase(_FXToAdd
.begin());
467 // if cam pos has moved too much, must completely resort fx by distance
468 if ((_CurrDate
.Day
== 0 && _CurrDate
.Hour
== 0.f
) ||(camPos
- _LastCamPos
).sqrnorm() > _SortDistance
* _SortDistance
)
470 _LastCamPos
= camPos
;
471 updateCandidateFXListSorting();
472 _CandidateFXListTouched
= true;
474 // update instanciated fxs
475 updateInstanciatedFXList();
479 // *******************************************************************************************
480 void CTimedFXManager::shutDown(TFXGroupHandle handle
)
483 H_AUTO_USE(RZ_TimedFX
)
486 TManagedFXGroup
&fxGroup
= handle
->Group
;
487 // remove all fxs of that group from both queues, and shutdown those that are instanciated
488 for(uint k
= 0; k
< fxGroup
.size(); ++k
)
490 fxGroup
[k
].shutDown(_Scene
, _FXManager
);
497 // *******************************************************************************************
498 void CTimedFXManager::CManagedFX::unlinkFromCandidateFXList()
501 H_AUTO_USE(RZ_TimedFX
)
502 if (_PrevCandidateFX
)
504 *_PrevCandidateFX
= _NextCandidateFX
;
505 if (_NextCandidateFX
)
507 _NextCandidateFX
->_PrevCandidateFX
= _PrevCandidateFX
;
509 _NextCandidateFX
= NULL
;
510 _PrevCandidateFX
= NULL
;
514 // *******************************************************************************************
515 void CTimedFXManager::CManagedFX::unlinkFromInstanciatedFXList()
518 H_AUTO_USE(RZ_TimedFX
)
519 if (_PrevInstanciatedFX
)
521 *_PrevInstanciatedFX
= _NextInstanciatedFX
;
522 if (_NextInstanciatedFX
)
524 _NextInstanciatedFX
->_PrevInstanciatedFX
= _PrevInstanciatedFX
;
526 _NextInstanciatedFX
= NULL
;
527 _PrevInstanciatedFX
= NULL
;
531 // *******************************************************************************************
532 void CTimedFXManager::CManagedFX::shutDown(NL3D::UScene
*scene
, CFXManager
&fxManager
)
535 H_AUTO_USE(RZ_TimedFX
)
536 if (!Instance
.empty())
538 // should not be already shutting down
539 // if the fx is spaned, it will end by itself, so we just add it to the shutdown list
540 if (FXSheet
&& FXSheet
->Mode
== CSeasonFXSheet::Spawn
)
542 // delegate to fx manager
543 fxManager
.addFX(Instance
, DEFAULT_SPAWNED_FX_TIMED_OUT
);
547 // fx isn't spwaned, so must tell fx to stop its emitters
548 if (Instance
.isSystemPresent() && Instance
.isValid())
550 if (!Instance
.removeByID(NELID("main")) && !Instance
.removeByID(NELID("STOP")))
552 // if a specific emitter has not be tagged, just stop all emitters if there's no better solution
553 Instance
.activateEmitters(false);
555 fxManager
.addFX(Instance
, FX_MANAGER_DEFAULT_TIMEOUT
, true);
559 // the system is not present yet -> delete it immediatly
560 if (scene
) scene
->deleteInstance(Instance
);
567 // *******************************************************************************************
568 void CTimedFXManager::cycleToDate(sint32 cycle
, float hour
, float cycleLength
, float dayLength
, CClientDate
&result
)
571 H_AUTO_USE(RZ_TimedFX
)
572 if (dayLength
== 0.f
)
577 double resultHour
= (double) cycle
* (double) cycleLength
+ (double) hour
;
578 result
.Day
= (sint32
) floor(resultHour
/ dayLength
);
579 result
.Hour
= (float) (resultHour
- (double) result
.Day
* (double) dayLength
);
582 // *******************************************************************************************
583 sint32
CTimedFXManager::dateToCycle(const CClientDate
&date
, float cycleLength
, float dayLength
)
586 H_AUTO_USE(RZ_TimedFX
)
587 if (cycleLength
== 0.f
) return 0;
588 if (dayLength
== cycleLength
) return date
.Day
;
589 double hour
= (double) date
.Day
* (double) dayLength
+ (double) date
.Hour
;
590 return (sint32
) floor(hour
/ cycleLength
);
594 // *******************************************************************************************
595 void CTimedFXManager::CManagedFX::computeHour(sint32 cycle
, float bias
, CClientDate
&resultDate
, float cycleLength
, float dayLength
, const NLMISC::CNoiseValue
&nv
, float minHour
, float maxHour
) const
598 H_AUTO_USE(RZ_TimedFX
)
599 // add an offset in z to get a different noise value each day
600 CVector pos
= SpawnPosition
+ (float) cycle
* CVector::K
+ bias
* nv
.Frequency
* CVector::I
;
601 float delta
= computeUniformNoise(nv
, pos
);
602 if (minHour
<= maxHour
)
604 // convert cycle:hour to day:hour (a cycle may be something other than 24 hour)
605 cycleToDate(cycle
, minHour
+ delta
* (maxHour
- minHour
), cycleLength
, dayLength
, resultDate
);
609 float hourDiff
= dayLength
- minHour
+ maxHour
;
610 float hour
= minHour
+ delta
* hourDiff
;
611 if (hour
>= cycleLength
)
613 cycleToDate(cycle
+ 1, hour
- dayLength
, cycleLength
, dayLength
, resultDate
);
617 cycleToDate(cycle
, hour
, cycleLength
, dayLength
, resultDate
);
622 // *******************************************************************************************
623 void CTimedFXManager::CManagedFX::computeStartHour(sint32 cycle
, CClientDate
&resultDate
, float cycleLength
, float dayLength
, const NLMISC::CNoiseValue
&nv
) const
626 H_AUTO_USE(RZ_TimedFX
)
629 resultDate
= CClientDate();
632 computeHour(cycle
, 0.f
, resultDate
, cycleLength
, dayLength
, nv
, FXSheet
->StartHourMin
, FXSheet
->StartHourMax
);
635 // *******************************************************************************************
636 void CTimedFXManager::CManagedFX::computeEndHour(sint32 cycle
, CClientDate
&resultDate
, float cycleLength
, float dayLength
, const NLMISC::CNoiseValue
&nv
) const
639 H_AUTO_USE(RZ_TimedFX
)
642 resultDate
= CClientDate();
645 if (!(FXSheet
->Mode
== CSeasonFXSheet::UseDuration
))
647 if (FXSheet
->EndHourMax
< FXSheet
->StartHourMin
)
649 computeHour(cycle
+ 1, 1.f
, resultDate
, cycleLength
, dayLength
, nv
, FXSheet
->EndHourMin
, FXSheet
->EndHourMax
);
653 computeHour(cycle
, 1.f
, resultDate
, cycleLength
, dayLength
, nv
, FXSheet
->EndHourMin
, FXSheet
->EndHourMax
);
658 // compute start hour and add duration
659 computeHour(cycle
, 0.f
, resultDate
, cycleLength
, dayLength
, nv
, FXSheet
->StartHourMin
, FXSheet
->StartHourMax
);
661 nlinfo("computeEndHour for day %d: start day = %d, start hour = %f, pos=(%f, %f, %f)", (int) day
, (int) resultDate
.Day
, resultDate
.Hour
, SpawnPosition
.x
, SpawnPosition
.y
, SpawnPosition
.z
);
664 const float bias
= 0.5656f
; // dummy bias to get another value
665 CVector pos
= SpawnPosition
+ (float) cycle
* CVector::K
+ bias
* nv
.Frequency
* CVector::I
;
666 float delta
= computeUniformNoise(nv
, pos
);
667 resultDate
.Hour
+= delta
* (FXSheet
->MaxDuration
- FXSheet
->MinDuration
) + FXSheet
->MinDuration
;
668 if (resultDate
.Hour
> dayLength
)
670 resultDate
.Hour
-= dayLength
;
676 // *******************************************************************************************
677 void CTimedFXManager::dumpFXToAdd() const
680 H_AUTO_USE(RZ_TimedFX
)
684 for(TTimeStampedFXPtrSet::const_iterator it
= _FXToAdd
.begin(); it
!= _FXToAdd
.end(); ++it
)
686 nlinfo("%s : day=%d, hour=%f, pos=(%.1f, %.1f, %.1f)",
687 it
->FX
->FXSheet
? it
->FX
->FXSheet
->FXName
.c_str() : "???",
690 it
->FX
->SpawnPosition
.x
,
691 it
->FX
->SpawnPosition
.y
,
692 it
->FX
->SpawnPosition
.z
697 // *******************************************************************************************
698 void CTimedFXManager::dumpFXToRemove() const
701 H_AUTO_USE(RZ_TimedFX
)
705 for(TTimeStampedFXPtrSet::const_iterator it
= _FXToRemove
.begin(); it
!= _FXToRemove
.end(); ++it
)
707 nlinfo("%s : day=%d, hour=%f, pos=(%.1f, %.1f, %.1f)",
708 it
->FX
->FXSheet
? it
->FX
->FXSheet
->FXName
.c_str() : "???",
711 it
->FX
->SpawnPosition
.x
,
712 it
->FX
->SpawnPosition
.y
,
713 it
->FX
->SpawnPosition
.z
718 // *******************************************************************************************
719 void CTimedFXManager::dumpFXInfo() const
722 H_AUTO_USE(RZ_TimedFX
)
726 nlinfo("Num FX to add = %d", (int) _FXToAdd
.size());
727 nlinfo("Num FX to remove = %d", (int) _FXToRemove
.size());
728 uint numInstance
= 0;
729 for(TManagedFXGroupList::const_iterator it
= _FXGroups
.begin(); it
!= _FXGroups
.end(); ++it
)
731 const TManagedFXGroup
&group
= it
->Group
;
732 for(uint k
= 0; k
< group
.size(); ++k
)
734 if (!group
[k
].Instance
.empty()) ++ numInstance
;
738 nlinfo("Num Intanciated FX = %d", (int) numInstance
);
741 // *******************************************************************************************
742 void CTimedFXManager::checkIntegrity()
745 H_AUTO_USE(RZ_TimedFX
)
747 // count number of instances that are not always instanciated
748 uint numInstance
= 0;
749 for(TManagedFXGroupList::const_iterator it
= _FXGroups
.begin(); it
!= _FXGroups
.end(); ++it
)
751 const TManagedFXGroup
&group
= it
->Group
;
752 for(uint k
= 0; k
< group
.size(); ++k
)
754 if (!group
[k
].Instance
.empty())
756 if (!(group
[k
].FXSheet
&& group
[k
].FXSheet
->Mode
== CSeasonFXSheet::AlwaysStarted
))
764 for(TTimeStampedFXPtrSet::iterator it
= _FXToAdd
.begin(); it
!= _FXToAdd
.end(); ++it
)
766 nlassert(it
->FX
->Magic
== 0xbaadcafe);
768 for(TTimeStampedFXPtrSet::iterator it
= _FXToRemove
.begin(); it
!= _FXToRemove
.end(); ++it
)
770 nlassert(it
->FX
->Magic
== 0xbaadcafe);
776 // *******************************************************************************************
777 void CTimedFXManager::setupUserParams(CManagedFX
&fi
, uint cycle
)
780 H_AUTO_USE(RZ_TimedFX
)
783 for(uint k
= 0; k
< 4; ++k
)
785 if (fi
.FXSheet
->UserParamsMax
[k
] != fi
.FXSheet
->UserParamsMin
[k
])
787 float delta
= computeUniformNoise(_Noise
, fi
.SpawnPosition
+ (float) 1.23564f
* k
* CVector::I
+ (float) cycle
* 2.564848f
* CVector::J
);
788 fi
.Instance
.setUserParam(k
, fi
.FXSheet
->UserParamsMin
[k
] + delta
* (fi
.FXSheet
->UserParamsMax
[k
] - fi
.FXSheet
->UserParamsMin
[k
]));
792 fi
.Instance
.setUserParam(k
, fi
.FXSheet
->UserParamsMin
[k
]);
798 for(uint k
= 0; k
< 4; ++k
)
800 fi
.Instance
.setUserParam(k
, 0.f
);
806 // *******************************************************************************************
807 // from a fx mode, get a description string
808 const char *timedFXModeToStr(CSeasonFXSheet::TMode mode
)
811 H_AUTO_USE(RZ_TimedFX
)
814 case CSeasonFXSheet::AlwaysStarted
: return "always started";
815 case CSeasonFXSheet::UseEndHour
: return "use end hour";
816 case CSeasonFXSheet::UseDuration
: return "use duration";
817 case CSeasonFXSheet::Spawn
: return "spawn";
823 // *******************************************************************************************
824 void CTimedFXManager::displayFXBoxes(TDebugDisplayMode displayMode
) const
827 H_AUTO_USE(RZ_TimedFX
)
828 Driver
->setViewMatrix(Scene
->getCam().getMatrix().inverted());
830 Scene
->getCam().getFrustum(fr
.Left
, fr
.Right
, fr
.Bottom
, fr
.Top
, fr
.Near
, fr
.Far
);
831 fr
.Perspective
= true;
832 Driver
->setFrustum(fr
);
833 TextContext
->setColor(CRGBA::Blue
);
834 TextContext
->setShaded(false);
835 TextContext
->setShadeOutline(false);
836 TextContext
->setFontSize(10);
839 float textSize
= 2.5f
;
841 for(TManagedFXGroupList::const_iterator groupIt
= _FXGroups
.begin(); groupIt
!= _FXGroups
.end(); ++groupIt
)
843 for(uint k
= 0; k
< groupIt
->Group
.size(); ++k
)
845 const CManagedFX
&mf
= groupIt
->Group
[k
];
846 NLMISC::CMatrix mat
= mf
.getInstanceMatrix();
847 Driver
->setModelMatrix(mat
);
848 NLMISC::CRGBA color
= CRGBA::Black
;
851 case CManagedFX::Permanent
: color
= (!mf
.Instance
.empty()) ? CRGBA::Magenta
: CRGBA::Cyan
; break;
852 case CManagedFX::InAddList
: color
= CRGBA::Blue
; break;
853 case CManagedFX::InRemoveList
: color
= (!mf
.Instance
.empty()) ? CRGBA::Red
: CRGBA::Yellow
; break;
854 case CManagedFX::Unknown
: break;
856 drawBox(CVector(- size
, - size
, - size
),
857 CVector(size
, size
, size
),
859 std::string textToDisplay
;
865 textToDisplay
= mf
.FXSheet
->FXName
;
870 case CManagedFX::Permanent
: textToDisplay
= NLMISC::toString("PERMANENT"); break;
871 case CManagedFX::InAddList
:
872 case CManagedFX::InRemoveList
:
873 textToDisplay
= NLMISC::toString("day:%d, hour:%d:%d", (int) mf
.SetHandle
->Date
.Day
, (int) mf
.SetHandle
->Date
.Hour
, (int) (60.f
* fmodf(mf
.SetHandle
->Date
.Hour
, 1.f
)));
886 textToDisplay
= "*" + textToDisplay
;
889 if (!textToDisplay
.empty())
891 TextContext
->setColor(color
);
893 mat
.setRot(Scene
->getCam().getRotQuat());
894 mat
.setPos(mf
.SpawnPosition
+ 2.5f
* size
* CVector::K
);
895 CVector distPos
= mf
.SpawnPosition
- Scene
->getCam().getPos();
896 mat
.scale(textSize
* distPos
.norm());
897 TextContext
->render3D(mat
, textToDisplay
);
904 // *******************************************************************************************
905 void CTimedFXManager::linkCandidateFX(TCandidateFXListSortedByDist
&targetList
, float dist
, CManagedFX
*fx
)
908 H_AUTO_USE(RZ_TimedFX
)
910 uint rank
= (uint
) (dist
/ _SortDistance
);
911 fx
->unlinkFromCandidateFXList(); // unlink from previous list
912 rank
= std::min(rank
, (uint
) (_CandidateFXListSortedByDist
.size() - 1));
913 fx
->_NextCandidateFX
= targetList
[rank
];
914 if (targetList
[rank
] != NULL
)
916 targetList
[rank
]->_PrevCandidateFX
= &fx
->_NextCandidateFX
;
918 fx
->_PrevCandidateFX
= &targetList
[rank
]; // NB the vector won't grow so no prb
919 targetList
[rank
] = fx
;
922 // *******************************************************************************************
923 void CTimedFXManager::linkInstanciatedFX(CManagedFX
*&listHead
, CManagedFX
*fx
)
926 H_AUTO_USE(RZ_TimedFX
)
928 fx
->unlinkFromInstanciatedFXList();
929 fx
->_NextInstanciatedFX
= listHead
;
932 listHead
->_PrevInstanciatedFX
= &fx
->_NextInstanciatedFX
;
934 fx
->_PrevInstanciatedFX
= &listHead
;
938 // *******************************************************************************************
939 void CTimedFXManager::updateInstanciatedFXList()
942 H_AUTO_USE(RZ_TimedFX
)
944 // if the list has not been modified since last time, no-op
945 if (!_CandidateFXListTouched
) return;
946 // algo : we take at most '_MaxNumberOfFXInstances' from the start of the updated list of candidate fxs (they are roughly sorted by distance)
947 // these fx are inserted in new list of instanciated fxs. All fx that haven't been inserted (they remains in the previous list)
948 // are discarded. At the end the new list replaces the previous one
949 CManagedFX
*toInstanciateListHead
= NULL
;
950 CManagedFX
*alreadyInstanciatedListHead
= NULL
;
951 uint numInstances
= 0;
952 uint numDistanceRanges
= (uint
)_CandidateFXListSortedByDist
.size();
953 sint maxNumPossibleInstance
= (sint
) (_MaxNumberOfFXInstances
- _FXManager
.getNumFXtoRemove());
954 if (maxNumPossibleInstance
> 0)
956 // NB: Ideally _CandidateFXListSortedByDist is small as a vector, so it's ok to traverse it in its entirety.
957 for(uint k
= 0; k
< numDistanceRanges
; ++k
)
959 CManagedFX
*currCandidate
= _CandidateFXListSortedByDist
[k
];
962 nlassert(currCandidate
->_PrevCandidateFX
);
963 // if fx already instanciated, put in special list
964 if (currCandidate
->_PrevInstanciatedFX
!= NULL
)
966 linkInstanciatedFX(alreadyInstanciatedListHead
, currCandidate
);
970 // not already instanciated
971 linkInstanciatedFX(toInstanciateListHead
, currCandidate
);
974 if (numInstances
== (uint
) maxNumPossibleInstance
) break; // max number of instances reached
975 currCandidate
= currCandidate
->_NextCandidateFX
;
977 if (numInstances
== (uint
) maxNumPossibleInstance
) break; // max number of instances reached
980 // removes old instances (those that were instanciated at previous frame, but are not anymore)
981 CManagedFX
*currFXToRemove
= _InstanciatedFXs
;
982 while (currFXToRemove
)
984 CManagedFX
*tmp
= currFXToRemove
;
985 nlassert(tmp
->_PrevInstanciatedFX
);
986 currFXToRemove
= currFXToRemove
->_NextInstanciatedFX
;
989 tmp
->shutDown(_Scene
, _FXManager
);
991 // fast unlink (all instance are removed from that list)
992 tmp
->_PrevInstanciatedFX
= NULL
;
993 tmp
->_NextInstanciatedFX
= NULL
;
995 // create new instances
996 CManagedFX
*prevFXToInstanciate
= NULL
;
997 CManagedFX
*currFXToInstanciate
= toInstanciateListHead
;
998 if (maxNumPossibleInstance
> 0)
1000 while (currFXToInstanciate
)
1002 nlassert(currFXToInstanciate
->_PrevInstanciatedFX
);
1003 bool oktoCreate
= true;
1004 // special case : if the fx must be spawned, then it must be a valid candidate at the frame it is inserted, otherwise
1005 // it is not created at all (this is to avoid it to spawn at a later date, when it's too late)
1006 if (currFXToInstanciate
->FXSheet
&& currFXToInstanciate
->FXSheet
->Mode
== CSeasonFXSheet::Spawn
)
1008 if (currFXToInstanciate
->SetHandle
->Date
!= _CurrDate
)
1011 // nb : the fx will be removed from the 'instanciated list' at next pass, because spawned fx are shutdown one frame after their creation (see CTimedFXManager::update)
1016 nlassert(currFXToInstanciate
->Instance
.empty());
1017 // if fx was in shutting down state, delete the instance
1018 NL3D::UInstance ts
= _Scene
->createInstance(currFXToInstanciate
->FXSheet
->FXName
);
1021 currFXToInstanciate
->Instance
.detach();
1025 currFXToInstanciate
->Instance
.cast (ts
);
1026 if (currFXToInstanciate
->Instance
.empty())
1028 // bad type, removes the shape
1029 _Scene
->deleteInstance(ts
);
1033 currFXToInstanciate
->Instance
.setTransformMode(NL3D::UTransform::DirectMatrix
);
1034 currFXToInstanciate
->Instance
.setMatrix(currFXToInstanciate
->getInstanceMatrix());
1035 if (currFXToInstanciate
->State
== CManagedFX::Permanent
)
1037 setupUserParams(*currFXToInstanciate
,0);
1041 setupUserParams(*currFXToInstanciate
, currFXToInstanciate
->SetHandle
->Date
.Day
);
1043 currFXToInstanciate
->Instance
.freezeHRC();
1047 prevFXToInstanciate
= currFXToInstanciate
;
1048 currFXToInstanciate
= currFXToInstanciate
->_NextInstanciatedFX
;
1051 // merge toInstanciateListHead & alreadyInstanciatedListHead together
1052 // this becomes the new list of instanciated fxs
1053 if (prevFXToInstanciate
)
1055 nlassert(prevFXToInstanciate
->_NextInstanciatedFX
== NULL
);
1056 prevFXToInstanciate
->_NextInstanciatedFX
= alreadyInstanciatedListHead
;
1057 if (alreadyInstanciatedListHead
)
1059 alreadyInstanciatedListHead
->_PrevInstanciatedFX
= &prevFXToInstanciate
->_NextInstanciatedFX
;
1062 _InstanciatedFXs
= toInstanciateListHead
;
1063 toInstanciateListHead
->_PrevInstanciatedFX
= &_InstanciatedFXs
;
1068 _InstanciatedFXs
= alreadyInstanciatedListHead
;
1069 if (alreadyInstanciatedListHead
)
1071 alreadyInstanciatedListHead
->_PrevInstanciatedFX
= &_InstanciatedFXs
;
1074 _CandidateFXListTouched
= false;
1077 // *******************************************************************************************
1078 void CTimedFXManager::updateCandidateFXListSorting()
1081 H_AUTO_USE(RZ_TimedFX
)
1082 // re-sort all instances by distance
1083 nlassert(_CandidateFXListSortedByDist
.size() == _CandidateFXListSortedByDistTmp
.size());
1084 uint numDistanceRanges
= (uint
)_CandidateFXListSortedByDist
.size();
1085 for(uint k
= 0; k
< numDistanceRanges
; ++k
)
1087 CManagedFX
*currCandidate
= _CandidateFXListSortedByDist
[k
];
1088 while(currCandidate
)
1090 CManagedFX
*tmp
= currCandidate
;
1091 currCandidate
= currCandidate
->_NextCandidateFX
;
1092 float dist
= (tmp
->SpawnPosition
- _LastCamPos
).norm();
1093 // unlink & relink in new list
1094 linkCandidateFX(_CandidateFXListSortedByDistTmp
, dist
, tmp
);
1098 _CandidateFXListSortedByDist
.swap(_CandidateFXListSortedByDistTmp
);
1101 // *******************************************************************************************
1102 void CTimedFXManager::setMaxNumFXInstances(uint maxNumInstances
)
1105 H_AUTO_USE(RZ_TimedFX
)
1106 _MaxNumberOfFXInstances
= maxNumInstances
;
1107 _CandidateFXListTouched
= true;
1110 // *******************************************************************************************
1112 NLMISC_COMMAND(dumpFXToAdd
, "dumpFXToAdd", "<>")
1114 if (!args
.empty()) return false;
1115 CTimedFXManager::getInstance().dumpFXToAdd();
1119 // *******************************************************************************************
1121 NLMISC_COMMAND(dumpFXToRemove
, "dumpFXToRemove", "<>")
1123 if (!args
.empty()) return false;
1124 CTimedFXManager::getInstance().dumpFXToRemove();
1129 // *******************************************************************************************
1131 NLMISC_COMMAND(dumpFXInfo
, "dumpFXInfo", "<>")
1133 if (!args
.empty()) return false;
1134 CTimedFXManager::getInstance().dumpFXInfo();
1139 extern class CIGCallback
*IGCallbacks
;
1141 // *******************************************************************************************
1143 NLMISC_COMMAND(updateSeason
, "updateSeason", "<>")
1145 if (!args
.empty()) return false;
1146 if (IGCallbacks
) IGCallbacks
->changeSeason();
1150 // *******************************************************************************************
1152 NLMISC_COMMAND(setMaxNumTimedFXs
, "set the max number of timed fx that are visible at a time", "<numInst>")
1154 if (args
.size() != 1) return false;
1155 uint maxNumInstances
;
1156 fromString(args
[0], maxNumInstances
);
1157 CTimedFXManager::getInstance().setMaxNumFXInstances(maxNumInstances
);