521 lines
12 KiB
C++
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
|