/*==========================================================*\ 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