topical media & game development
graphic-directx-game-19-FireRing-PSystem.cpp / cpp
//=============================================================================
// PSystem.cpp by Frank Luna (C) 2004 All Rights Reserved.
//=============================================================================
include <PSystem.h>
include <Camera.h>
include <d3dApp.h>
include <d3dUtil.h>
include <cassert>
PSystem::PSystem(const std::string& fxName,
const std::string& techName,
const std::string& texName,
const D3DXVECTOR3& accel,
const AABB& box,
int maxNumParticles,
float timePerParticle)
: mAccel(accel), mBox(box), mTime(0.0f),
mMaxNumParticles(maxNumParticles), mTimePerParticle(timePerParticle)
{
// Allocate memory for maximum number of particles.
mParticles.resize(mMaxNumParticles);
mAliveParticles.reserve(mMaxNumParticles);
mDeadParticles.reserve(mMaxNumParticles);
// They start off all dead.
for(int i = 0; i < mMaxNumParticles; ++i)
{
mParticles[i].lifeTime = -1.0f;
mParticles[i].initialTime = 0.0f;
}
D3DXMatrixIdentity(&mWorld);
D3DXMatrixIdentity(&mInvWorld);
// Create the texture.
HR(D3DXCreateTextureFromFile(gd3dDevice, texName.c_str(), &mTex));
// Create the FX.
ID3DXBuffer* errors = 0;
HR(D3DXCreateEffectFromFile(gd3dDevice, fxName.c_str(),
0, 0, D3DXSHADER_DEBUG, 0, &mFX, &errors));
if( errors )
MessageBox(0, (char*)errors->GetBufferPointer(), 0, 0);
mhTech = mFX->GetTechniqueByName(techName.c_str());
mhWVP = mFX->GetParameterByName(0, "gWVP");
mhEyePosL = mFX->GetParameterByName(0, "gEyePosL");
mhTex = mFX->GetParameterByName(0, "gTex");
mhTime = mFX->GetParameterByName(0, "gTime");
mhAccel = mFX->GetParameterByName(0, "gAccel");
mhViewportHeight = mFX->GetParameterByName(0, "gViewportHeight");
// We don't need to set these every frame since they do not change.
HR(mFX->SetTechnique(mhTech));
HR(mFX->SetValue(mhAccel, mAccel, sizeof(D3DXVECTOR3)));
HR(mFX->SetTexture(mhTex, mTex));
HR(gd3dDevice->CreateVertexBuffer(mMaxNumParticles*sizeof(Particle),
D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY|D3DUSAGE_POINTS,
0, D3DPOOL_DEFAULT, &mVB, 0));
}
PSystem::~PSystem()
{
ReleaseCOM(mFX);
ReleaseCOM(mTex);
ReleaseCOM(mVB);
}
float PSystem::getTime()
{
return mTime;
}
void PSystem::setTime(float t)
{
mTime = t;
}
const AABB& PSystem::getAABB()const
{
return mBox;
}
void PSystem::setWorldMtx(const D3DXMATRIX& world)
{
mWorld = world;
// Compute the change of coordinates matrix that changes coordinates
// relative to world space so that they are relative to the particle
// system's local space.
D3DXMatrixInverse(&mInvWorld, 0, &mWorld);
}
void PSystem::addParticle()
{
if( mDeadParticles.size() > 0)
{
// Reinitialize a particle.
Particle* p = mDeadParticles.back();
initParticle(*p);
// No longer dead.
mDeadParticles.pop_back();
mAliveParticles.push_back(p);
}
}
void PSystem::onLostDevice()
{
HR(mFX->OnLostDevice());
// Default pool resources need to be freed before reset.
ReleaseCOM(mVB);
}
void PSystem::onResetDevice()
{
HR(mFX->OnResetDevice());
// Default pool resources need to be recreated after reset.
if(mVB == 0)
{
HR(gd3dDevice->CreateVertexBuffer(mMaxNumParticles*sizeof(Particle),
D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY|D3DUSAGE_POINTS,
0, D3DPOOL_DEFAULT, &mVB, 0));
}
}
void PSystem::update(float dt)
{
mTime += dt;
// Rebuild the dead and alive list. Note that resize(0) does
// not deallocate memory (i.e., the capacity of the vector does
// not change).
mDeadParticles.resize(0);
mAliveParticles.resize(0);
// For each particle.
for(int i = 0; i < mMaxNumParticles; ++i)
{
// Is the particle dead?
if( (mTime - mParticles[i].initialTime) > mParticles[i].lifeTime)
{
mDeadParticles.push_back(&mParticles[i]);
}
else
{
mAliveParticles.push_back(&mParticles[i]);
}
}
// A negative or zero mTimePerParticle value denotes
// not to emit any particles.
if( mTimePerParticle > 0.0f )
{
// Emit particles.
static float timeAccum = 0.0f;
timeAccum += dt;
while( timeAccum >= mTimePerParticle )
{
addParticle();
timeAccum -= mTimePerParticle;
}
}
}
void PSystem::draw()
{
// Get camera position relative to world space system and make it
// relative to the particle system's local system.
D3DXVECTOR3 eyePosW = gCamera->pos();
D3DXVECTOR3 eyePosL;
D3DXVec3TransformCoord(&eyePosL, &eyePosW, &mInvWorld);
// Set FX parameters.
HR(mFX->SetValue(mhEyePosL, &eyePosL, sizeof(D3DXVECTOR3)));
HR(mFX->SetFloat(mhTime, mTime));
HR(mFX->SetMatrix(mhWVP, &(mWorld*gCamera->viewProj())));
// Point sprite sizes are given in pixels. So if the viewport size
// is changed, then more or less pixels become available, which alters
// the perceived size of the particles. For example, if the viewport
// is 32x32, then a 32x32 sprite covers the entire viewport! But if
// the viewport is 1024x1024, then a 32x32 sprite only covers a small
// portion of the viewport. Thus, we scale the particle's
// size by the viewport height to keep them in proportion to the
// viewport dimensions.
HWND hwnd = gd3dApp->getMainWnd();
RECT clientRect;
GetClientRect(hwnd, &clientRect);
HR(mFX->SetInt(mhViewportHeight, clientRect.bottom));
UINT numPasses = 0;
HR(mFX->Begin(&numPasses, 0));
HR(mFX->BeginPass(0));
HR(gd3dDevice->SetStreamSource(0, mVB, 0, sizeof(Particle)));
HR(gd3dDevice->SetVertexDeclaration(Particle::Decl));
AABB boxWorld;
mBox.xform(mWorld, boxWorld);
if( gCamera->isVisible( boxWorld ) )
{
// Initial lock of VB for writing.
Particle* p = 0;
HR(mVB->Lock(0, 0, (void**)&p, D3DLOCK_DISCARD));
int vbIndex = 0;
// For each living particle.
for(UINT i = 0; i < mAliveParticles.size(); ++i)
{
// Copy particle to VB
p[vbIndex] = *mAliveParticles[i];
++vbIndex;
}
HR(mVB->Unlock());
// Render however many particles we copied over.
if(vbIndex > 0)
{
HR(gd3dDevice->DrawPrimitive(D3DPT_POINTLIST, 0, vbIndex));
}
}
HR(mFX->EndPass());
HR(mFX->End());
}
(C) Æliens
20/2/2008
You may not copy or print any of this material without explicit permission of the author or the publisher.
In case of other copyright issues, contact the author.