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

1152 lines
32 KiB
C++

/**==========================================================*\
@file : SGameWeather.h
@brief : definitions of base classes and utilities for weather effect
@date : 2007.03.xx
@author : sonador
\*===========================================================*/
#pragma once
#include <queue>
#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;
}
}
};
}