www.pudn.com > snow.rar > pSystem.cpp


 
#include  
#include "pSystem.h" 
 
using namespace psys; 
 
const DWORD Particle::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE; 
 
PSystem::PSystem() 
{ 
	_device = 0; 
	_vb     = 0; 
	_tex    = 0; 
} 
 
PSystem::~PSystem() 
{ 
	d3d::Release(_vb); 
	d3d::Release(_tex); 
} 
 
bool PSystem::init(IDirect3DDevice9* device, char* texFileName) 
{ 
 
	_device = device; // save a ptr to the device 
 
	HRESULT hr = 0; 
 
	hr = device->CreateVertexBuffer( 
		_vbSize * sizeof(Particle), 
		D3DUSAGE_DYNAMIC | D3DUSAGE_POINTS | D3DUSAGE_WRITEONLY, 
		Particle::FVF, 
		D3DPOOL_DEFAULT, // D3DPOOL_MANAGED can't be used with D3DUSAGE_DYNAMIC  
		&_vb, 
		0); 
	 
	if(FAILED(hr)) 
	{ 
		::MessageBox(0, "CreateVertexBuffer() - FAILED", "PSystem", 0); 
		return false; 
	} 
 
	hr = D3DXCreateTextureFromFile( 
		device, 
		texFileName, 
		&_tex); 
 
	if(FAILED(hr)) 
	{ 
		::MessageBox(0, "D3DXCreateTextureFromFile() - FAILED", "PSystem", 0); 
		return false; 
	} 
 
	return true; 
} 
 
void PSystem::reset() 
{ 
	std::list::iterator i; 
	for(i = _particles.begin(); i != _particles.end(); i++) 
	{ 
		resetParticle( &(*i) ); 
	} 
} 
 
void PSystem::addParticle() 
{ 
	Attribute attribute; 
 
	resetParticle(&attribute); 
 
	_particles.push_back(attribute); 
} 
 
void PSystem::preRender() 
{ 
	_device->SetRenderState(D3DRS_LIGHTING, true); 
	_device->SetRenderState(D3DRS_POINTSPRITEENABLE, true); 
	_device->SetRenderState(D3DRS_POINTSCALEENABLE, true);  
	_device->SetRenderState(D3DRS_POINTSIZE, d3d::FtoDw(_size)); 
	_device->SetRenderState(D3DRS_POINTSIZE_MIN, d3d::FtoDw(0.0f)); 
 
	// control the size of the particle relative to distance 
	_device->SetRenderState(D3DRS_POINTSCALE_A, d3d::FtoDw(0.0f)); 
	_device->SetRenderState(D3DRS_POINTSCALE_B, d3d::FtoDw(5.0f)); 
	_device->SetRenderState(D3DRS_POINTSCALE_C, d3d::FtoDw(10.0f)); 
	 
	// use alpha from texture 
    _device->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE ); 
    _device->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE ); 
    _device->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE ); 
	_device->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTA_TEXTURE); 
	_device->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); 
 
	_device->SetRenderState(D3DRS_ALPHABLENDENABLE, true); 
	_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); 
    _device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); 
} 
 
void PSystem::postRender() 
{ 
	_device->SetRenderState(D3DRS_LIGHTING,          true); 
	_device->SetRenderState(D3DRS_POINTSPRITEENABLE, false); 
	_device->SetRenderState(D3DRS_POINTSCALEENABLE,  false); 
	_device->SetRenderState(D3DRS_ALPHABLENDENABLE,  false); 
} 
 
void PSystem::render() 
{ 
	 
	if( !_particles.empty() ) 
	{ 
		// 
		// set render states 
		// 
 
		preRender(); 
		 
		_device->SetTexture(0, _tex); 
		_device->SetFVF(Particle::FVF); 
		_device->SetStreamSource(0, _vb, 0, sizeof(Particle)); 
 
		// 
		// render batches one by one 
		// 
		if(_vbOffset >= _vbSize) 
			_vbOffset = 0; 
 
		Particle* v = 0; 
 
		_vb->Lock( 
			_vbOffset    * sizeof( Particle ), 
			_vbBatchSize * sizeof( Particle ), 
			(void**)&v, 
			_vbOffset ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD); 
 
		DWORD numParticlesInBatch = 0; 
 
		// 
		// Until all particles have been rendered. 
		// 
		std::list::iterator i; 
		for(i = _particles.begin(); i != _particles.end(); i++) 
		{ 
			if( i->_isAlive ) 
			{ 
				// 
				// Copy a batch of the living particles to the 
				// next vertex buffer segment 
				// 
				v->_position = i->_position; 
				v->_color    = (D3DCOLOR)i->_color; 
				v++; // next element; 
 
				numParticlesInBatch++; //increase batch counter 
 
				// if this batch full? 
				if(numParticlesInBatch == _vbBatchSize)  
				{ 
					// 
					// Draw the last batch of particles that was 
					// copied to the vertex buffer.  
					// 
					_vb->Unlock(); 
 
					_device->DrawPrimitive( 
						D3DPT_POINTLIST, 
						_vbOffset, 
						_vbBatchSize); 
 
					// move the offset to the start of the next batch 
					_vbOffset += _vbBatchSize;  
 
 
					if(_vbOffset >= _vbSize)  
						_vbOffset = 0;        
 
					_vb->Lock( 
						_vbOffset    * sizeof( Particle ), 
						_vbBatchSize * sizeof( Particle ), 
						(void**)&v, 
						_vbOffset ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD); 
 
					numParticlesInBatch = 0; // reset for new batch 
				}	 
			} 
		} 
 
		_vb->Unlock(); 
 
		if( numParticlesInBatch ) 
		{ 
			_device->DrawPrimitive( 
				D3DPT_POINTLIST, 
				_vbOffset, 
				numParticlesInBatch); 
		} 
 
		// next block 
		_vbOffset += _vbBatchSize;  
 
		// 
		// reset render states 
		// 
 
		postRender(); 
	} 
} 
 
bool PSystem::isEmpty() 
{ 
	return _particles.empty(); 
} 
 
bool PSystem::isDead() 
{ 
	std::list::iterator i; 
	for(i = _particles.begin(); i != _particles.end(); i++) 
	{ 
 
		if( i->_isAlive ) 
			return false; 
	} 
	 
	return true; 
} 
 
void PSystem::removeDeadParticles() 
{ 
	std::list::iterator i; 
 
	i = _particles.begin(); 
 
	while( i != _particles.end() ) 
	{ 
		if( i->_isAlive == false ) 
		{ 
 
			i = _particles.erase(i);  
		} 
		else 
		{ 
			i++; // next in list 
		} 
	} 
} 
 
//***************************************************************************** 
// Snow System 
//*************** 
 
Snow::Snow(d3d::BoundingBox* boundingBox, int numParticles) 
{ 
	_boundingBox   = *boundingBox; 
	_size          = 0.23f; 
	_vbSize        = 2048; 
	_vbOffset      = 0;  
	_vbBatchSize   = 512;  
	 
	for(int i = 0; i < numParticles; i++) 
		addParticle(); 
} 
 
void Snow::resetParticle(Attribute* attribute) 
{ 
	attribute->_isAlive  = true; 
 
	// get random x, z coordinate for the position of the snow flake. 
	d3d::GetRandomVector( 
		&attribute->_position, 
		&_boundingBox._min, 
		&_boundingBox._max); 
 
	attribute->_position.y = _boundingBox._max.y;  
 
	// snow flakes fall downwards and slightly to the left 
	attribute->_velocity.x = d3d::GetRandomFloat(0.0f, 1.0f) * -3.0f; 
	attribute->_velocity.y = d3d::GetRandomFloat(0.0f, 1.0f) * -10.0f; 
	attribute->_velocity.z = 0.0f; 
 
	// white snow flake 
	attribute->_color = d3d::WHITE; 
} 
 
void Snow::update(float timeDelta) 
{ 
	std::list::iterator i; 
	for(i = _particles.begin(); i != _particles.end(); i++) 
	{ 
		i->_position += i->_velocity * timeDelta; 
 
		if( _boundingBox.isPointInside( i->_position ) == false )  
		{ 
 
			resetParticle( &(*i) ); 
		} 
	} 
}