1152 lines
32 KiB
C++
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;
|
|
}
|
|
}
|
|
};
|
|
} |