Can't get particle system to display

Started by
28 comments, last by rahulpawar 13 years, 8 months ago
Your pVertices is a double pointer so you need to dereference it when accessing its members such as pos and color.

if (ParticleArray->state != PSTATE_DEAD)		{		*pVertices->pos=ParticleArray->position;		*pVertices->color=ParticleArray->color;		*pVertices++;		RenderCount++;		}


You mentioned having trouble locking. Do you have a valid vertex buffer pointer before calling Lock()? Otherwise the Lock syntax looks ok. Is ParticleArray.size() greater than 0?

I wrote a small particle engine that uses point sprites a few years ago. I *think* its still compiles. [looksaround]

Particle Generator

Steve>

[Edited by - smjones on August 3, 2010 1:38:04 PM]
Advertisement
Maybe a little clearer,:
Vertex *pVertices;pVertexBuffer->Lock(0, 0, (void**)&pVertices, 0);for (UINT i = 0; i < ParticleArray.size(); ++i){	if (ParticleArray->state != PSTATE_DEAD)	{		pVertices.pos=ParticleArray->position;		pVertices.color=ParticleArray->color;		RenderCount++;	}}pVertexBuffer->Unlock();

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

I had an error with the vertex buffer (used D3DPOOL_MANAGED for some reason) and there is not any exception errors now, but I can't see any of the particles.

Here's the new code:

particleSystem.h
#ifndef _PARTICLE_#define _PARTICLE_#include <d3d9.h>#include <d3dx9.h>#include <math.h>#include <vector>#include "gfx_dx9.h"using namespace std;#define FVF_CUSTOM (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1)enum ParticleState{	PSTATE_QUEUED,	PSTATE_ACTIVE,	PSTATE_DEAD};struct Vertex{	D3DXVECTOR3 pos;	DWORD color;	Vertex()	{		pos=D3DXVECTOR3(0.0f,0.0f,0.0f);		color=D3DXCOLOR(1.0f,1.0f,1.0f,1.0f);	}};inline DWORD FtoDW( FLOAT f ) { return *((DWORD*)&f); }struct Particle{	D3DXVECTOR3 position;	D3DCOLOR color;	float speed;	float friction;	float gravity;	float direction;	int life;	ParticleState state;	Particle()	{		position=D3DXVECTOR3( 0,0,0);		color=D3DCOLOR_ARGB( 255,0, 0, 0);		speed=friction=gravity=direction=0;		state=PSTATE_QUEUED;	}	void update()	{		life -=1;		if (life < 0){state=PSTATE_DEAD;}		float x_speed=cos( (D3DX_PI/ 180) * direction)*speed-friction;		float y_speed=sin( (D3DX_PI/ 180) * direction)*speed-gravity;		position+=D3DXVECTOR3( x_speed, y_speed, 0);	}};class emitter{public:	emitter( LPDIRECT3DDEVICE9 device, const char *tex, D3DXVECTOR3 pos,float speed, float friction, float gravity, int life);	~emitter();	inline float getRandomMinMax( float fMin, float fMax )	{		float fRandNum = (float)rand () / RAND_MAX;		return fMin + (fMax - fMin) * fRandNum;	}	void emit_stream( int count, D3DXVECTOR3 pos, float direction);	void emit_burst( int count, D3DXVECTOR3 pos, float min_direction, float max_direction);	void update();private:	LPDIRECT3DDEVICE9 pDevice;	LPDIRECT3DVERTEXBUFFER9 pVertexBuffer;	LPDIRECT3DTEXTURE9 texture;	int MaxParticles;	int ParticleCount;	DWORD RenderCount;	D3DXVECTOR3 xyz;	vector<Particle *> ParticleArray;};#endif


particleSystem.cpp
[source lang="cpp]#include "particleSystem.h"emitter::emitter( LPDIRECT3DDEVICE9 device, const char *tex, D3DXVECTOR3 pos,float speed, float friction, float gravity, int life):	pDevice( device),		xyz(pos){	MaxParticles = 256;	if (FAILED(pDevice->CreateVertexBuffer( 256*sizeof(Vertex),		D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY | D3DUSAGE_POINTS,		FVF_CUSTOM, D3DPOOL_DEFAULT, &pVertexBuffer, NULL)))	{		MessageBox( NULL, "Failed To Create Vertex Buffer", "Error", MB_OK);	}	if(FAILED(D3DXCreateTextureFromFile( pDevice, tex, &texture)))	{		MessageBox(NULL, "Could Not Load Particle Texture", "Error", MB_OK);	}	for ( int i = 0; i <= MaxParticles; ++i)	{		Particle *pParticle = new Particle();		pParticle->position=xyz;		pParticle->speed=speed;		pParticle->friction=friction;		pParticle->gravity=gravity;		pParticle->life=life;		pParticle->state=PSTATE_QUEUED;		ParticleArray.push_back( pParticle);	}}emitter::~emitter(){	if (texture){texture->Release();}	if (pVertexBuffer){pVertexBuffer->Release();}	for (int i = 0; i <=MaxParticles;++i)	{		delete ParticleArray;		ParticleArray = NULL;	}	ParticleArray.clear();}void emitter::emit_stream( int count, D3DXVECTOR3 pos, float direction){	for (int i = 0; i < count; ++i)	{		ParticleArray.at(ParticleCount)->position=pos;		ParticleArray.at(ParticleCount)->direction=direction;		ParticleCount+=1;	}}void emitter::emit_burst( int count, D3DXVECTOR3 pos, float min_direction, float max_direction){	for (int i = 0; i < count; ++i)	{		float direction = getRandomMinMax( min_direction, max_direction);		ParticleArray[ParticleCount]->position=pos;		ParticleArray[ParticleCount]->direction=direction;		ParticleCount+=1;	}}void emitter::update(){	for (int i = 0; i < ParticleCount; ++i)	{		if (ParticleArray->state = PSTATE_QUEUED)		{			if (ParticleArray->position!=xyz){ParticleArray->position=xyz;}			ParticleArray->update();			++RenderCount;		}		if (ParticleArray->state = PSTATE_ACTIVE)		{			ParticleArray->update();			++RenderCount;		}		if (ParticleArray->state = PSTATE_DEAD)		{			ParticleArray.erase(ParticleArray.begin()+i);			Particle *temp  = new Particle();			ParticleArray.insert(ParticleArray.begin()+i+1, temp);		}	}	pDevice->SetRenderState( D3DRS_POINTSPRITEENABLE, TRUE );       // Turn on point sprites    pDevice->SetRenderState( D3DRS_POINTSCALEENABLE,  TRUE );       // Allow sprites to be scaled with distance    pDevice->SetRenderState( D3DRS_POINTSIZE,     FtoDW(1.0f) ); // Float value that specifies the size to use for point size computation in cases where point size is not specified for each vertex.    pDevice->SetRenderState( D3DRS_POINTSIZE_MIN, FtoDW(1.0f) );    // Float value that specifies the minimum size of point primitives. Point primitives are clamped to this size during rendering.     pDevice->SetRenderState( D3DRS_POINTSCALE_A,  FtoDW(1.0f) );       pDevice->SetRenderState( D3DRS_POINTSCALE_B,  FtoDW(1.0f) );       pDevice->SetRenderState( D3DRS_POINTSCALE_C,  FtoDW(0.0f) );   	pDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );    pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );	pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );		// fountain particles  	pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );	Vertex *pVertices;	pVertexBuffer->Lock(0, ParticleArray.size()*sizeof(Vertex), (void**)&pVertices, D3DLOCK_DISCARD);	for (UINT i = 0; i < ParticleArray.size(); ++i)	{		if (ParticleArray->state != PSTATE_DEAD)		{		pVertices->pos=ParticleArray->position;		pVertices->color=ParticleArray->color;		++pVertices;		RenderCount++;		}	}		pVertexBuffer->Unlock();		if (RenderCount)	{		pDevice->SetStreamSource( 0, pVertexBuffer, 0, sizeof(Vertex));		pDevice->SetFVF(FVF_CUSTOM);		pDevice->SetTexture( 0,texture);		pDevice->DrawPrimitive(D3DPT_POINTLIST, 0, RenderCount);	}	pDevice->SetRenderState( D3DRS_POINTSPRITEENABLE, FALSE );    pDevice->SetRenderState( D3DRS_POINTSCALEENABLE,  FALSE );	pDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE );    pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );}


EDIT:

I'm also getting a lot of these errors:

HEAP[SADE_2D.exe]: Invalid allocation size - 9B9B9FBD (exceeded 7ffdefff)
HEAP[SADE_2D.exe]: Invalid allocation size - 9B9BA1BF (exceeded 7ffdefff)
HEAP[SADE_2D.exe]: Invalid allocation size - 9B9BA3C1 (exceeded 7ffdefff)
First-chance exception at 0x7c812afb in SADE_2D.exe: Microsoft C++ exception: long at memory location 0x0012f800..
First-chance exception at 0x7c812afb in SADE_2D.exe: Microsoft C++ exception: long at memory location 0x0012f800..
First-chance exception at 0x7c812afb in SADE_2D.exe: Microsoft C++ exception: long at memory location 0x0012f800..
First-chance exception at 0x7c812afb in SADE_2D.exe: Microsoft C++ exception: long at memory location 0x0012f800..
HEAP[SADE_2D.exe]: Invalid allocation size - 9B9BA5C3 (exceeded 7ffdefff)
HEAP[SADE_2D.exe]: Invalid allocation size - 9B9BA7C5 (exceeded 7ffdefff)
First-chance exception at 0x7c812afb in SADE_2D.exe: Microsoft C++ exception: long at memory location 0x0012f800..
HEAP[SADE_2D.exe]: Invalid allocation size - 9B9BA9C7 (exceeded 7ffdefff)
First-chance exception at 0x7c812afb in SADE_2D.exe: Microsoft C++ exception: long at memory location 0x0012f800..
HEAP[SADE_2D.exe]: Invalid allocation size - 9B9BABC9 (exceeded 7ffdefff)
First-chance exception at 0x7c812afb in SADE_2D.exe: Microsoft C++ exception: long at memory location 0x0012f800..
HEAP[SADE_2D.exe]: Invalid allocation size - 9B9BADCB (exceeded 7ffdefff)
First-chance exception at 0x7c812afb in SADE_2D.exe: Microsoft C++ exception: long at memory location 0x0012f800..
HEAP[SADE_2D.exe]: Invalid allocation size - 9B9BAFCD (exceeded 7ffdefff)
First-chance exception at 0x7c812afb in SADE_2D.exe: Microsoft C++ exception: long at memory location 0x0012f800..
HEAP[SADE_2D.exe]: Invalid allocation size - 9B9BB1CF (exceeded 7ffdefff)
First-chance exception at 0x7c812afb in SADE_2D.exe: Microsoft C++ exception: long at memory location 0x0012f800..
HEAP[SADE_2D.exe]: Invalid allocation size - 9B9BB3D1 (exceeded 7ffdefff)
Several suspicious and incorrect pieces of code. You need to be much more rigorous in your programming habits. Particularly as a beginner, you should be very liberal with comments about what functions do (particularly if you're going to post it and ask people to look at it), and, if it's not very, very obvious, add comments to individual lines to explain (to yourself if no-one else) what you're doing.

If you force yourself to write comments, you'll have a self-check on whether you're coding what you want to happen.

First, you should do some debugging. Find out where in your code the errors occur.

Second:
ParticleArray.erase(ParticleArray.begin()+i);

That's a memory leak. Erasing a vector element doesn't call the destructor of an object when the element is a pointer to that object.

Third:
In emitter::update, where do you set RenderCount to 0? You increase it a lot, through what appears to be several series of loops before you render.

Fourth:
In emit_stream and emit_burst, you use ParticleCount to do something. Do you need to set it to 0 prior to the loops?

Lastly:
You should never, never get this far with a program that has those kinds of errors. You should do thorough testing at each stage of adding features. Have you tried just rendering a single particle without all the surrounding code? If you haven't done that, you shouldn't be testing a changing array of 256 of them.

EDIT: Rathering than erasing a vector element, why not incorporate a Reset() function and, if the particle's dead, just reset it. Alternatively, don't push_back pointers, push_back an instance of the class. Then the destructor will be called on vector::erase(). Even better, use boost::shared_ptrs.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Quote:
Several suspicious and incorrect pieces of code. You need to be much more rigorous in your programming habits. Particularly as a beginner, you should be very liberal with comments about what functions do (particularly if you're going to post it and ask people to look at it), and, if it's not very, very obvious, add comments to individual lines to explain (to yourself if no-one else) what you're doing.

If you force yourself to write comments, you'll have a self-check on whether you're coding what you want to happen.

First, you should do some debugging. Find out where in your code the errors occur.


Good advice, I'll start doing that.

Quote:
Second:

ParticleArray.erase(ParticleArray.begin()+i);


That's a memory leak. Erasing a vector element doesn't call the destructor of an object when the element is a pointer to that object.


I'm using a vector of pointers, so I can use delete there but really need a better way of keeping track of those.

Quote:
Third:
In emitter::update, where do you set RenderCount to 0? You increase it a lot, through what appears to be several series of loops before you render.


Yes I does get increased, so I should set it to 0 before each frame.

Quote:
Fourth:
In emit_stream and emit_burst, you use ParticleCount to do something. Do you need to set it to 0 prior to the loops?


ParticleCount doesn't get set to 0, but it should increase and decrease with amount of active particles.

ParticleArray.insert(ParticleArray.begin()+i+1, temp);

Do you have a reason for inserting a new particle at that particular location, rather than just ParticleArray.push_back()?
Quote:ParticleCount ... should increase and decrease with amount of active particles.

Okay. There doesn't appear to be anything in your posted code that does that.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Quote:Original post by Buckeye
ParticleArray.insert(ParticleArray.begin()+i+1, temp);

Do you have a reason for inserting a new particle at that particular location, rather than just ParticleArray.push_back()?
Quote:ParticleCount ... should increase and decrease with amount of active particles.

Okay. There doesn't appear to be anything in your posted code that does that.


I should be using push_back instead of insert. I thought that it leaves a "hole" where I remove a dead particle(Just read over the vector reference), but it does shift the elements for me. As for the ParticleCount var, I just added that part.

EDIT:
Is there any I have to set when initializing d3d9 because I still can't figure this out.
Quote:As for the ParticleCount var, I just added that part.

Okay. I still don't see where you ever decrease it. If you do it somewhere else in your code, that's fine. Otherwise, the following looks very wrong.
for (int i = 0; i < count; ++i){	ParticleArray.at(ParticleCount)->position=pos;	ParticleArray.at(ParticleCount)->direction=direction;	ParticleCount+=1;}

Quote:Is there any I have to set when initializing d3d9..?

I'm not quite sure what you mean. You can look at the docs to see what the default settings are and change them to suit.

If you're asking basic questions like that, however, you certainly shouldn't be trying to implement anything more complicated than rendering a single static object. When you can do that, make one small change and test again.

I strongly urge you to modify your program to do that - render a single particle. Until you do that successfully, all this reviewing and commenting is of no value. It appears that you've written hundreds of lines of code without testing it along the way, with little error-checking or handling of any kind - and then find it doesn't work as expected.

Just some tough love.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

I guess there is no choice but to figure out how to render one point sprite.
Buckeye is correct when he says to do your programming incrementally. Get some of the basics working before progressing on to other things. That is very good practice.

Quote:I guess there is no choice but to figure out how to render one point sprite.


I gave a link in my post to an application that shows how to do point sprite particle effects including setting up DirectX. It includes a couple very basic emitters: fountain and radial (think: fireworks).

If you don't like that sample then check out an even simpler one at CodeSampler.
Point Sprites.

Try to understand various parts of the sample programs especially things like setup. You can comment out parts of your own code to pare it down to the bare minimum in order to get things working.

Quote:Alternatively, don't push_back pointers, push_back an instance of the class

Depending on the size of the object this can be costly especially if you are creating and destroying particles on the fly. Do not be afraid of storing and working with pointers. They are fundamental in C++ and should be understood. Pushing pointers from objects (particles) created on the heap is merely 4 bytes (on 32-bit OS) and very fast. You will need to be careful with memory leaks caused by not deleting the memory correctly or at all.

Here's a simple demonstration of using std::vector.
#include <vector>using std::vector;class Particle{public:	float x,y,z;};int main(){	std::vector<Particle*>  Particles;		// Add	Particles.push_back(new Particle);	Particles.push_back(new Particle);	Particles.push_back(new Particle);	Particles.push_back(new Particle);	Particles.push_back(new Particle);	Particles.push_back(new Particle);	Particles.push_back(new Particle);	Particles.push_back(new Particle);	Particles.push_back(new Particle);	Particles.push_back(new Particle);	// Clean up, delete the Particle object memory	std::vector<Particle*>::iterator iter;	for (iter = Particles.begin();		 iter != Particles.end();		 ++iter)	{		delete (*iter);		(*iter) = NULL;	}	// Remove the elements from the std::vector.  An elements is 	// a "bucket" that hold a pointer to a Particle.	Particles.clear();	// Yay! No memory leaks!}



Steve

[Edited by - smjones on August 3, 2010 1:16:29 PM]

This topic is closed to new replies.

Advertisement