Files
Leviathan/Client/Game/game/Env/SGameWeatherImpl.hpp
T
2026-06-01 12:46:52 +02:00

521 lines
12 KiB
C++

/*==========================================================*\
file : SGameWeatherImpl.hpp
desc : rappelz weather effect template hpp( rain, snow, etc... )
date : 2007.06.23
author : sonador
history :
\*===========================================================*/
#pragma once
namespace env_fx
{
// ===========================================================
// SGameWeatherImpl
// ===========================================================
TDECL_GAMEWEATHER::SGameWeatherImpl()
: SGameWeather ()
, m_activity ( false )
, m_alive ( false )
, m_skipCollision ( false )
, m_timePrev ( 0 )
, m_time ( 0 )
, m_timeDiff ( 0 )
, m_timeElapsed ( 0 )
, m_timeBeginFade ( 0 )
, m_capacityFactor ( 0 )
, m_capacity ( 0 )
, m_limit ( 0 )
, m_limitCurrent ( 0 )
, m_emitPerMSec ( 0 )
, m_position ( 0, 0, 0 )
, m_target ( 0, 0, 0 )
, m_collisionStrategy ( 0 )
, m_natureInfo ( 0 )
, m_2ndfx ( 0 )
{
Reset();
}
TDECL_GAMEWEATHER::~SGameWeatherImpl()
{
Reset();
}
TDECL_GAMEWEATHER_MEMBER( void )::Reset()
{
m_collisionStrategy = 0;
p_geometry.reset();
m_activity = false ;
m_alive = false ;
m_skipCollision = false ;
m_timePrev = 0 ;
m_time = 0 ;
m_timeDiff = 0 ;
m_timeElapsed = 0 ;
m_timeBeginFade = 0 ;
m_capacityFactor = 0 ;
m_capacity = 0 ;
m_limit = 0 ;
m_limitCurrent = 0 ;
m_emitPerMSec = 0 ;
m_position = K3DVector( 0, 0, 0 );
m_target = K3DVector( 0, 0, 0 );
m_natureInfo = 0 ;
m_area.Reset();
_releaseParticle();
SAFE_DELETE( m_2ndfx );
}
TDECL_GAMEWEATHER_MEMBER( void )::_setAttribute
( const SWeatherAttr& Attr
, float fCapacityFactor )
{
m_capacityFactor = fCapacityFactor;
m_weatherAttr = Attr;
m_area.Set( Attr.area_width, Attr.area_height );
m_area.m_slopeDuration = m_weatherAttr.area_slope_duration;
m_area.m_isSlope = m_weatherAttr.area_slope;
m_capacity = m_weatherAttr.hori_density * m_weatherAttr.hori_density
* m_weatherAttr.vert_density * m_capacityFactor;
}
TDECL_GAMEWEATHER_MEMBER( void )::Init
( K3DRenderDeviceDX* pDevice
, const SWeatherAttr& Attr
, float fCapacityFactor )
{
Reset();
srand( 0 );
_setAttribute( Attr, fCapacityFactor );
// initialize geometry policy
SGeomInitArg geom_arg;
geom_arg.device = pDevice;
geom_arg.count = m_capacity;
geom_arg.resource = m_weatherAttr.resource;
p_geometry.init( geom_arg );
_initParticle();
_create2ndFx( TIntToType< Has2ndFx >() );
}
TDECL_GAMEWEATHER_MEMBER( void )::Process( DWORD dwTime )
{
m_time = dwTime;
if( m_timePrev == 0 )
{
m_timePrev = m_time;
return;
}
m_timeDiff = m_time - m_timePrev;
m_timeElapsed = (float)( m_timeDiff ) * 0.001f;
if( m_timeDiff > 200 )
{
m_timeDiff = 200;
m_timeElapsed = 0.2f;
m_skipCollision = true;
}
else
m_skipCollision = false;
// fader
if( m_time >= m_timeBeginFade &&
m_time < m_timeBeginFade + m_weatherAttr.state_fade_duration )
{
m_limit += _getEmitAmount();
m_limitCurrent = ( m_emitPerMSec > 0 )
? std::min( m_limitCurrent, (float)m_limit )
: std::max( m_limitCurrent, (float)m_limit );
}
else
{
m_limitCurrent = (float)_getLimitParticles();
}
// emit particles
m_area.Update( m_position, m_target, m_time, m_timeDiff, m_weatherAttr );
// procedure
_procParticle( m_time );
m_timePrev = m_time;
// process sub effect
if( true == Has2ndFx )
m_2ndfx->Process( dwTime );
}
TDECL_GAMEWEATHER_MEMBER( void )::Render( KViewportObject* pViewport )
{
SGeomRenderArg< particle_t > arg(
m_time, m_timeDiff, m_limitCurrent, m_particles,
pViewport, m_weatherAttr, m_area, m_skipCollision
);
m_alive = p_geometry.render( arg );
if( true == Has2ndFx ) m_2ndfx->Render( pViewport );
}
TDECL_GAMEWEATHER_MEMBER( void )::_initParticle()
{
_releaseParticle();
if( m_capacity <= 0 )
return;
m_particles.reserve( m_capacity );
float fX, fY, fZ, fVDensity, fHDensity;
fVDensity = m_weatherAttr.area_height / m_weatherAttr.vert_density;
fHDensity = m_weatherAttr.area_width / m_weatherAttr.hori_density;
for( fZ = m_area.m_min.z; fZ < m_area.m_max.z; fZ += fVDensity )
{
for( fY = m_area.m_min.y; fY < m_area.m_max.y; fY += fHDensity )
{
for( fX = m_area.m_min.x; fX < m_area.m_max.x; fX += fHDensity )
{
particle_t* particle = new particle_t();
_initParticleAttribute(
m_time,
K3DVector(
fX + (float)( rand() % 100 ) * 0.01f * fHDensity,
fY + (float)( rand() % 100 ) * 0.01f * fHDensity,
fZ + (float)( rand() % 100 ) * 0.01f * fVDensity
),
*particle
);
// no collision at first time
particle->collidable = false;
m_particles.push_back( particle );
}
}
}
std::random_shuffle( m_particles.begin(), m_particles.end() );
}
TDECL_GAMEWEATHER_MEMBER( void )::_releaseParticle()
{
env_fx::util::wipe_seq( m_particles );
}
TDECL_GAMEWEATHER_MEMBER( void )::_procParticle( DWORD dwTime )
{
// update particles
int nParticleCnt = ::min( m_capacity, (int)m_particles.size() );
if( nParticleCnt <= 0 ) return;
int nLimitCnt = 0;
K3DVectorTransform(
m_localCamera,
p_geometry.m_camera.GetPosVector(),
p_geometry.m_worldInverse
);
for( int index = 0; index < nParticleCnt; ++index )
{
particle_t& particle = *m_particles[ index ];
if( !m_area.IsInArea( particle.position ) )
_swapParticle( particle, m_time );
_updateParticle( particle );
if( nLimitCnt < m_limitCurrent && particle.collidable &&
!particle.collided )
{
K3DVector collisionPoint;
if( _getCollisionPosition( collisionPoint, particle ) )
{
particle.collided = true;
// has collided!! add particle to 2nd fx
_call2ndFx( collisionPoint );
}
}
nLimitCnt++;
}
}
TDECL_GAMEWEATHER_MEMBER( void )::_initParticleAttribute
( DWORD time
, const K3DVector& position
, particle_t& particle )
{
particle.collidable = ( env_fx::util::GetRandomNumberInRange( 0.0f, 1.0f )
<= m_weatherAttr.collision_factor ) ? true : false;
particle.collided = false;
particle.velocity = m_weatherAttr.particle_velocity;
particle.position = position;
particle.positionBirth = position;
particle.positionPrev = position;
if( particle.collidable )
particle.normal = _getNormalFromTerrain( particle );
particle.birth = time;
particle.timePrev = time;
particle.life = 0;
particle.fadeoutDuration = m_weatherAttr.destroy_duration;
particle.rotationUnit = m_weatherAttr.rotation_unit;
particle.rotationUnitX = (float)( rand() % 100 ) / 100.0f;
particle.rotationSpeed = m_weatherAttr.rotation_speed;
particle.visibility = 1.0f;
particle.alive = true;
}
TDECL_GAMEWEATHER_MEMBER( void )::_swapParticle( particle_t& particle, DWORD dwTime )
{
_initParticleAttribute( dwTime, m_area.GetSwapEmitPoint( particle.positionPrev ), particle );
}
TDECL_GAMEWEATHER_MEMBER( bool )::_updateParticle( particle_t& particle )
{
particle_t::SUpdateArgument arg( m_time, m_timeDiff, m_weatherAttr, m_area, m_localCamera );
return p_particle( particle, arg );
}
TDECL_GAMEWEATHER_MEMBER( void )::_create2ndFx( TIntToType< false > )
{
}
TDECL_GAMEWEATHER_MEMBER( void )::_create2ndFx( TIntToType< true > )
{
m_2ndfx = new ty2ndFx();
m_2ndfx->Init( p_geometry.m_device, *m_weatherAttr.linked_attr );
}
TDECL_GAMEWEATHER_MEMBER( bool )::_getCollisionPosition
( K3DVector& out
, const particle_t& particle )
{
bool bCollided = false;
// setting end position by collision detection
if( m_collisionStrategy )
{
K3DVector wld_pos;
// send particle to world coordinate
K3DVectorTransform( wld_pos, particle.position, p_geometry.m_world );
// get collision point in world coordinate
if( m_collisionStrategy->GetCollidePosition(
&out.x, wld_pos.x, wld_pos.y, wld_pos.z ) )
{
// found collision point
if( out.z >= wld_pos.z )
bCollided = true;
}
}
return bCollided;
}
TDECL_GAMEWEATHER_MEMBER( K3DVector )::_getNormalFromTerrain
( const particle_t& particle )
{
K3DVector _out( 0, 0, 1 );
if( m_collisionStrategy )
{
K3DVector wld_pos;
// send particle to world coordinate
K3DVectorTransform( wld_pos, particle.position, p_geometry.m_world );
m_collisionStrategy->GetCollideNormal(
&_out.x, &_out.y, &_out.z, wld_pos.x, wld_pos.y, 0.0f );
}
return _out;
}
TDECL_GAMEWEATHER_MEMBER( void )::_call2ndFx( const K3DVector& vEmitPos ) const
{
if( Has2ndFx == true )
{
m_2ndfx->EmitAt( vEmitPos );
}
}
TDECL_GAMEWEATHER_MEMBER( int )::GetParticleCnt() const
{
return (int)m_particles.size();
}
TDECL_GAMEWEATHER_MEMBER( void )::SetActivity
( DWORD dwTime
, bool bActive
, float fAmountFactor )
{
m_activity = bActive;
int nLimit = static_cast< int >( fAmountFactor * m_capacity );
if( m_activity )
{
if( nLimit == 0 ) m_limit = m_capacity;
else m_limit = ::max( ::min( nLimit, m_capacity ), 0 );
}
else
{
m_limit = 0;
}
m_timeBeginFade = dwTime;
m_emitPerMSec = (float)( m_limit - (int)m_limitCurrent )
/ (float)m_weatherAttr.state_fade_duration;
}
TDECL_GAMEWEATHER_MEMBER( float )::GetEmitRate() const
{
return m_emitPerMSec;
}
TDECL_GAMEWEATHER_MEMBER( void )::SetViewMatrix( const K3DMatrix& mView )
{
p_geometry.setViewMatrix( mView );
if( Has2ndFx == true )
m_2ndfx->SetViewMatrix( mView );
}
TDECL_GAMEWEATHER_MEMBER( void )::SetPosition( const K3DVector& vAreaPos
, const K3DVector& vTargetPos )
{
m_position = vAreaPos;
m_target = vTargetPos;
p_geometry.setPosition( m_position );
if( Has2ndFx == true )
m_2ndfx->SetPosition( vAreaPos, vTargetPos );
}
// ===========================================================
// SGameWeather2ndFx
// ===========================================================
TDECL_GAMEWEATHER_2NDFX_MEMBER( void )::Reset()
{
parent_type::Reset();
m_nCurrentAliveCnt = 0;
}
TDECL_GAMEWEATHER_2NDFX_MEMBER( void )::Process( DWORD dwTime )
{
m_time = dwTime;
if( m_timePrev == 0 )
{
m_timePrev = m_time;
return;
}
m_timeDiff = m_time - m_timePrev;
m_timeElapsed = (float)( m_timeDiff ) * 0.001f;
if( m_timeDiff > 200 )
{
m_timeDiff = 200;
m_timeElapsed = 0.2f;
}
m_area.Update( m_position, m_target, m_time, m_timeDiff, m_weatherAttr );
_procParticle( m_time );
m_timePrev = m_time;
}
TDECL_GAMEWEATHER_2NDFX_MEMBER( void )::_procParticle( DWORD dwTime )
{
// update particles
particles_t::size_type nParticleCnt = m_particles.size();
int nLimitCnt = 0;
if( nParticleCnt > 0 )
{
for( int index = 0; index < m_capacity; ++index )
{
particle_t* pParticle = m_particles[ index ];
if( pParticle->alive )
{
if( !_updateParticle( *pParticle ) )
{
m_nCurrentAliveCnt--;
}
}
}
}
}
TDECL_GAMEWEATHER_2NDFX_MEMBER( void )::_initParticle()
{
_releaseParticle();
if( m_capacity <= 0 )
return;
m_particles.reserve( m_capacity );
for( int n = 0; n < m_capacity; ++n )
{
particle_t* pNew = new particle_t();
m_particles.push_back( pNew );
}
m_limitCurrent = (float)m_particles.size();
}
TDECL_GAMEWEATHER_2NDFX_MEMBER( void )::EmitAt( const K3DVector& position )
{
if( m_nCurrentAliveCnt < m_capacity )
{
particles_t::iterator i = std::find_if( m_particles.begin(),
m_particles.end(),
SDeadParticleGettor() );
if( i != m_particles.end() )
{
particle_t& particle = **i;
if( particle.alive == false )
{
_initParticleAttribute( m_time, position, particle );
m_nCurrentAliveCnt++;
}
}
}
}
TDECL_GAMEWEATHER_2NDFX_MEMBER( void )::SetPosition
( const K3DVector& vAreaPos
, const K3DVector& vTargetPos )
{
m_position = vAreaPos;
m_target = vTargetPos;
// second fx works on world coordinate, so
// we need to make world matrix be identity matrix.
if( Has2ndFx == true )
m_2ndfx->SetPosition( vAreaPos, vTargetPos );
}
} // namespace env_fx