2010-05-06 00:08:41 +00:00
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
# include "stdpch.h"
# include "timed_fx_manager.h"
# include "ig_callback.h"
# include "client_sheets/plant_sheet.h"
# include "nel/3d/u_transform.h"
# include "nel/3d/u_particle_system_instance.h"
# include "nel/3d/u_driver.h"
# include "nel/3d/u_text_context.h"
# include "nel/3d/u_scene.h"
# include "nel/3d/u_camera.h"
# include "nel/misc/fast_floor.h"
# include "misc.h"
# include "fx_manager.h"
# include "nel/misc/check_fpu.h"
# include <utility>
# if defined(NL_DEBUG) && defined(NL_OS_WINDOWS)
# include <crtdbg.h>
# endif
using namespace std : : rel_ops ;
using namespace NLMISC ;
extern NL3D : : UTextContext * TextContext ;
extern NL3D : : UDriver * Driver ;
extern NL3D : : UScene * Scene ;
//#define DEBUG_FX
# ifdef DEBUG_FX
# ifndef NL_DEBUG
# error Flag DEBUG_FX can only be used in debug mode
# endif
# endif
# ifdef DEBUG_FX
# define CHECK_INTEGRITY checkIntegrity();
# else
# define CHECK_INTEGRITY
# endif
CTimedFXManager & CTimedFXManager : : getInstance ( )
{
FPU_CHECKER
static CTimedFXManager manager ;
return manager ;
}
const float DEFAULT_SPAWNED_FX_TIMED_OUT = 100.f ;
H_AUTO_DECL ( RZ_TimedFX )
//*******************************************************************************************
NLMISC : : CMatrix CTimedFX : : getInstanceMatrix ( ) const
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
NLMISC : : CMatrix result ;
result . identity ( ) ;
result . translate ( SpawnPosition ) ;
if ( FXSheet & & FXSheet - > InheritRot ) result . rotate ( Rot ) ;
if ( FXSheet & & FXSheet - > InheritScale ) result . scale ( Scale ) ;
return result ;
}
//*******************************************************************************************
CTimedFXManager : : CTimedFXManager ( )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
_Scene = NULL ,
_DayLength = 24.f ,
_InitDone = false ;
_InstanciatedFXs = NULL ;
_CandidateFXListTouched = false ;
_SortDistance = 0.f ;
_MaxNumberOfFXInstances = 0 ;
}
//*******************************************************************************************
CTimedFXManager : : ~ CTimedFXManager ( )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
reset ( ) ;
}
//*******************************************************************************************
void CTimedFXManager : : init ( NL3D : : UScene * scene ,
const CClientDate & startDate ,
float /* dayLength */ ,
float noiseFrequency ,
uint maxNumberOfFXInstances ,
float sortDistanceInterval ,
float maxDist )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
nlassert ( ! _InitDone ) ;
if ( ! scene )
{
nlwarning ( " Scene is NULL " ) ;
}
_Scene = scene ;
_CurrDate = startDate ;
_Noise . Frequency = noiseFrequency ;
_Noise . Abs = 0.f ;
_Noise . Rand = 1.f ;
CHECK_INTEGRITY
_InitDone = true ;
nlassert ( maxDist > 0.f ) ;
nlassert ( sortDistanceInterval > 0.f ) ;
_CandidateFXListSortedByDist . resize ( ( int ) ceilf ( maxDist / sortDistanceInterval ) ) ;
_CandidateFXListSortedByDistTmp . resize ( ( int ) ceilf ( maxDist / sortDistanceInterval ) ) ;
_MaxNumberOfFXInstances = maxNumberOfFXInstances ;
_SortDistance = sortDistanceInterval ;
}
//*******************************************************************************************
void CTimedFXManager : : reset ( )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
if ( _InitDone )
{
nlassert ( _InitDone ) ;
CHECK_INTEGRITY
while ( ! _FXGroups . empty ( ) )
{
remove ( _FXGroups . begin ( ) ) ;
}
_Scene = NULL ;
_CurrDate = CClientDate ( ) ;
_InitDone = false ;
_FXManager . reset ( ) ;
}
}
//*******************************************************************************************
void CTimedFXManager : : setDate ( const CClientDate & date )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
_CurrDate = date ;
}
//*******************************************************************************************
CTimedFXManager : : TFXGroupHandle CTimedFXManager : : add ( const std : : vector < CTimedFX > & fxs , EGSPD : : CSeason : : TSeason /* season */ )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
nlassert ( _InitDone ) ;
CHECK_INTEGRITY
_FXGroups . push_front ( CManagedFXGroup ( ) ) ;
# ifdef DEBUG_FX
_FXGroups . front ( ) . Season = season ;
# endif
TManagedFXGroup & newGroup = _FXGroups . begin ( ) - > Group ;
newGroup . resize ( fxs . size ( ) ) ;
for ( uint k = 0 ; k < fxs . size ( ) ; + + k )
{
CManagedFX & fi = newGroup [ k ] ;
fi . FXSheet = fxs [ k ] . FXSheet ;
fi . SpawnPosition = fxs [ k ] . SpawnPosition ;
fi . Scale = fxs [ k ] . Scale ;
fi . Rot = fxs [ k ] . Rot ;
fi . Instance = NULL ;
# if !FINAL_VERSION
fi . FromIG = fxs [ k ] . FromIG ;
# endif
# ifdef DEBUG_FX
fi . OwnerGroup = & ( _FXGroups . front ( ) ) ;
# endif
// compute start and end hour to see in which list insertion should occurs
bool instanciate = false ;
CClientDate startDate ;
CClientDate endDate ;
//sint32 debugDay;
if ( ! ( fi . FXSheet & & fi . FXSheet - > Mode = = CSeasonFXSheet : : AlwaysStarted ) )
{
if ( fi . FXSheet - > Mode = = CSeasonFXSheet : : Spawn )
{
// compute next spawn date
float cycleLength = fi . FXSheet ? fi . FXSheet - > CycleDuration : _DayLength ;
sint32 cycle = dateToCycle ( _CurrDate , cycleLength , _DayLength ) ;
fi . computeStartHour ( cycle - 1 , startDate , cycleLength , _DayLength , _Noise ) ;
if ( startDate < _CurrDate )
{
fi . computeStartHour ( cycle , startDate , cycleLength , _DayLength , _Noise ) ;
if ( startDate < _CurrDate )
{
fi . computeStartHour ( cycle + 1 , startDate , cycleLength , _DayLength , _Noise ) ;
}
}
}
else
{
// the system is not always instanciated, so must check if it currently is.
float cycleLength = fi . FXSheet ? fi . FXSheet - > CycleDuration : _DayLength ;
sint32 cycle = dateToCycle ( _CurrDate , cycleLength , _DayLength ) ;
fi . computeStartHour ( cycle , startDate , cycleLength , _DayLength , _Noise ) ;
//debugDay = _CurrDate.Day;
if ( startDate > _CurrDate )
{
// Let see if it did not start the previous cycle and then ended that cycle
fi . computeEndHour ( cycle - 1 , endDate , cycleLength , _DayLength , _Noise ) ;
if ( endDate > _CurrDate )
{
CClientDate prevStartDate ;
fi . computeStartHour ( cycle - 1 , prevStartDate , cycleLength , _DayLength , _Noise ) ;
if ( prevStartDate < = _CurrDate )
{
instanciate = true ;
}
else
{
startDate = prevStartDate ;
//debugDay = _CurrDate.Day - 1;
}
}
}
else
{
fi . computeEndHour ( cycle , endDate , cycleLength , _DayLength , _Noise ) ;
if ( endDate > _CurrDate )
{
// the fx is currently running
instanciate = true ;
}
else
{
fi . computeStartHour ( _CurrDate . Day + 1 , endDate , cycleLength , _DayLength , _Noise ) ;
// the fx will start only during the next day
//debugDay = _CurrDate.Day + 1;
}
}
}
}
else
{
if ( fi . FXSheet & & fi . FXSheet - > Mode = = CSeasonFXSheet : : AlwaysStarted )
{
fi . State = CManagedFX : : Permanent ;
instanciate = true ;
}
}
if ( instanciate )
{
if ( ! _Scene | | ! fi . FXSheet )
{
fi . Instance = NULL ;
}
else
{
// insert in candidate fx list
float dist = ( fi . SpawnPosition - _LastCamPos ) . norm ( ) ;
linkCandidateFX ( _CandidateFXListSortedByDist , dist , & fi ) ;
_CandidateFXListTouched = true ;
}
// Add the system to the queue of systems to be removed, only if it is not always started
if ( ! ( fi . FXSheet & & fi . FXSheet - > Mode = = CSeasonFXSheet : : AlwaysStarted ) )
{
// As the system is currenlty instanciated, we won't deals with it again until it shutdowns, so, we save it in the priority queue.
CTimeStampedFX tsf ;
tsf . Date = endDate ;
//tsf.DebugDay = 666;
tsf . FX = & fi ; // yes, we keep a pointer on a vector element, but we will never grow that vector
fi . SetHandle = _FXToRemove . insert ( tsf ) . first ;
fi . State = CManagedFX : : InRemoveList ;
}
}
else
{
// The system is not instanciated yet, but will be later, so add to the queue of systems that "will be instanciated later"
CTimeStampedFX tsf ;
tsf . Date = startDate ;
tsf . FX = & fi ; // yes, we keep a pointer on a vector element, but we will never grow that vector
//tsf.DebugDay = debugDay;
nlassert ( fi . State ! = CManagedFX : : InAddList ) ;
fi . SetHandle = _FXToAdd . insert ( tsf ) . first ;
fi . State = CManagedFX : : InAddList ;
# ifdef DEBUG_FX
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 ,
fi . SpawnPosition . x , fi . SpawnPosition . y , fi . SpawnPosition . z ) ;
# endif
}
}
CHECK_INTEGRITY
return _FXGroups . begin ( ) ;
}
//*******************************************************************************************
void CTimedFXManager : : remove ( TFXGroupHandle handle )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
nlassert ( _InitDone ) ;
CHECK_INTEGRITY
TManagedFXGroup & group = handle - > Group ;
for ( uint k = 0 ; k < group . size ( ) ; + + k )
{
CManagedFX & fi = group [ k ] ;
if ( ! fi . Instance . empty ( ) )
{
nlassert ( _Scene ) ;
_Scene - > deleteInstance ( fi . Instance ) ;
}
switch ( fi . State )
{
case CManagedFX : : InAddList : _FXToAdd . erase ( fi . SetHandle ) ; break ;
case CManagedFX : : InRemoveList : _FXToRemove . erase ( fi . SetHandle ) ; break ;
}
fi . unlinkFromCandidateFXList ( ) ;
fi . unlinkFromInstanciatedFXList ( ) ;
}
_FXGroups . erase ( handle ) ;
CHECK_INTEGRITY
}
//*******************************************************************************************
void CTimedFXManager : : update ( const CClientDate & date , EGSPD : : CSeason : : TSeason /* season */ , const NLMISC : : CVector & camPos )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
nlassert ( _InitDone ) ;
# ifdef DEBUG_FX
# ifdef NL_OS_WINDOWS
_CrtCheckMemory ( ) ;
# endif
# endif
CHECK_INTEGRITY
_FXManager . update ( ) ;
// check for instanciated fxs that should be removed
while ( ! _FXToRemove . empty ( ) )
{
const CTimeStampedFX & tsf = * _FXToRemove . begin ( ) ;
# ifdef DEBUG_FX
nlassert ( tsf . FX - > OwnerGroup ) ;
//nlassert(tsf.FX->OwnerGroup->Season == season);
# endif
nlassert ( tsf . FX - > State = = CManagedFX : : InRemoveList ) ;
if ( tsf . Date > date ) break ; // all following fx will be remove in the future
// removes from candidate & instanciated list (this causes fx to be shutdown at the end of the pass)
tsf . FX - > unlinkFromCandidateFXList ( ) ;
_CandidateFXListTouched = true ;
// compute new activation date, and insert in _FXToAdd queue
CTimeStampedFX newTsf ;
newTsf . FX = tsf . FX ;
float cycleLength = newTsf . FX - > FXSheet ? newTsf . FX - > FXSheet - > CycleDuration : _DayLength ;
sint32 cycle = dateToCycle ( tsf . Date , cycleLength , _DayLength ) ;
tsf . FX - > computeStartHour ( cycle , newTsf . Date , cycleLength , _DayLength , _Noise ) ;
if ( newTsf . Date < = tsf . Date )
{
// already started for that day, so compute start hour for next day.
tsf . FX - > computeStartHour ( cycle + 1 , newTsf . Date , cycleLength , _DayLength , _Noise ) ;
}
tsf . FX - > SetHandle = _FXToAdd . insert ( newTsf ) . first ;
tsf . FX - > State = CManagedFX : : InAddList ;
// remove from current queue
_FXToRemove . erase ( _FXToRemove . begin ( ) ) ;
}
// check for fxs that should be instanciated
while ( ! _FXToAdd . empty ( ) )
{
const CTimeStampedFX & tsf = * _FXToAdd . begin ( ) ;
# ifdef DEBUG_FX
nlassert ( tsf . FX - > OwnerGroup ) ;
//nlassert(tsf.FX->OwnerGroup->Season == season);
# endif
nlassert ( tsf . FX - > State = = CManagedFX : : InAddList ) ;
if ( tsf . Date > = date ) break ; // all following fx will be instancied in the future
// activate current fx
if ( ! _Scene | | ! tsf . FX - > FXSheet )
{
tsf . FX - > Instance = NULL ;
}
else
{
// insert in candidate fx list
float dist = ( tsf . FX - > SpawnPosition - _LastCamPos ) . norm ( ) ;
linkCandidateFX ( _CandidateFXListSortedByDist , dist , tsf . FX ) ;
_CandidateFXListTouched = true ;
}
// compute next shutdown date
# ifdef DEBUG_FX
nlinfo ( " ===== compute end date for %s, debug day = %d " , tsf . FX - > FXSheet ? tsf . FX - > FXSheet - > FXName . c_str ( ) : " ??? " , /*tsf.DebugDay*/ 666 ) ;
# endif
CTimeStampedFX newTsf ;
newTsf . FX = tsf . FX ;
if ( tsf . FX - > FXSheet & & tsf . FX - > FXSheet - > Mode = = CSeasonFXSheet : : Spawn )
{
// 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
newTsf . Date = date ;
}
else
{
# ifdef DEBUG_FX
nlinfo ( " start day = %d, start hour = %f " , ( int ) tsf . Date . Day , tsf . Date . Hour ) ;
# endif
float cycleLength = tsf . FX - > FXSheet ? tsf . FX - > FXSheet - > CycleDuration : _DayLength ;
sint32 cycle = dateToCycle ( tsf . Date , cycleLength , _DayLength ) ;
tsf . FX - > computeEndHour ( cycle - 1 , newTsf . Date , cycleLength , _DayLength , _Noise ) ;
# ifdef DEBUG_FX
nlinfo ( " try 0 : end day = %d, end hour = %f " , ( int ) newTsf . Date . Day , newTsf . Date . Hour ) ;
# endif
if ( newTsf . Date < tsf . Date )
{
tsf . FX - > computeEndHour ( cycle , newTsf . Date , cycleLength , _DayLength , _Noise ) ;
# ifdef DEBUG_FX
nlinfo ( " try 1 : end day = %d, end hour = %f " , ( int ) newTsf . Date . Day , newTsf . Date . Hour ) ;
# endif
if ( newTsf . Date < tsf . Date )
{
tsf . FX - > computeEndHour ( cycle + 1 , newTsf . Date , cycleLength , _DayLength , _Noise ) ;
# ifdef DEBUG_FX
nlinfo ( " try 2 : end day = %d, end hour = %f " , ( int ) newTsf . Date . Day , newTsf . Date . Hour ) ;
# endif
}
}
}
newTsf . FX - > SetHandle = _FXToRemove . insert ( newTsf ) . first ;
newTsf . FX - > State = CManagedFX : : InRemoveList ;
// remove from current queue
_FXToAdd . erase ( _FXToAdd . begin ( ) ) ;
}
CHECK_INTEGRITY
_CurrDate = date ;
// if cam pos has moved too much, must completely resort fx by distance
if ( ( _CurrDate . Day = = 0 & & _CurrDate . Hour = = 0.f ) | | ( camPos - _LastCamPos ) . sqrnorm ( ) > _SortDistance * _SortDistance )
{
_LastCamPos = camPos ;
updateCandidateFXListSorting ( ) ;
_CandidateFXListTouched = true ;
}
// update instanciated fxs
updateInstanciatedFXList ( ) ;
}
//*******************************************************************************************
void CTimedFXManager : : shutDown ( TFXGroupHandle handle )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
nlassert ( _InitDone ) ;
CHECK_INTEGRITY
TManagedFXGroup & fxGroup = handle - > Group ;
// remove all fxs of that group from both queues, and shutdown those that are instanciated
for ( uint k = 0 ; k < fxGroup . size ( ) ; + + k )
{
fxGroup [ k ] . shutDown ( _Scene , _FXManager ) ;
}
CHECK_INTEGRITY
remove ( handle ) ;
}
//*******************************************************************************************
void CTimedFXManager : : CManagedFX : : unlinkFromCandidateFXList ( )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
if ( _PrevCandidateFX )
{
* _PrevCandidateFX = _NextCandidateFX ;
if ( _NextCandidateFX )
{
_NextCandidateFX - > _PrevCandidateFX = _PrevCandidateFX ;
}
_NextCandidateFX = NULL ;
_PrevCandidateFX = NULL ;
}
}
//*******************************************************************************************
void CTimedFXManager : : CManagedFX : : unlinkFromInstanciatedFXList ( )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
if ( _PrevInstanciatedFX )
{
* _PrevInstanciatedFX = _NextInstanciatedFX ;
if ( _NextInstanciatedFX )
{
_NextInstanciatedFX - > _PrevInstanciatedFX = _PrevInstanciatedFX ;
}
_NextInstanciatedFX = NULL ;
_PrevInstanciatedFX = NULL ;
}
}
//*******************************************************************************************
void CTimedFXManager : : CManagedFX : : shutDown ( NL3D : : UScene * scene , CFXManager & fxManager )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
if ( ! Instance . empty ( ) )
{
// should not be already shutting down
// if the fx is spaned, it will end by itself, so we just add it to the shutdown list
if ( FXSheet & & FXSheet - > Mode = = CSeasonFXSheet : : Spawn )
{
// delegate to fx manager
fxManager . addFX ( Instance , DEFAULT_SPAWNED_FX_TIMED_OUT ) ;
}
else
{
// fx isn't spwaned, so must tell fx to stop its emitters
if ( Instance . isSystemPresent ( ) & & Instance . isValid ( ) )
{
if ( ! Instance . removeByID ( ' main ' ) & & ! Instance . removeByID ( ' STOP ' ) )
{
// if a specific emitter has not be tagged, just stop all emitters if there's no better solution
Instance . activateEmitters ( false ) ;
}
fxManager . addFX ( Instance , FX_MANAGER_DEFAULT_TIMEOUT , true ) ;
}
else
{
// the system is not present yet -> delete it immediatly
if ( scene ) scene - > deleteInstance ( Instance ) ;
}
}
Instance = NULL ;
}
}
//*******************************************************************************************
void CTimedFXManager : : cycleToDate ( sint32 cycle , float hour , float cycleLength , float dayLength , CClientDate & result )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
if ( dayLength = = 0.f )
{
result . Day = 0 ;
result . Hour = 0 ;
}
double resultHour = ( double ) cycle * ( double ) cycleLength + ( double ) hour ;
result . Day = ( sint32 ) floor ( resultHour / dayLength ) ;
result . Hour = ( float ) ( resultHour - ( double ) result . Day * ( double ) dayLength ) ;
}
//*******************************************************************************************
sint32 CTimedFXManager : : dateToCycle ( const CClientDate & date , float cycleLength , float dayLength )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
if ( cycleLength = = 0.f ) return 0 ;
if ( dayLength = = cycleLength ) return date . Day ;
double hour = ( double ) date . Day * ( double ) dayLength + ( double ) date . Hour ;
return ( sint32 ) floor ( hour / cycleLength ) ;
}
//*******************************************************************************************
void CTimedFXManager : : CManagedFX : : computeHour ( sint32 cycle , float bias , CClientDate & resultDate , float cycleLength , float dayLength , const NLMISC : : CNoiseValue & nv , float minHour , float maxHour ) const
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
// add an offset in z to get a different noise value each day
CVector pos = SpawnPosition + ( float ) cycle * CVector : : K + bias * nv . Frequency * CVector : : I ;
float delta = computeUniformNoise ( nv , pos ) ;
if ( minHour < = maxHour )
{
// convert cycle:hour to day:hour (a cycle may be something other than 24 hour)
cycleToDate ( cycle , minHour + delta * ( maxHour - minHour ) , cycleLength , dayLength , resultDate ) ;
}
else
{
float hourDiff = dayLength - minHour + maxHour ;
float hour = minHour + delta * hourDiff ;
if ( hour > = cycleLength )
{
cycleToDate ( cycle + 1 , hour - dayLength , cycleLength , dayLength , resultDate ) ;
}
else
{
cycleToDate ( cycle , hour , cycleLength , dayLength , resultDate ) ;
}
}
}
//*******************************************************************************************
void CTimedFXManager : : CManagedFX : : computeStartHour ( sint32 cycle , CClientDate & resultDate , float cycleLength , float dayLength , const NLMISC : : CNoiseValue & nv ) const
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
if ( ! FXSheet )
{
resultDate = CClientDate ( ) ;
return ;
}
computeHour ( cycle , 0.f , resultDate , cycleLength , dayLength , nv , FXSheet - > StartHourMin , FXSheet - > StartHourMax ) ;
}
//*******************************************************************************************
void CTimedFXManager : : CManagedFX : : computeEndHour ( sint32 cycle , CClientDate & resultDate , float cycleLength , float dayLength , const NLMISC : : CNoiseValue & nv ) const
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
if ( ! FXSheet )
{
resultDate = CClientDate ( ) ;
return ;
}
if ( ! ( FXSheet - > Mode = = CSeasonFXSheet : : UseDuration ) )
{
if ( FXSheet - > EndHourMax < FXSheet - > StartHourMin )
{
computeHour ( cycle + 1 , 1.f , resultDate , cycleLength , dayLength , nv , FXSheet - > EndHourMin , FXSheet - > EndHourMax ) ;
}
else
{
computeHour ( cycle , 1.f , resultDate , cycleLength , dayLength , nv , FXSheet - > EndHourMin , FXSheet - > EndHourMax ) ;
}
}
else
{
// compute start hour and add duration
computeHour ( cycle , 0.f , resultDate , cycleLength , dayLength , nv , FXSheet - > StartHourMin , FXSheet - > StartHourMax ) ;
# ifdef DEBUG_FX
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 ) ;
# endif
// add duration
const float bias = 0.5656f ; // dummy bias to get another value
CVector pos = SpawnPosition + ( float ) cycle * CVector : : K + bias * nv . Frequency * CVector : : I ;
float delta = computeUniformNoise ( nv , pos ) ;
resultDate . Hour + = delta * ( FXSheet - > MaxDuration - FXSheet - > MinDuration ) + FXSheet - > MinDuration ;
if ( resultDate . Hour > dayLength )
{
resultDate . Hour - = dayLength ;
+ + resultDate . Day ;
}
}
}
//*******************************************************************************************
void CTimedFXManager : : dumpFXToAdd ( ) const
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
nlassert ( _InitDone ) ;
nlinfo ( " FX to add " ) ;
nlinfo ( " ========= " ) ;
for ( TTimeStampedFXPtrSet : : const_iterator it = _FXToAdd . begin ( ) ; it ! = _FXToAdd . end ( ) ; + + it )
{
nlinfo ( " %s : day=%d, hour=%f, pos=(%.1f, %.1f, %.1f) " ,
it - > FX - > FXSheet ? it - > FX - > FXSheet - > FXName . c_str ( ) : " ??? " ,
( int ) it - > Date . Day ,
it - > Date . Hour ,
it - > FX - > SpawnPosition . x ,
it - > FX - > SpawnPosition . y ,
it - > FX - > SpawnPosition . z
) ;
}
}
//*******************************************************************************************
void CTimedFXManager : : dumpFXToRemove ( ) const
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
nlassert ( _InitDone ) ;
nlinfo ( " FX to add " ) ;
nlinfo ( " ========= " ) ;
for ( TTimeStampedFXPtrSet : : const_iterator it = _FXToRemove . begin ( ) ; it ! = _FXToRemove . end ( ) ; + + it )
{
nlinfo ( " %s : day=%d, hour=%f, pos=(%.1f, %.1f, %.1f) " ,
it - > FX - > FXSheet ? it - > FX - > FXSheet - > FXName . c_str ( ) : " ??? " ,
( int ) it - > Date . Day ,
it - > Date . Hour ,
it - > FX - > SpawnPosition . x ,
it - > FX - > SpawnPosition . y ,
it - > FX - > SpawnPosition . z
) ;
}
}
//*******************************************************************************************
void CTimedFXManager : : dumpFXInfo ( ) const
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
nlassert ( _InitDone ) ;
nlinfo ( " FX Infos " ) ;
nlinfo ( " ========= " ) ;
nlinfo ( " Num FX to add = %d " , ( int ) _FXToAdd . size ( ) ) ;
nlinfo ( " Num FX to remove = %d " , ( int ) _FXToRemove . size ( ) ) ;
uint numInstance = 0 ;
for ( TManagedFXGroupList : : const_iterator it = _FXGroups . begin ( ) ; it ! = _FXGroups . end ( ) ; + + it )
{
const TManagedFXGroup & group = it - > Group ;
for ( uint k = 0 ; k < group . size ( ) ; + + k )
{
if ( ! group [ k ] . Instance . empty ( ) ) + + numInstance ;
}
}
nlinfo ( " Num Intanciated FX = %d " , ( int ) numInstance ) ;
}
//*******************************************************************************************
void CTimedFXManager : : checkIntegrity ( )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
// count number of instances that are not always instanciated
uint numInstance = 0 ;
for ( TManagedFXGroupList : : const_iterator it = _FXGroups . begin ( ) ; it ! = _FXGroups . end ( ) ; + + it )
{
const TManagedFXGroup & group = it - > Group ;
for ( uint k = 0 ; k < group . size ( ) ; + + k )
{
if ( ! group [ k ] . Instance . empty ( ) )
{
if ( ! ( group [ k ] . FXSheet & & group [ k ] . FXSheet - > Mode = = CSeasonFXSheet : : AlwaysStarted ) )
{
+ + numInstance ;
}
}
}
}
# ifdef NL_DEBUG
for ( TTimeStampedFXPtrSet : : iterator it = _FXToAdd . begin ( ) ; it ! = _FXToAdd . end ( ) ; + + it )
{
nlassert ( it - > FX - > Magic = = 0xbaadcafe ) ;
}
for ( TTimeStampedFXPtrSet : : iterator it = _FXToRemove . begin ( ) ; it ! = _FXToRemove . end ( ) ; + + it )
{
nlassert ( it - > FX - > Magic = = 0xbaadcafe ) ;
}
# endif
}
//*******************************************************************************************
void CTimedFXManager : : setupUserParams ( CManagedFX & fi , uint cycle )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
if ( fi . FXSheet )
{
for ( uint k = 0 ; k < 4 ; + + k )
{
if ( fi . FXSheet - > UserParamsMax [ k ] ! = fi . FXSheet - > UserParamsMin [ k ] )
{
float delta = computeUniformNoise ( _Noise , fi . SpawnPosition + ( float ) 1.23564f * k * CVector : : I + ( float ) cycle * 2.564848f * CVector : : J ) ;
fi . Instance . setUserParam ( k , fi . FXSheet - > UserParamsMin [ k ] + delta * ( fi . FXSheet - > UserParamsMax [ k ] - fi . FXSheet - > UserParamsMin [ k ] ) ) ;
}
else
{
fi . Instance . setUserParam ( k , fi . FXSheet - > UserParamsMin [ k ] ) ;
}
}
}
else
{
for ( uint k = 0 ; k < 4 ; + + k )
{
fi . Instance . setUserParam ( k , 0.f ) ;
}
}
}
//*******************************************************************************************
// from a fx mode, get a description string
const char * timedFXModeToStr ( CSeasonFXSheet : : TMode mode )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
switch ( mode )
{
case CSeasonFXSheet : : AlwaysStarted : return " always started " ;
case CSeasonFXSheet : : UseEndHour : return " use end hour " ;
case CSeasonFXSheet : : UseDuration : return " use duration " ;
case CSeasonFXSheet : : Spawn : return " spawn " ;
default : break ;
}
return " ??? " ;
}
//*******************************************************************************************
void CTimedFXManager : : displayFXBoxes ( TDebugDisplayMode displayMode ) const
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
Driver - > setViewMatrix ( Scene - > getCam ( ) . getMatrix ( ) . inverted ( ) ) ;
NL3D : : CFrustum fr ;
Scene - > getCam ( ) . getFrustum ( fr . Left , fr . Right , fr . Bottom , fr . Top , fr . Near , fr . Far ) ;
fr . Perspective = true ;
Driver - > setFrustum ( fr ) ;
TextContext - > setColor ( CRGBA : : Blue ) ;
TextContext - > setShaded ( false ) ;
TextContext - > setFontSize ( 10 ) ;
//
float size = 0.4f ;
float textSize = 2.5f ;
// display fx to add
for ( TManagedFXGroupList : : const_iterator groupIt = _FXGroups . begin ( ) ; groupIt ! = _FXGroups . end ( ) ; + + groupIt )
{
for ( uint k = 0 ; k < groupIt - > Group . size ( ) ; + + k )
{
const CManagedFX & mf = groupIt - > Group [ k ] ;
NLMISC : : CMatrix mat = mf . getInstanceMatrix ( ) ;
Driver - > setModelMatrix ( mat ) ;
NLMISC : : CRGBA color = CRGBA : : Black ;
switch ( mf . State )
{
case CManagedFX : : Permanent : color = ( ! mf . Instance . empty ( ) ) ? CRGBA : : Magenta : CRGBA : : Cyan ; break ;
case CManagedFX : : InAddList : color = CRGBA : : Blue ; break ;
case CManagedFX : : InRemoveList : color = ( ! mf . Instance . empty ( ) ) ? CRGBA : : Red : CRGBA : : Yellow ; break ;
}
drawBox ( CVector ( - size , - size , - size ) ,
CVector ( size , size , size ) ,
color ) ;
std : : string textToDisplay ;
switch ( displayMode )
{
case NoText :
break ;
case PSName :
textToDisplay = mf . FXSheet - > FXName ;
break ;
case SpawnDate :
switch ( mf . State )
{
case CManagedFX : : Permanent : textToDisplay = NLMISC : : toString ( " PERMANENT " ) ; break ;
case CManagedFX : : InAddList :
case CManagedFX : : InRemoveList :
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 ) ) ) ;
break ;
}
break ;
default :
nlassert ( 0 ) ;
break ;
}
# if !FINAL_VERSION
if ( mf . FromIG )
{
textToDisplay = " * " + textToDisplay ;
}
# endif
if ( ! textToDisplay . empty ( ) )
{
TextContext - > setColor ( color ) ;
mat . identity ( ) ;
mat . setRot ( Scene - > getCam ( ) . getRotQuat ( ) ) ;
mat . setPos ( mf . SpawnPosition + 2.5f * size * CVector : : K ) ;
CVector distPos = mf . SpawnPosition - Scene - > getCam ( ) . getPos ( ) ;
mat . scale ( textSize * distPos . norm ( ) ) ;
TextContext - > render3D ( mat , textToDisplay ) ;
}
}
}
}
//*******************************************************************************************
void CTimedFXManager : : linkCandidateFX ( TCandidateFXListSortedByDist & targetList , float dist , CManagedFX * fx )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
if ( ! fx ) return ;
uint rank = ( uint ) ( dist / _SortDistance ) ;
fx - > unlinkFromCandidateFXList ( ) ; // unlink from previous list
rank = std : : min ( rank , ( uint ) ( _CandidateFXListSortedByDist . size ( ) - 1 ) ) ;
fx - > _NextCandidateFX = targetList [ rank ] ;
if ( targetList [ rank ] ! = NULL )
{
targetList [ rank ] - > _PrevCandidateFX = & fx - > _NextCandidateFX ;
}
fx - > _PrevCandidateFX = & targetList [ rank ] ; // NB the vector won't grow so no prb
targetList [ rank ] = fx ;
}
//*******************************************************************************************
void CTimedFXManager : : linkInstanciatedFX ( CManagedFX * & listHead , CManagedFX * fx )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
if ( ! fx ) return ;
fx - > unlinkFromInstanciatedFXList ( ) ;
fx - > _NextInstanciatedFX = listHead ;
if ( listHead )
{
listHead - > _PrevInstanciatedFX = & fx - > _NextInstanciatedFX ;
}
fx - > _PrevInstanciatedFX = & listHead ;
listHead = fx ;
}
//*******************************************************************************************
void CTimedFXManager : : updateInstanciatedFXList ( )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
nlassert ( _InitDone ) ;
// if the list has not been modified since last time, no-op
if ( ! _CandidateFXListTouched ) return ;
// algo : we take at most '_MaxNumberOfFXInstances' from the start of the updated list of candidate fxs (they are roughly sorted by distance)
// these fx are inserted in new list of instanciated fxs. All fx that haven't been inserted (they remains in the previous list)
// are discarded. At the end the new list replaces the previous one
CManagedFX * toInstanciateListHead = NULL ;
CManagedFX * alreadyInstanciatedListHead = NULL ;
uint numInstances = 0 ;
2010-05-13 20:45:24 +00:00
uint numDistanceRanges = ( uint ) _CandidateFXListSortedByDist . size ( ) ;
2010-05-06 00:08:41 +00:00
sint maxNumPossibleInstance = ( sint ) ( _MaxNumberOfFXInstances - _FXManager . getNumFXtoRemove ( ) ) ;
if ( maxNumPossibleInstance > 0 )
{
// NB: Ideally _CandidateFXListSortedByDist is small as a vector, so it's ok to traverse it in its entirety.
for ( uint k = 0 ; k < numDistanceRanges ; + + k )
{
CManagedFX * currCandidate = _CandidateFXListSortedByDist [ k ] ;
while ( currCandidate )
{
nlassert ( currCandidate - > _PrevCandidateFX ) ;
// if fx already instanciated, put in special list
if ( currCandidate - > _PrevInstanciatedFX ! = NULL )
{
linkInstanciatedFX ( alreadyInstanciatedListHead , currCandidate ) ;
}
else
{
// not already instanciated
linkInstanciatedFX ( toInstanciateListHead , currCandidate ) ;
}
+ + numInstances ;
if ( numInstances = = ( uint ) maxNumPossibleInstance ) break ; // max number of instances reached
currCandidate = currCandidate - > _NextCandidateFX ;
}
if ( numInstances = = ( uint ) maxNumPossibleInstance ) break ; // max number of instances reached
}
}
// removes old instances (those that were instanciated at previous frame, but are not anymore)
CManagedFX * currFXToRemove = _InstanciatedFXs ;
while ( currFXToRemove )
{
CManagedFX * tmp = currFXToRemove ;
nlassert ( tmp - > _PrevInstanciatedFX ) ;
currFXToRemove = currFXToRemove - > _NextInstanciatedFX ;
// shut the fx down
tmp - > shutDown ( _Scene , _FXManager ) ;
// fast unlink (all instance are removed from that list)
tmp - > _PrevInstanciatedFX = NULL ;
tmp - > _NextInstanciatedFX = NULL ;
}
// create new instances
CManagedFX * prevFXToInstanciate = NULL ;
CManagedFX * currFXToInstanciate = toInstanciateListHead ;
if ( maxNumPossibleInstance > 0 )
{
while ( currFXToInstanciate )
{
nlassert ( currFXToInstanciate - > _PrevInstanciatedFX ) ;
bool oktoCreate = true ;
// special case : if the fx must be spawned, then it must be a valid candidate at the frame it is inserted, otherwise
// it is not created at all (this is to avoid it to spawn at a later date, when it's too late)
if ( currFXToInstanciate - > FXSheet & & currFXToInstanciate - > FXSheet - > Mode = = CSeasonFXSheet : : Spawn )
{
if ( currFXToInstanciate - > SetHandle - > Date ! = _CurrDate )
{
oktoCreate = false ;
// 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)
}
}
if ( oktoCreate )
{
nlassert ( currFXToInstanciate - > Instance . empty ( ) ) ;
// if fx was in shutting down state, delete the instance
NL3D : : UInstance ts = _Scene - > createInstance ( currFXToInstanciate - > FXSheet - > FXName ) ;
if ( ts . empty ( ) )
{
currFXToInstanciate - > Instance . detach ( ) ;
}
else
{
currFXToInstanciate - > Instance . cast ( ts ) ;
if ( currFXToInstanciate - > Instance . empty ( ) )
{
// bad type, removes the shape
_Scene - > deleteInstance ( ts ) ;
}
else
{
currFXToInstanciate - > Instance . setTransformMode ( NL3D : : UTransform : : DirectMatrix ) ;
currFXToInstanciate - > Instance . setMatrix ( currFXToInstanciate - > getInstanceMatrix ( ) ) ;
if ( currFXToInstanciate - > State = = CManagedFX : : Permanent )
{
setupUserParams ( * currFXToInstanciate , 0 ) ;
}
else
{
setupUserParams ( * currFXToInstanciate , currFXToInstanciate - > SetHandle - > Date . Day ) ;
}
currFXToInstanciate - > Instance . freezeHRC ( ) ;
}
}
}
prevFXToInstanciate = currFXToInstanciate ;
currFXToInstanciate = currFXToInstanciate - > _NextInstanciatedFX ;
}
}
// merge toInstanciateListHead & alreadyInstanciatedListHead together
// this becomes the new list of instanciated fxs
if ( prevFXToInstanciate )
{
nlassert ( prevFXToInstanciate - > _NextInstanciatedFX = = NULL ) ;
prevFXToInstanciate - > _NextInstanciatedFX = alreadyInstanciatedListHead ;
if ( alreadyInstanciatedListHead )
{
alreadyInstanciatedListHead - > _PrevInstanciatedFX = & prevFXToInstanciate - > _NextInstanciatedFX ;
}
// set new list
_InstanciatedFXs = toInstanciateListHead ;
toInstanciateListHead - > _PrevInstanciatedFX = & _InstanciatedFXs ;
}
else
{
// set new list
_InstanciatedFXs = alreadyInstanciatedListHead ;
if ( alreadyInstanciatedListHead )
{
alreadyInstanciatedListHead - > _PrevInstanciatedFX = & _InstanciatedFXs ;
}
}
_CandidateFXListTouched = false ;
}
//*******************************************************************************************
void CTimedFXManager : : updateCandidateFXListSorting ( )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
// re-sort all instances by distance
nlassert ( _CandidateFXListSortedByDist . size ( ) = = _CandidateFXListSortedByDistTmp . size ( ) ) ;
2010-05-13 20:45:24 +00:00
uint numDistanceRanges = ( uint ) _CandidateFXListSortedByDist . size ( ) ;
2010-05-06 00:08:41 +00:00
for ( uint k = 0 ; k < numDistanceRanges ; + + k )
{
CManagedFX * currCandidate = _CandidateFXListSortedByDist [ k ] ;
while ( currCandidate )
{
CManagedFX * tmp = currCandidate ;
currCandidate = currCandidate - > _NextCandidateFX ;
float dist = ( tmp - > SpawnPosition - _LastCamPos ) . norm ( ) ;
// unlink & relink in new list
linkCandidateFX ( _CandidateFXListSortedByDistTmp , dist , tmp ) ;
}
}
// swap the 2 list
_CandidateFXListSortedByDist . swap ( _CandidateFXListSortedByDistTmp ) ;
}
//*******************************************************************************************
void CTimedFXManager : : setMaxNumFXInstances ( uint maxNumInstances )
{
FPU_CHECKER
H_AUTO_USE ( RZ_TimedFX )
_MaxNumberOfFXInstances = maxNumInstances ;
_CandidateFXListTouched ;
}
//*******************************************************************************************
// temp, for debug
NLMISC_COMMAND ( dumpFXToAdd , " dumpFXToAdd " , " <> " )
{
if ( ! args . empty ( ) ) return false ;
CTimedFXManager : : getInstance ( ) . dumpFXToAdd ( ) ;
return true ;
}
//*******************************************************************************************
// temp, for debug
NLMISC_COMMAND ( dumpFXToRemove , " dumpFXToRemove " , " <> " )
{
if ( ! args . empty ( ) ) return false ;
CTimedFXManager : : getInstance ( ) . dumpFXToRemove ( ) ;
return true ;
}
//*******************************************************************************************
// temp, for debug
NLMISC_COMMAND ( dumpFXInfo , " dumpFXInfo " , " <> " )
{
if ( ! args . empty ( ) ) return false ;
CTimedFXManager : : getInstance ( ) . dumpFXInfo ( ) ;
return true ;
}
extern class CIGCallback * IGCallbacks ;
//*******************************************************************************************
// temp, for debug
NLMISC_COMMAND ( updateSeason , " updateSeason " , " <> " )
{
if ( ! args . empty ( ) ) return false ;
if ( IGCallbacks ) IGCallbacks - > changeSeason ( ) ;
return true ;
}
//*******************************************************************************************
// temp, for debug
NLMISC_COMMAND ( setMaxNumTimedFXs , " set the max number of timed fx that are visible at a time " , " <numInst> " )
{
if ( args . size ( ) ! = 1 ) return false ;
uint maxNumInstances ;
fromString ( args [ 0 ] , maxNumInstances ) ;
CTimedFXManager : : getInstance ( ) . setMaxNumFXInstances ( maxNumInstances ) ;
return true ;
}