/**==========================================================*\ @file : SGameWeather.h @brief : definitions of base classes and utilities for weather effect @date : 2007.03.xx @author : sonador \*===========================================================*/ #pragma once #include #include "K3DTypes.h" #include "KSeqModel.h" #include "SGameWeatherAttr.h" #if defined( _DEBUG ) #define __smartinline #else #define __smartinline __inline #endif class SGame; /// [sonador] namespace env_fx namespace env_fx { /// [sonador] namespace 'util' namespace util { template< class _Ct > inline void wipe_seq( _Ct& container ) { typedef typename _Ct::iterator iterator; iterator end = container.end(); for( iterator itor = container.begin(); itor != end; ++itor ) if( *itor ) delete *itor; container.clear(); } template< typename _Ty > inline void wipe_seq( std::queue< _Ty >& container ) { typedef typename std::queue< _Ty >::value_type value_t; while( !container.empty() ) { value_t value = container.front(); container.pop(); delete value; } } template< typename _Ty > inline void wipe_seq( std::deque< _Ty >& container ) { typedef typename std::deque< _Ty >::value_type value_t; while( !container.empty() ) { value_t value = container.back(); container.pop_back(); delete value; } } template< typename _Ty > __smartinline void compact_vector( std::vector< _Ty >& container ) { std::vector< _Ty >().swap( container ); } template< typename T > inline T lerp( const T& a, const T& b, const T& A, const T& C, const T& B ) { return a + ( C - A ) * ( b - a ) / ( B - A ); } template<> inline int lerp< int >( const int& a, const int& b, const int& A, const int& C, const int& B ) { return (int)( a + ( C - A ) * (float)( b - a ) / (float)( B - A ) ); } /// get random position between fMin and fMax inline K3DVALUE GetRandomNumberInRange( const K3DVALUE fMin, const K3DVALUE fMax ) { K3DVALUE fRandNum = (float)rand() / 0x7fff; fRandNum = fMin + ( fMax - fMin ) * fRandNum; return fRandNum; } inline K3DMatrix GetRotationMatrixByNormal( const K3DVector& normal , const K3DVector& vNewNormal ) { K3DMatrix mRot; K3DVector vAxis = CrossProduct( normal, vNewNormal ); float fAngle = ::acosf( DotProduct( normal, vNewNormal ) ); /// update matrix of rainy box K3DMatrixRotationAxis( mRot, vAxis, fAngle ); return mRot; } } // namespace 'util' // ==================================================================== // geometries // ==================================================================== struct K3DVERTEX_WEATHER { enum { FVF = K3DFVF_XYZ | K3DFVF_NORMAL | K3DFVF_DIFFUSE | K3DFVF_TEX1 }; K3DVERTEX_WEATHER() { ::ZeroMemory( this, sizeof( *this ) ); } K3DVertex local; K3DVertex pos; KColor diffuse; K3DTexel texel; }; typedef WORD K3DINDEX_WEATHER; struct SNatureEnvInfo { K3DVector m_vWind; ///< 바람 K3DVector m_vGravity; ///< 중력 }; class ICollisionStrategy { public: virtual ~ICollisionStrategy() { } virtual bool GetCollidePosition( float* out, float x, float y, float z ) = 0; virtual bool GetCollideNormal( float* out_x, float* out_y, float* out_z, float x, float y, float z ) = 0; }; template< typename _ParticleType > struct ParticleSystem { typedef _ParticleType particle_t; class ParticleOp { public: virtual ~ParticleOp() {} virtual void prepare( DWORD time ) {} virtual void operator()( particle_t& particle ) = 0; }; class ParticleOpUnit : public ParticleOp { public: typedef ParticleOp particle_op_t; typedef std::vector< particle_op_t* > particle_op_container_t; typedef typename particle_op_container_t::iterator particle_op_iterator_t; ~ParticleOpUnit() { clear(); } void append( particle_op_t* particleOp ) { mParticleOps.push_back( particleOp ); } void clear() { util::wipe_seq( mParticleOps ); } void prepare( DWORD time ) { std::for_each( mParticleOps.begin(), mParticleOps.end(), std::bind2nd( std::mem_fun( &particle_op_t::prepare ), time ) ); } void operator()( particle_t& particle ) { //std::for_each( mParticleOps.begin(), mParticleOps.end(), std::bind2nd( std::mem_fun( &particle_op_t::operator() ), particle ) ); particle_op_iterator_t it = mParticleOps.begin(), it_end = mParticleOps.end(); for( ; it != it_end; ++it ) (**it)( particle ); } private: particle_op_container_t mParticleOps; }; class ParticleBundle { public: typedef std::vector< particle_t* > particle_vector_t; typedef typename particle_vector_t::iterator particle_iterator_t; typedef typename particle_vector_t::size_type size_type; ParticleBundle( ParticleOp* initor, ParticleOp* action ) : mInitor( initor ), mAction( action ), mIndex( 0 ) { } ~ParticleBundle() { clear(); SAFE_DELETE( mInitor ); SAFE_DELETE( mAction ); } particle_t* operator [] ( size_type n ) { assert( n >= 0 && n < size() ); return mParticles[ n ]; } const particle_t* operator [] ( size_type n ) const { assert( n >= 0 && n < size() ); return mParticles[ n ]; } void initialize( size_type _size ) { clear(); mParticles.reserve( _size ); for( size_type n = 0; n < _size; ++n ) mParticles.push_back( new particle_t() ); } void clear() { util::wipe_seq( mParticles ); util::compact_vector( mParticles ); mIndex = 0; } bool empty() const { return mParticles.empty(); } size_type size() const { return mParticles.size(); } size_type count() const { return mIndex; } particle_iterator_t begin() { return mParticles.begin(); } particle_iterator_t end() { return mParticles.end(); } void emit( size_type amount ) { if( ( count() + amount ) > size() ) { amount = size() - count(); } if( amount > 0 ) { size_type n = mIndex; mIndex += amount; for( ; n < mIndex; ++n ) { ( *mInitor )( *mParticles[ n ] ); } } } void update( DWORD time ) { ( *mAction ).prepare( time ); for( size_type n = 0; n < mIndex; ) { ( *mAction )( *mParticles[ n ] ); if( mParticles[ n ]->life <= 0 ) { std::swap( mParticles[ n ], mParticles[ mIndex - 1 ] ); --mIndex; } else { ++n; } } } private: ParticleOp* mInitor; ParticleOp* mAction; size_type mIndex; particle_vector_t mParticles; }; class ParticleResource { public: typedef ParticleBundle particle_bundle_t; // constructor & destructor ParticleResource( const std::string& name ) : mName( name ) {} virtual ~ParticleResource() {} // abstract method virtual void initialize( int capacity ) = 0; virtual void release() = 0; virtual void render( DWORD time, KViewportObject* viewport, const particle_bundle_t& particles ) = 0; virtual void locate( const K3DVector& position ) = 0; protected: std::string mName; }; }; // struct ParticleSystem // ==================================================================== /// weather particle attribute // ==================================================================== struct SParticle { SParticle(); void Reset(); bool IsCollidableCollided() const; K3DVector position; ///< 위치 K3DVector positionPrev; ///< 이전 위치 K3DVector velocity; ///< 속도 K3DColor color; ///< 색 K3DVALUE visibility; K3DPoint uvLT; ///< UV 좌상단 K3DPoint uvRB; ///< UV 우하단 K3DVector normal; ///< normal int birth; ///< 생성 시간 int life; ///< 총 활성 시간 float width; float height; DWORD fadeoutDuration; //! float rotationUnit; ///< 회전성분 float rotationUnitX; ///< 회전성분 float rotationSpeed; ///< 회전성분( 속도 ) bool collidable; ///< 충돌 가능 여부 bool collided; ///< bool alive; ///< 활성 여부 }; typedef ParticleSystem< SParticle > particle_system_t; typedef particle_system_t::particle_t particle_t; typedef particle_system_t::ParticleOp particle_op_t; typedef particle_system_t::ParticleOpUnit particle_op_unit_t; typedef particle_system_t::ParticleBundle particle_bundle_t; typedef particle_system_t::ParticleResource particle_resource_t; /// life struct LifeFixedInitor : public particle_op_t { LifeFixedInitor( DWORD life ) : mLife( life ) {} virtual ~LifeFixedInitor() {} virtual void operator()( particle_t& particle ) { particle.life = mLife; particle.birth = mLife; } DWORD mLife; }; struct LifeDecreaseAction : public particle_op_t { LifeDecreaseAction() : mTime( 0 ), mTimeDiff( 0 ) {} virtual ~LifeDecreaseAction() {} virtual void prepare( DWORD time ) { if( mTime == 0 ) mTime = time; mTimeDiff = time - mTime; mTime = time; } virtual void operator()( particle_t& particle ) { particle.life -= mTimeDiff; } DWORD mTime; DWORD mTimeDiff; }; struct LifeDieAfterCollisionAction : public particle_op_t { void operator()( particle_t& particle ) { if( particle.collided == true ) particle.alive = false; } }; /// velocity struct VelocityFixedInitor : public particle_op_t { VelocityFixedInitor( const K3DVector& velocity ) : mVelocity( velocity ) {} virtual void operator()( particle_t& particle ) { particle.velocity = mVelocity; } K3DVector mVelocity; }; struct VelocityAction : public particle_op_t { VelocityAction() : mTime( 0 ), mTimeDiff( 0 ) {} void prepare( DWORD time ) { if( mTime == 0 ) mTime = time; mTimeDiff = time - mTime; mTime = time; } void operator()( particle_t& particle ) { particle.positionPrev = particle.position; particle.position += particle.velocity * (float)mTimeDiff * 0.001f; } DWORD mTime; DWORD mTimeDiff; }; struct VelocityReactiveAction : public particle_op_t { VelocityReactiveAction( K3DVector& target ) : mTarget( target ), mPrevPosition( target ) {} void prepare( DWORD time ) { mReaction = mPrevPosition - mTarget; mPrevPosition = mTarget; } void operator()( particle_t& particle ) { particle.position += mReaction; } K3DVector& mTarget; K3DVector mPrevPosition; K3DVector mReaction; }; /// rotate struct RotationInitor : public particle_op_t { RotationInitor( float rotationUnit, float rotationSpeed ) : mRotationUnit( rotationUnit ), mRotationSpeed( rotationSpeed ) {} void operator()( particle_t& particle ) { particle.rotationUnit = mRotationUnit; particle.rotationUnitX = (float)( rand() % 100 ) / 100.0f; particle.rotationSpeed = mRotationSpeed; } float mRotationUnit; float mRotationSpeed; }; struct RotationAction : public particle_op_t { RotationAction() : mTime( 0 ) {} void prepare( DWORD time ) { mTime = time; } void operator()( particle_t& particle ) { K3DVector rotation; float fTheta = ( float )( particle.birth - particle.life ) / 1000.0f * particle.rotationSpeed; float xy = cosf( fTheta ) * particle.rotationUnit; float z = sin( fTheta ) * particle.rotationUnit; rotation.x = xy * particle.rotationUnitX; rotation.y = xy * ( 1.0f - particle.rotationUnitX ); rotation.z = z; particle.position += rotation; } DWORD mTime; }; /// position struct PositionFixedDistributionInitor : public particle_op_t { private: struct _RECT { float minx; float miny; float maxx; float maxy; }; public: PositionFixedDistributionInitor( float width, int density, float emitZ ) : mIndex( 0 ), mEmitZ( emitZ ) { float offset = width / density; float half = width / 2.0f; int yCount = 0; for( float y = -half; yCount < density; y += offset, ++yCount ) { int xCount = 0; for( float x = -half; xCount < density; x += offset, ++xCount ) { _RECT* rect = new _RECT; rect->minx = x; rect->miny = y; rect->maxx = x + offset; rect->maxy = y + offset; mRects.push_back( rect ); } } std::random_shuffle( mRects.begin(), mRects.end() ); } ~PositionFixedDistributionInitor() { util::wipe_seq( mRects ); } void operator()( particle_t& particle ) { if( mRects.empty() ) return; if( mIndex >= mRects.size() ) mIndex = 0; _RECT* rect = mRects.at( mIndex ); particle.position.x = util::GetRandomNumberInRange( rect->minx, rect->maxx ); particle.position.y = util::GetRandomNumberInRange( rect->miny, rect->maxy ); particle.position.z = mEmitZ; ++mIndex; } std::vector< _RECT* >::size_type mIndex; std::vector< _RECT* > mRects; float mEmitZ; }; struct PositionNormalFixedDistributionOnTerrainInitor : public particle_op_t { private: struct _RECT { float minx; float miny; float maxx; float maxy; }; public: PositionNormalFixedDistributionOnTerrainInitor( ICollisionStrategy* collision, float width, int density, K3DVector& target ) : mCollision( collision ), mIndex( 0 ), mTarget( target ) { float offset = width / density; float half = width / 2.0f; int yCount = 0; for( float y = -half; yCount < density; y += offset, ++yCount ) { int xCount = 0; for( float x = -half; xCount < density; x += offset, ++xCount ) { _RECT* rect = new _RECT; rect->minx = x; rect->miny = y; rect->maxx = x + offset; rect->maxy = y + offset; mRects.push_back( rect ); } } std::random_shuffle( mRects.begin(), mRects.end() ); } ~PositionNormalFixedDistributionOnTerrainInitor() { util::wipe_seq( mRects ); } void operator()( particle_t& particle ) { bool result = false; if( !mRects.empty() ) { if( mIndex >= mRects.size() ) mIndex = 0; _RECT* rect = mRects.at( mIndex ); particle.position.x = util::GetRandomNumberInRange( rect->minx, rect->maxx ); particle.position.y = util::GetRandomNumberInRange( rect->miny, rect->maxy ); particle.position.z = 0; K3DVector terrainPosition( 0, 0, 0 ); K3DVector terrainNormal( 0, 0, 1 ); if( mCollision ) { K3DVector worldPosition( particle.position + mTarget ); // send particle to world coordinate // get position if( mCollision->GetCollidePosition( &terrainPosition.x, worldPosition.x, worldPosition.y, worldPosition.z ) ) { // get normal mCollision->GetCollideNormal( &terrainNormal.x, &terrainNormal.y, &terrainNormal.z, worldPosition.x, worldPosition.y, 0.0f ); particle.position = terrainPosition - mTarget; particle.normal = terrainNormal; result = true; } } ++mIndex; } if( result == false ) particle.life = 0; } ICollisionStrategy* mCollision; std::vector< _RECT* >::size_type mIndex; std::vector< _RECT* > mRects; K3DVector& mTarget; }; struct PositionSwapAction : public particle_op_t { PositionSwapAction( float xBound, float yBound ) : mXBound( xBound ), mYBound( yBound ) {} void operator()( particle_t& particle ) { // sonador 1.1.19 파티클 스왑 오류 수정 float xOffset = 0, yOffset = 0; if( particle.position.x < -mXBound ) { xOffset = particle.position.x + mXBound; particle.position.x = mXBound + xOffset; } else if( particle.position.x >= mXBound ) { xOffset = particle.position.x - mXBound; particle.position.x = -mXBound + xOffset; } if( particle.position.y < -mYBound ) { yOffset = particle.position.y + mYBound; particle.position.y = mYBound + yOffset; } else if( particle.position.y >= mYBound ) { yOffset = particle.position.y - mYBound; particle.position.y = -mYBound + yOffset; } } float mXBound; float mYBound; }; // visibility struct VisibilityFixedInitor : public particle_op_t { VisibilityFixedInitor( float visibility ) : mVisibility( visibility ) {} void operator()( particle_t& particle ) { particle.visibility = mVisibility; } float mVisibility; }; struct VisibilityFadeOutAction { void operator()( particle_t& particle ) { particle.visibility = (float)particle.life / particle.birth; } }; struct VisibilityFadeInOutAction : public particle_op_t { VisibilityFadeInOutAction( int fadeTime ) : mFadeTime( fadeTime ) {} void operator()( particle_t& particle ) { if( particle.life > particle.birth - mFadeTime ) particle.visibility = 1.0f - (float)( particle.life - ( particle.birth - mFadeTime ) ) / mFadeTime; else if( particle.life > mFadeTime ) particle.visibility = 1.0f; else particle.visibility = (float)particle.life / mFadeTime; } int mFadeTime; }; struct VisibilityFadeInOutOffsetAdditiveAction : public particle_op_t { VisibilityFadeInOutOffsetAdditiveAction( float width, float offset ) : mWidth( width / 2 - offset ), mOffset( offset ) {} void operator()( particle_t& particle ) { float offset = mOffset; if( ::abs( particle.position.x ) > offset ) offset = ::abs( particle.position.x ); if( ::abs( particle.position.y ) > offset ) offset = ::abs( particle.position.y ); if( offset > mOffset ) { offset -= mOffset; float factor = 1.0f - offset / mWidth; // additive particle.visibility *= factor; } } float mWidth; float mOffset; }; /// size struct SizeFixedInitor : public particle_op_t { SizeFixedInitor( float width, float height ) : mWidth( width ), mHeight( height ) {} void operator()( particle_t& particle ) { particle.width = mWidth; particle.height = mHeight; } float mWidth; float mHeight; }; /// color struct ColorFixedInitor : public particle_op_t { ColorFixedInitor( const K3DColor& color ) : mColor( color ) {} void operator()( particle_t& particle ) { particle.color = mColor; } K3DColor mColor; }; template< typename _VertexType, typename _IndexType > class BillboardTextureResource : public particle_resource_t { public: typedef _VertexType vertex_t; typedef _IndexType index_t; enum { Offset = 4, Stride = sizeof( vertex_t ), FVF = vertex_t::FVF, }; protected: K3DTextureSPtr mTexture; ///< 효과 텍스쳐 K3DMaterial* mMtrl; ///< 재질값 vertex_t* mVtxBuffer; ///< VB index_t* mIdxBuffer; ///< IB SWeatherTexturedPrimitive* mPrimitive; ///< primitive K3DVector mPosition; ///< 월드 위치 K3DMatrix mWorld; ///< 날씨효과 시스템의 월드 변환 행렬 K3DMatrix mView; ///< 카메라 뷰 행렬 K3DMatrix mCamera; ///< 카메라 월드 행렬 K3DMatrix mBillboard; ///< 빌보드 행렬 K3DRenderDevice* mDevice; ///< Render Device public: BillboardTextureResource( const std::string& name, K3DRenderDevice* device ) : particle_resource_t ( name ) , mDevice ( device ) , mTexture ( 0 ) , mMtrl ( 0 ) , mVtxBuffer ( 0 ) , mIdxBuffer ( 0 ) , mPrimitive ( 0 ) { release(); } virtual ~BillboardTextureResource() { release(); } virtual void initialize( int capacity ) { release(); // load resource NX3LoadPack loadpack; loadpack.Init(); if( mName.length() > 0 ) { mTexture = KTextureManager::GetManager()->GetTexture( mName.c_str(), &loadpack, true, KTextureManager::GetManager()->GetMipMapBiasLevel() ); assert( mTexture && "weather texture was not loaded!!" ); } // create buffer if( capacity > 0 ) { mVtxBuffer = new vertex_t[ capacity * Offset ]; ::memset( mVtxBuffer, 0, sizeof( vertex_t ) * capacity * Offset ); mIdxBuffer = new index_t[ capacity * 6 ]; for( int nIdx = 0; nIdx < capacity; ++nIdx ) { mIdxBuffer[ nIdx * 6 + 0 ] = nIdx * 4 + 2; mIdxBuffer[ nIdx * 6 + 1 ] = nIdx * 4 + 1; mIdxBuffer[ nIdx * 6 + 2 ] = nIdx * 4 + 0; mIdxBuffer[ nIdx * 6 + 3 ] = nIdx * 4 + 1; mIdxBuffer[ nIdx * 6 + 4 ] = nIdx * 4 + 2; mIdxBuffer[ nIdx * 6 + 5 ] = nIdx * 4 + 3; } } // create primitive mPrimitive = new SWeatherTexturedPrimitive(); if( mPrimitive ) { mMtrl = new K3DMaterial; mMtrl->SetAmbient( K3DColor( 1.f, 1.f, 1.f, 1.f ) ); mMtrl->SetDiffuse( K3DColor( 1.f, 1.f, 1.f, 1.f ) ); mMtrl->SetSpecular( K3DColor( 0.f, 0.f, 0.f, 1.f ) ); mMtrl->SetSpecularPower( 1.0f ); K3DMatrix mParent; K3DMatrixIdentity( mParent ); mPrimitive->SetBlendMode( K3DMaterial::MBM_ADDITIVE ); mPrimitive->SetTransparent( true ); mPrimitive->SetTransform( &mWorld, &mParent ); mPrimitive->SetMaterial( mMtrl ); mPrimitive->SetTexture( 0, mTexture ); mPrimitive->SetCommonVertex( mVtxBuffer ); mPrimitive->SetCommonIndexed( mIdxBuffer ); mPrimitive->SetVertexFmt( FVF ); mPrimitive->SetVertexStride( Stride ); } } virtual void render( DWORD time, KViewportObject* viewport, const particle_bundle_t& particles ) { if( particles.empty() || particles.count() == 0 || mVtxBuffer == 0 || mIdxBuffer == 0 ) return; mView = *viewport->GetViewMatrix(); K3DMatrixInverse( mCamera, mView ); prepare(); vertex_t* vertex = mVtxBuffer; particle_bundle_t::size_type particleCount = particles.count(); for( particle_bundle_t::size_type index = 0; index < particleCount; ++index ) { const particle_t& particle = *particles[ index ]; primitive( vertex, particle ); vertex += Offset; } mPrimitive->SetVertexCnt( particleCount * Offset ); viewport->Register( mPrimitive, KRenderObject::RENDEREFX_WEATHER ); } virtual void release() { SAFE_DELETE_ARRAY( mVtxBuffer ); SAFE_DELETE_ARRAY( mIdxBuffer ); SAFE_DELETE( mPrimitive ); SAFE_DELETE( mMtrl ); mTexture = 0; mPosition.Set( 0, 0, 0 ); K3DMatrixIdentity( mWorld ); K3DMatrixIdentity( mView ); K3DMatrixIdentity( mCamera ); K3DMatrixIdentity( mBillboard ); } virtual void locate( const K3DVector& position ) { mPosition = position; mPrimitive->SetAreaPosition( position ); K3DMatrixIdentity( mWorld ); mWorld.SetPosVector( mPosition ); } protected: virtual void prepare() { K3DMatrixTranspose( mBillboard, mView ); K3DMatrixIdentity( mWorld ); mWorld._11 = mBillboard._11; mWorld._12 = mBillboard._12; mWorld._13 = mBillboard._13; mWorld._21 = mBillboard._31; mWorld._22 = mBillboard._32; mWorld._23 = mBillboard._33; mWorld._31 = mBillboard._21; mWorld._32 = mBillboard._22; mWorld._33 = mBillboard._23; } virtual void primitive( vertex_t* vertices, const particle_t& particle ) { float fHalfWidth = particle.width * 0.5f; float fHalfHeight = particle.height * 0.5f; KColor diffuse = (KColor)particle.color; diffuse.a = (unsigned char)( particle.color.a * particle.visibility * 255.f ); vertices[ 0 ].local = K3DVector( -fHalfWidth, 0, fHalfHeight ); vertices[ 0 ].pos = particle.position; vertices[ 0 ].diffuse = diffuse; vertices[ 0 ].texel.u = particle.uvLT.x; vertices[ 0 ].texel.v = particle.uvLT.y; vertices[ 1 ].local = K3DVector( fHalfWidth, 0, fHalfHeight ); vertices[ 1 ].pos = particle.position; vertices[ 1 ].diffuse = diffuse; vertices[ 1 ].texel.u = particle.uvRB.x; vertices[ 1 ].texel.v = particle.uvLT.y; vertices[ 2 ].local = K3DVector( -fHalfWidth, 0, -fHalfHeight ); vertices[ 2 ].pos = particle.position; vertices[ 2 ].diffuse = diffuse; vertices[ 2 ].texel.u = particle.uvLT.x; vertices[ 2 ].texel.v = particle.uvRB.y; vertices[ 3 ].local = K3DVector( fHalfWidth, 0, -fHalfHeight ); vertices[ 3 ].pos = particle.position; vertices[ 3 ].diffuse = diffuse; vertices[ 3 ].texel.u = particle.uvRB.x; vertices[ 3 ].texel.v = particle.uvRB.y; } }; template< typename _VertexType, typename _IndexType > class ZAxisBillboardTextureResource : public BillboardTextureResource< _VertexType, _IndexType > { public: ZAxisBillboardTextureResource( const std::string& name, K3DRenderDevice* device, K3DVector& vestige ) : BillboardTextureResource( name, device ) , mVestige( vestige ) { } protected: virtual void prepare() { K3DMatrixIdentity( mBillboard ); mBillboard._11 = mView._11; mBillboard._12 = mView._12; mBillboard._21 = mView._21; mBillboard._22 = mView._22; K3DMatrixTranspose( mBillboard, mBillboard ); mWorld = mBillboard; // prepare shader constant mPrimitive->SetAreaVelocity( mVestige ); } virtual void primitive( vertex_t* vertices, const particle_t& particle ) { float fHalfWidth = particle.width * 0.5f; KColor diffuse = (KColor)particle.color; diffuse.a = (unsigned char)( particle.color.a * particle.visibility * 255.f ); vertices[ 0 ].local = K3DVector( -fHalfWidth, 0, particle.height ); vertices[ 0 ].pos = particle.position; vertices[ 0 ].diffuse = diffuse; vertices[ 0 ].texel.u = 0; vertices[ 0 ].texel.v = 0; vertices[ 1 ].local = K3DVector( fHalfWidth, 0, particle.height ); vertices[ 1 ].pos = particle.position; vertices[ 1 ].diffuse = diffuse; vertices[ 1 ].texel.u = 0; vertices[ 1 ].texel.v = 1; vertices[ 2 ].local = K3DVector( -fHalfWidth, 0, 0 ); vertices[ 2 ].pos = particle.position; vertices[ 2 ].diffuse = diffuse; vertices[ 2 ].texel.u = 1; vertices[ 2 ].texel.v = 0; vertices[ 3 ].local = K3DVector( fHalfWidth, 0, 0 ); vertices[ 3 ].pos = particle.position; vertices[ 3 ].diffuse = diffuse; vertices[ 3 ].texel.u = 1; vertices[ 3 ].texel.v = 1; } K3DVector& mVestige; }; class NX3Resource : public particle_resource_t { public: NX3Resource( const std::string& name ) : particle_resource_t( name ), mModel( 0 ) { reset(); } ~NX3Resource() { release(); } virtual void initialize( int capacity ) { // create models mModel = new KSeqModel[ capacity ]; // load animation NX3LoadPack loadpack; loadpack.Init(); for( int index = 0; index < capacity; ++index ) { mModel[ index ].AddAnimation( "default", mName.c_str(), KNX3Manager::SEQTYPE_ALL, &loadpack ); } } virtual void render( DWORD time, KViewportObject* viewport, const particle_bundle_t& particles ) { if( particles.count() == 0 ) return; if( mModel ) { int nSize = particles.count(); for( int index = 0; index < nSize; ++index ) { const particle_t& particle = *particles[ index ]; KSeqModel& model = mModel[ index ]; if( !model.IsPlaying() ) { K3DVector vAxis( 0, 0, 0 ), vUp( 0, 0, 1 ); float fCos, fAngle; K3DVectorCross( vAxis, vUp, particle.normal ); fCos = K3DVectorDot( vUp, particle.normal ) / particle.normal.Magnitude(); fAngle = ::acosf( fCos ); K3DMatrixRotationAxis( mWorld, vAxis, fAngle ); mWorld.SetPosVector( mPosition + particle.position ); K3DMatrix mScl; K3DMatrixScaling( mScl, 1, 1, 1 ); K3DMatrixMultiply( mWorld, mScl, mWorld ); model.SetTransform( mWorld ); model.PlayAnimation( time, "default", KSeqModel::SEQTYPE_NORMAL ); } model.SetVisibility( particle.visibility ); model.SetSkinDiffuse( (KColor)particle.color ); model.Process( time ); if( model.IsPlaying() ) model.Render( viewport ); } } } virtual void release() { SAFE_DELETE_ARRAY( mModel ); } virtual void locate( const K3DVector& position ) { mPosition = position; mWorld.SetPosVector( mPosition ); } private: KSeqModel* mModel; K3DVector mPosition; ///< 월드 위치 K3DMatrix mWorld; ///< 날씨효과 시스템의 월드 변환 행렬 virtual void reset() { release(); mPosition.Set( 0, 0, 0 ); K3DMatrixIdentity( mWorld ); } }; template< typename _ParticleSystem > class SGameWeatherSystem { public: typedef _ParticleSystem particle_system_t; typedef typename particle_system_t::particle_t particle_t; typedef typename particle_system_t::ParticleBundle particle_bundle_t; typedef typename particle_system_t::ParticleResource resource_t; typedef SGameWeatherSystem< particle_system_t > this_t; protected: bool mActivity; ///< 날씨효과 활성화 여부 DWORD mTime; ///< 현재 시간( msec ) DWORD mTimeDiff; ///< 시간 변화량 DWORD mTimeBeginFade; ///< 활성화 / 비활성화 시작 시간 float mCapacityFactor; ///< 파티클 확보량 보정상수 int mCapacity; ///< 파티클 확보량 float mEmitPerMilliSec; ///< 밀리초당 발포량 float mEmitCount; float mEmitPerMilliSecFader; K3DVector mPosition; ///< 월드 위치 K3DVector mCamera; K3DVector mLookAt; ///< 타겟 위치 K3DVector mLocalCamera; ///< 로컬 카메라 위치 env_fx::SNatureEnvInfo* mNatureInfo; ///< 자연환경 정보 env_fx::SWeatherAttr mAttribute; ///< 효과 속성 particle_bundle_t* mParticle; resource_t* mResource; public: // constructor & destructor SGameWeatherSystem( particle_bundle_t* particleBundle, resource_t* resource ) : mActivity ( false ) , mTime ( 0 ) , mTimeDiff ( 0 ) , mTimeBeginFade ( 0 ) , mCapacityFactor ( 0 ) , mCapacity ( 0 ) , mEmitPerMilliSec ( 0 ) , mEmitPerMilliSecFader ( 0 ) , mEmitCount ( 0 ) , mPosition ( 0, 0, 0 ) , mCamera ( 0, 0, 0 ) , mLookAt ( 0, 0, 0 ) , mLocalCamera ( 0, 0, 0 ) , mNatureInfo ( 0 ) , mParticle ( particleBundle ) , mResource ( resource ) { } virtual ~SGameWeatherSystem() { SAFE_DELETE( mParticle ); SAFE_DELETE( mResource ); SAFE_DELETE( mNatureInfo ); } // public method void initialize( const env_fx::SWeatherAttr& attribute, float capacityFactor ) { // wipe & get ready to initialize reset(); srand( 0 ); // initialize weahter attribute mAttribute = attribute; mCapacityFactor = capacityFactor; mCapacity = attribute.capacity * capacityFactor; mEmitPerMilliSec = (float)mCapacity / attribute.particle_life; mParticle->initialize( mCapacity ); mResource->initialize( mCapacity ); } void reset() { mActivity = false; mTime = 0; mTimeDiff = 0; mTimeBeginFade = 0; mCapacityFactor = 0; mCapacity = 0; mEmitPerMilliSec = 0; mEmitPerMilliSecFader = 0; mEmitCount = 0; mPosition = K3DVector( 0, 0, 0 ); mCamera = K3DVector( 0, 0, 0 ); mLookAt = K3DVector( 0, 0, 0 ); mLocalCamera = K3DVector( 0, 0, 0 ); mParticle->clear(); mResource->release(); } void process( DWORD time ) { if( mTime == 0 ) mTime = time; mTimeDiff = time - mTime; mTime = time; //// limit time differential //if( mTimeDiff > 200 ) // mTimeDiff = 200; _fadeEmit(); mEmitCount += mEmitPerMilliSecFader * mTimeDiff; if( mEmitCount >= 1.0f ) { mParticle->emit( mEmitCount ); mEmitCount = 0; } mParticle->update( mTime ); } void render( KViewportObject* viewport ) { mResource->render( mTime, viewport, *mParticle ); } void locate( const K3DVector& position, const K3DVector& lookAt ) { mPosition = position; mLookAt = lookAt; mResource->locate( position ); } void activate( DWORD time, bool activity ) { mActivity = activity; mTimeBeginFade = time; } bool isActive() const { return ( mActivity || mParticle->count() > 0 ); } const env_fx::SWeatherAttr& getAttribute() const { return mAttribute; } private: virtual void _fadeEmit() { if( mTime >= mTimeBeginFade && mTime < mTimeBeginFade + mAttribute.state_fade_duration ) { float positiveFactor = (float)( mTime - mTimeBeginFade ) / mAttribute.state_fade_duration; mEmitPerMilliSecFader = ( mActivity == true ) ? mEmitPerMilliSec * positiveFactor : mEmitPerMilliSec * ( 1.0f - positiveFactor ); } else { mEmitPerMilliSecFader = ( mActivity == true ) ? mEmitPerMilliSec : 0; } } }; }