# Possible state problem in my particle system - SOLVED

This topic is 4884 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hello. In my program I am mainly using textured quads so I use a set of global states which do not change, these are shown below.
// Set the vertex format
GlobalDirect3D9Device->SetFVF(D3DFVF_TLVERTEX);

// Set the render states for alpha blending
GlobalDirect3D9Device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); // Enable alpha blending
GlobalDirect3D9Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); // Set the source blend state
GlobalDirect3D9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); // Set the destination blend state
GlobalDirect3D9Device->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
GlobalDirect3D9Device->SetRenderState(D3DRS_LIGHTING, FALSE);

// Set the texture stage states
GlobalDirect3D9Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
GlobalDirect3D9Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
GlobalDirect3D9Device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);

GlobalDirect3D9Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TFACTOR);
GlobalDirect3D9Device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);
GlobalDirect3D9Device->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
GlobalDirect3D9Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE2X);

// Where the TLVERTEX is defined as:
const DWORD D3DFVF_TLVERTEX = D3DFVF_XYZRHW | D3DFVF_TEX1;


However, I have just added a Particle System to the program which is defined in a derived class called ScreenEntityParticleSystem, this obviously requires different states on the device to be rendered. Please see the source for the Particle System below. The render states are changed in the RenderScreenEntity() method. When the program starts up I can see the textures quads in the background very briefly but then the whole screen starts flashing. I think that some of the states that are set in the RenderScreenEntity() method are not reverted back properly so the rest of the screen ends up being subjected to incorrect states are hence, an incorrect display. ScreenEntityParticleSystem.h
#ifndef SCREENENTITYPARTICLESYSTEM
#define SCREENENTITYPARTICLESYSTEM

#include "ScreenEntityBase.h"

struct Particle
{
D3DXVECTOR2 CurrentPosition;	// The current position of the particle
D3DXVECTOR2 CurrentVelocity;	// The current velocity of the particle
D3DCOLOR Colour;				// The colour of the particle (RGBA)
float StartTime;				// The time the particle started
float LifeTime;					// The amount of time the particle has to live
float AlphaDecay;				// The amount of alpha which decays each second
};

class ScreenEntityParticleSystem : public ScreenEntityBase
{
public:
// (De)Constructors
ScreenEntityParticleSystem();									// Default constructor
ScreenEntityParticleSystem(ScreenEntityParticleSystem & rhs);	// Copy constructor
ScreenEntityParticleSystem(RECT cScreenRectangle,
DWORD cFlags,
int cDepthLevel,
string cBehaviourModifier,
string cParticleTexture);			// Constrcutor

~ScreenEntityParticleSystem();									// Deconstructor

// Assignment operator
ScreenEntityParticleSystem& ScreenEntityParticleSystem::operator =(ScreenEntityParticleSystem & rhs);

// Public methods
HRESULT Initialise();
HRESULT HandleMessage(UINT MessageType, WPARAM wParam, LPARAM lParam);
HRESULT RenderScreenEntity();
void OutputInformation();
virtual void Release();
virtual void ControlEntity(bool start);

// Get and set methods
string GetParticleTextureLocation();
void SetParticleTextureLocation(string newParticleTextureLocation);

bool GetGravity();
void SetGravity(bool newGravity);

RECT GetReleaseArea();
void SetReleaseArea(RECT newReleaseArea);

float GetReleaseInterval();
void SetReleaseInterval(float newReleaseInterval);

int GetMaxParticles();
void SetMaxParticles(int newMaxParticles);

DWORD GetFlush();
void SetFlush(DWORD newFlush);

float GetMaxPointSize();
void SetMaxPointSize(float newMaxPointSize);

float GetSize();
void SetSize(float newSize);

float GetVelocityVar();
void SetVelocityVar(float newVelocityVar);

DWORD GetVBOffset();
void SetVBOffset(DWORD newVBOffset);

bool GetPaused();
void SetPaused(bool newPaused);

bool GetSupportsPSIZE();
void SetSupportsPSIZE(bool newSupportsPSIZE);

float GetLastTime();
void SetLastTime(float newLastTime);

vector<Particle> GetParticleList();
void SetParticleList(vector<Particle> newParticleList);

private:

// Private methods
HRESULT Update();

// Private Data Members
string ParticleTextureLocation;	// The location of the particle file
bool Gravity;					// true if gravity is to be taken into account
RECT ReleaseArea;				// A rect from within which the particles are emitted
float ReleaseInterval;			// The interval at which new particles should be released
int MaxParticles;				// The maximum number of particles which may exist in the system
DWORD VBOffset;					// The offset of the VB to write to
DWORD Flush;					// The number of sprites to send to the hardware at once
DWORD Discard;					// The maximum number of point sprites that the vertex buffer can load before we have to discard and start again
float MaxPointSize;				// The maximum point size
float Size;						// The size of the sprite to be drawn
float VelocityVar;				// A scaling factor for the velocity
bool Paused;					// Updates on particles are not carried out if this is true
bool SupportsPSIZE;				// Hardware allows us to change the size of the point sprites
float LastTime;					// Holds the time when the last updated occured

LPDIRECT3DVERTEXBUFFER9 VertexBuffer;	// Holds the vertices to be rendered
LPDIRECT3DTEXTURE9		ParticleTexture;// Stores the particle texture
vector<Particle> ParticleList;			// Holds all the particles
};

#endif

ScreenEntityParticleSystem.cpp


ScreenEntityParticleSystem.cpp
#include "ScreenEntityParticleSystem.h"
// Default constructor
ScreenEntityParticleSystem::ScreenEntityParticleSystem()
:ScreenEntityBase()
{
RECT TempRect;
TempRect.bottom = 0;
TempRect.top = 0;
TempRect.left = 0;
TempRect.right = 0;

ReleaseArea				= TempRect;
ParticleTextureLocation	= "";
Gravity					= false;
ReleaseInterval			= 1.0f;
MaxParticles			= 1;
VBOffset				= 0;
Flush					= 128;
Size					= 1.0f;
VelocityVar				= 1.0f;
Paused					= true;
SupportsPSIZE			= false;
LastTime				= timeGetTime();

//TODO -- Is this necessary
ParticleList.clear();
}

// Copy constructor
ScreenEntityParticleSystem::ScreenEntityParticleSystem(ScreenEntityParticleSystem & rhs)
:ScreenEntityBase(rhs)
{
// Copy all the simple types
ParticleTextureLocation = rhs.GetParticleTextureLocation();
Gravity					= rhs.GetGravity();
ReleaseArea				= rhs.GetReleaseArea();
ReleaseInterval			= rhs.GetReleaseInterval();
MaxParticles			= rhs.GetMaxParticles();
VBOffset				= rhs.GetVBOffset();
Flush					= rhs.GetFlush();
MaxPointSize			= rhs.GetMaxPointSize();
Size					= rhs.GetSize();
VelocityVar				= rhs.GetVelocityVar();
Paused					= rhs.GetPaused();
SupportsPSIZE			= rhs.GetSupportsPSIZE();
LastTime				= rhs.GetLastTime();

for(int i = 0;i < DeadList.size();i++)

{
}

ParticleList.clear();

for(vector<Particle>::iterator walker = rhs.GetParticleList().begin();walker != rhs.GetParticleList().end();walker++)
{
ParticleList.push_back(*walker);
}
}

// Constructor
ScreenEntityParticleSystem::ScreenEntityParticleSystem(RECT cScreenRectangle,
DWORD cFlags,
int cDepthLevel,
string cBehaviourModifier,
string cParticleTexture)
:ScreenEntityBase(cScreenRectangle,
cFlags,
cDepthLevel,
cBehaviourModifier),
ParticleTextureLocation(cParticleTexture)
{
LastTime = timeGetTime();
}

// Deconstructor
ScreenEntityParticleSystem::~ScreenEntityParticleSystem()
{
// This may also need to be filled in at a later date
}

// Assignment operator
ScreenEntityParticleSystem& ScreenEntityParticleSystem::operator =(ScreenEntityParticleSystem & rhs)
{
if(this == &rhs)
return *this;

// Call the base class operator so that the full assignment in carried out
ScreenEntityBase::operator=(rhs);

// Copy all the simple types
ParticleTextureLocation = rhs.GetParticleTextureLocation();
Gravity					= rhs.GetGravity();
ReleaseArea				= rhs.GetReleaseArea();
ReleaseInterval			= rhs.GetReleaseInterval();
MaxParticles			= rhs.GetMaxParticles();
VBOffset				= rhs.GetVBOffset();
Flush					= rhs.GetFlush();
MaxPointSize			= rhs.GetMaxPointSize();
Size					= rhs.GetSize();
VelocityVar				= rhs.GetVelocityVar();
Paused					= rhs.GetPaused();
SupportsPSIZE			= rhs.GetSupportsPSIZE();

for(int i = 0;i < DeadList.size();i++)

{
}

ParticleList.clear();

for(vector<Particle>::iterator walker = rhs.GetParticleList().begin();walker != rhs.GetParticleList().end();walker++)
{
ParticleList.push_back(*walker);
}

return *this;
}

// Initialise the screen entity
HRESULT ScreenEntityParticleSystem::Initialise()
{
// Create a vertex buffer for the particle system
D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY | D3DUSAGE_POINTS,
D3DFVF_TLVERTEXPOINT,
D3DPOOL_DEFAULT,
&VertexBuffer,
NULL)))
{
return E_FAIL;
DXTRACE_MSG("Could not create vertex buffer in particle system");
}

// Get the max point size
D3DCAPS9 d3dcaps;
GlobalDirect3D9Device->GetDeviceCaps(&d3dcaps);
MaxPointSize = d3dcaps.MaxPointSize;

// Check to see if we can change the size of the point sprites
if(d3dcaps.FVFCaps & D3DFVFCAPS_PSIZE)
SupportsPSIZE = true;
else
SupportsPSIZE = false;

// Create the texture object
if(FAILED(D3DXCreateTextureFromFile(GlobalDirect3D9Device,
ParticleTextureLocation.c_str(),
&ParticleTexture)))
{
return E_FAIL;
DXTRACE_MSG("Could not create texture file in particle system");
}

// Initialise the GlobalGravityVector
GlobalGravityVector.x = 0;
GlobalGravityVector.y = 0.5;

// The initialisation has been successful so we set the valid flag to true
Flags = Flags | SCREENENTITY_VALID;

return S_OK;
}

HRESULT ScreenEntityParticleSystem::HandleMessage(UINT MessageType, WPARAM wParam, LPARAM lParam)
{
// The particle system is not effected by user input or render calls so just return S_OK
return S_OK;
}

HRESULT ScreenEntityParticleSystem::Update()
{
DXTRACE_MSG("************************************** Before update");
OutputInformation();
float CurrentTime = timeGetTime();
float ElapsedTime = (float)((CurrentTime - LastTime) * 0.001);

for(int i = 0;i < ParticleList.size();i++)
{
// If the particle is not dead then we process it
{
// If the particle is dead, put it on the dead list
if(((CurrentTime - ParticleList.StartTime) * 0.001) >= ParticleList.LifeTime)
{
char test[500];
CurrentTime,
ParticleList.StartTime,

DXTRACE_MSG("Particle has died");
DXTRACE_MSG(test);

// Tell the Particle that it is dead and put a reference to it on the deadlist
}
else
{
// The particle is still in it's active lifetime so we updated the position and velocity
// If gravity is switched on then we apply this here
if(Gravity)
{
ParticleList.CurrentVelocity += GlobalGravityVector * ElapsedTime;
}

// Update the position with respect to the velocity
ParticleList.CurrentPosition += ParticleList.CurrentVelocity * ElapsedTime;

// Update the alpha value
// TODO -- Finsh this
}
}
else
{
// This particle is dead so it does not need to be updated
}
}

// Now we need to emit new particles
if(CurrentTime - LastTime > ReleaseInterval)
{
// It is time to release a new particle, first check if there are any on the dead list
{

// Remove the reference from the deadlist
}
else
{
// There are no dead particles to reactivate so we will make
// another one if there is enough space on the ParticleList
if(ParticleList.size() <= MaxParticles)
{
Particle newParticle;
InitialiseParticle(&newParticle);
ParticleList.push_back(newParticle);
}
else
{
// There is no space for any more particles so we don't do anything
}
}
}

// Last update the LastTime float
LastTime = CurrentTime;

DXTRACE_MSG("************************************** After update");
OutputInformation();

return S_OK;
}

HRESULT ScreenEntityParticleSystem::RenderScreenEntity()
{
// If the system is not paused, then update the particles before they are rendered
if(!(Paused))
Update();

// Set the render states for using point sprites
GlobalDirect3D9Device->SetRenderState(D3DRS_POINTSPRITEENABLE, TRUE);
GlobalDirect3D9Device->SetRenderState(D3DRS_POINTSCALEENABLE, TRUE);
GlobalDirect3D9Device->SetRenderState(D3DRS_POINTSIZE, FtoDW(Size));
GlobalDirect3D9Device->SetRenderState(D3DRS_POINTSIZE_MIN, FtoDW(1.0f));
GlobalDirect3D9Device->SetRenderState(D3DRS_POINTSCALE_A, FtoDW(0.0f));
GlobalDirect3D9Device->SetRenderState(D3DRS_POINTSCALE_B, FtoDW(0.0f));
GlobalDirect3D9Device->SetRenderState(D3DRS_POINTSCALE_C, FtoDW(1.0f));
GlobalDirect3D9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);

// Used as a reference to the vertex buffer
PointVertex* Vertices;
DWORD NumParticlesToRender= 0;

// Lock the vertex buffer. Fill the vertex buffer in small chunks, using D3DLOCK_NOOVERWRITE.
// When done filling each chunk, call DrawPrimitive and lock the next chunk. When space runs
// out in the vertex buffer, start over at the beginning using D3DLOCK_DISCARD.

// Move the offset to fill the next chunk of the vertex buffer
VBOffset += Flush;

// If about to overflow the buffer, set it back to 0
VBOffset = 0;

// Lock the vertex buffer
HRESULT hr;
if(FAILED(hr = VertexBuffer->Lock(VBOffset*sizeof(PointVertex),	// Offset to lock
Flush*sizeof(PointVertex),	// Size to lock
(VOID **) &Vertices,
{
DXTRACE_ERR("Failed to lock the vertex buffer in the render method of the particle system", hr);
return hr;
}

// Render each particle
for(vector<Particle>::iterator walker = ParticleList.begin(); walker != ParticleList.end();walker++)
{
D3DXVECTOR2	vVel(walker->CurrentVelocity);

Vertices->posit = walker->CurrentPosition;
Vertices->color = walker->Colour;
Vertices++;

if(++NumParticlesToRender == Flush)
{
// Done filling this chunk of the vertex buffer so unlock and draw so that the
// next chunk can start being filled
VertexBuffer->Unlock();

GlobalDirect3D9Device->SetStreamSource(0, VertexBuffer, 0, sizeof(PointVertex));
GlobalDirect3D9Device->SetFVF(D3DFVF_TLVERTEXPOINT);

if(FAILED(hr = GlobalDirect3D9Device->DrawPrimitive(D3DPT_POINTLIST,
VBOffset,
NumParticlesToRender)))
{
DXTRACE_ERR("Draw primitive failed inside the Flush-if in the render method", hr);
return hr;
}

// Lock the next chunk of the vertex buffer. If we are at the end of the vertex buffer
// DISCARD the vertex buffer and start at the beginning. Otherwise, specify NOOVERWRITE
// so we can continue filling the VB while the previous chunk is drawing.
VBOffset += Flush;

// If about to overflow reset the offset to 0
VBOffset = 0;

if(FAILED(hr = VertexBuffer->Lock(VBOffset*sizeof(PointVertex),
Flush*sizeof(PointVertex),
(VOID **) &Vertices,
{
DXTRACE_ERR("Failed to lock the vertex buffer in the latter part of the render method", hr);
return hr;
}

NumParticlesToRender = 0;
}
}
}

// Unlock the vertex buffer
VertexBuffer->Unlock();

// Render any remaining particles
if(NumParticlesToRender)
{
GlobalDirect3D9Device->SetStreamSource(0, VertexBuffer, 0, sizeof(PointVertex));
GlobalDirect3D9Device->SetFVF(D3DFVF_TLVERTEXPOINT);

if(FAILED(hr = GlobalDirect3D9Device->DrawPrimitive(D3DPT_POINTLIST,
VBOffset,
NumParticlesToRender)))
{
DXTRACE_ERR("Draw primitive failed inside the final part of the render method", hr);
return hr;
}
}

// Reset render states
GlobalDirect3D9Device->SetRenderState(D3DRS_POINTSPRITEENABLE, FALSE);
GlobalDirect3D9Device->SetRenderState(D3DRS_POINTSCALEENABLE, FALSE);
GlobalDirect3D9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
GlobalDirect3D9Device->SetFVF(D3DFVF_TLVERTEX);

return S_OK;
}

void ScreenEntityParticleSystem::OutputInformation()
{
char output[500];

DXTRACE_MSG("****************************************** Particle Output Information");

// Walk through the particle list and for each particle output the colour, position,
// velocity, starttime, lifetime and alpha decay
for(vector<Particle>::iterator walker = ParticleList.begin();walker != ParticleList.end();walker++)
{
sprintf(output, "Position x: %f, Position y: %f, Velocity x: %f, Velocity y: %f, Starttime: %f, LifeTime: %f, AlphaDecay: %f",
walker->CurrentPosition.x,
walker->CurrentPosition.y,
walker->CurrentVelocity.x,
walker->CurrentVelocity.y,
walker->StartTime,

DXTRACE_MSG(output);
}

DXTRACE_MSG("****************************************** Particle Output Information end");
}

void ScreenEntityParticleSystem::Release()
{
SAFE_RELEASE(VertexBuffer);
SAFE_RELEASE(ParticleTexture);
//TODO -- Is this necessary
ParticleList.clear();
}

void ScreenEntityParticleSystem::ControlEntity(bool start)
{
if(start)
Paused = false;
else
Paused = true;
}

{
// Tell the particle that it is alive!

// Place the particle somewhere within the release area

// Set the intial velocity, colour, lifetime and alpha decay in accordance with the type of effect being created
if((Flags & PARTICLE_FIRE) > 0)
{
// ************** VELOCITY ****
// If this is fire then all the particles need to be sent more or less upwards so we
// set the y value to -2 (-y is up) and then add a Random between -10 and 10 divided

// The x is set straight up the middle (0) but we vary it with a random number between
// 10 and -10 which is then divide by 20 giving a maximum of 0.5 either way. That equates
// to a maximum angle of 22.5 degrees which may have to be reduced

// ************** COLOUR ****
// Define each colour element (plus the alpha)
float r, g, b, a, random;

// Calculate the random to be 0.2 either way. So get a random number between -10 and
// 10 and then divide it by fifty.

random = (float)(GetRandomNumber(-10,10)/50); // Random

r = 0.9f + random; // Red

g = 0.4f + random; // Green

b = 0.2f + random; // Blue

a = 1.0f; // Start the alpha at full

// Set the colour
DeadParticle->Colour = D3DXCOLOR(r, g, b, a);

// Alter the life time by 0.5 either way

// ************** ALPHA DECAY ****
// Alter the alpha decay by 0.1 either way
}
else if((Flags & PARTICLE_FIRE) > 0)
{
DXTRACE_MSG("Particle dust not defined yet!!");
}
else
{
DXTRACE_MSG("Particle system is of an invalid type or flag not set");
PostQuitMessage(0);
}

// Set the start time of the particle
}

// Get and set methods
string ScreenEntityParticleSystem::GetParticleTextureLocation()
{
return ParticleTextureLocation;
}

void ScreenEntityParticleSystem::SetParticleTextureLocation(string newParticleTextureLocation)
{
ParticleTextureLocation = newParticleTextureLocation;
}

bool ScreenEntityParticleSystem::GetGravity()
{
return Gravity;
}

void ScreenEntityParticleSystem::SetGravity(bool newGravity)
{
Gravity = newGravity;
}

DWORD ScreenEntityParticleSystem::GetVBOffset()
{
return VBOffset;
}

void ScreenEntityParticleSystem::SetVBOffset(DWORD newVBOffset)
{
VBOffset = newVBOffset;
}

RECT ScreenEntityParticleSystem::GetReleaseArea()
{
return ReleaseArea;
}

bool ScreenEntityParticleSystem::GetSupportsPSIZE()
{
return SupportsPSIZE;
}

void ScreenEntityParticleSystem::SetReleaseArea(RECT newReleaseArea)
{
ReleaseArea = newReleaseArea;
}

float ScreenEntityParticleSystem::GetReleaseInterval()
{
return ReleaseInterval;
}

void ScreenEntityParticleSystem::SetReleaseInterval(float newReleaseInterval)
{
ReleaseInterval = newReleaseInterval;
}

int ScreenEntityParticleSystem::GetMaxParticles()
{
return MaxParticles;
}

void ScreenEntityParticleSystem::SetMaxParticles(int newMaxParticles)
{
MaxParticles = newMaxParticles;
}

DWORD ScreenEntityParticleSystem::GetFlush()
{
return Flush;
}

void ScreenEntityParticleSystem::SetFlush(DWORD newFlush)
{
Flush = newFlush;
}

{
}

{
}

float ScreenEntityParticleSystem::GetMaxPointSize()
{
return MaxPointSize;
}

void ScreenEntityParticleSystem::SetMaxPointSize(float newMaxPointSize)
{
MaxPointSize = newMaxPointSize;
}

float ScreenEntityParticleSystem::GetSize()
{
return Size;
}

void ScreenEntityParticleSystem::SetSize(float newSize)
{
Size = newSize;
}

float ScreenEntityParticleSystem::GetVelocityVar()
{
return VelocityVar;
}

void ScreenEntityParticleSystem::SetVelocityVar(float newVelocityVar)
{
VelocityVar = newVelocityVar;
}

bool ScreenEntityParticleSystem::GetPaused()
{
return Paused;
}

void ScreenEntityParticleSystem::SetPaused(bool newPaused)
{
Paused = newPaused;
}

float ScreenEntityParticleSystem::GetLastTime()
{
return LastTime;
}

void ScreenEntityParticleSystem::SetLastTime(float newLastTime)
{
LastTime = newLastTime;
}

{
}

{
for(int i = 0;i < DeadList.size();i++)

{
}
}

vector<Particle> ScreenEntityParticleSystem::GetParticleList()
{
return ParticleList;
}

void ScreenEntityParticleSystem::SetParticleList(vector<Particle> newParticleList)
{
ParticleList.clear();

for(vector<Particle>::iterator walker = newParticleList.begin();walker != newParticleList.end();walker++)
{
ParticleList.push_back(*walker);
}
}


edit: Almost forgot, D3DFVF_TLVERTEXPOINT = D3DFVF_XYZRHW | D3DFVF_DIFFUSE; If anybody can spot the problem I will be eternally grateful! Also, any comments on the code are welcome. Thanks in advance. Kind regards. Mark Coleman [Edited by - mrmrcoleman on January 11, 2005 12:31:02 PM]

##### Share on other sites
This kind of problem you solve easiest with a trial&error approach, just comment out states till you can pinpoint whats going wrong.

##### Share on other sites
I have had a bit of a tinker about and I have more problems...

I took all the rest of the interface out so that no textured quads were being drawn and now I can see my particles but they are flashing on and off with a frequency of about 0.5 seconds.

Also the supposedly dead particles seem to be being drawn (also flashing) in their final location until they are reused.

Can anybody see a problem in the render section of my code? I pretty much plagiarised the algorithm from an example that I found on the internet (which I believe is very similar to the SDK sample) without fully understanding it. And I still don't really understand how it works because my knowledge of vertex buffers and how they work is limited at best.

Could somebody look through my rendering code and see if they can see any problems? As far as I am aware it is pretty much identical to the sample but the vertices are XYZRHW | DIFFUSE because my interface is all in 2d.

Regards.

Mark Coleman

##### Share on other sites
To draw the particles are you just iterating through the live particle list? casue when you kill a particle you add it to the dead list but as far as i can tell you dont remove it from the live list. That could be why the dead particles are being redraw?

Then again maybe not, im not 1337 enought to know what most of that code does:)

##### Share on other sites
Grekster, the particles are not removed from the ParticleList, they just have a flag set to tell the render code that they are dead. In the render loop this is checked (if(!walker->Dead)), and the particle is only drawn if the particle is 'alive'.

Thanks for the response though.

Mark Coleman

##### Share on other sites
Oh I missed that check lol. Im not very up on Direct X so it must have just blended in with all the meaningless DX code lol

Anyway good luck fixing it:)

##### Share on other sites
Ok, I have had a play around and have found that some of the render states in the RenderScreenEntity method were incorrect for point sprites in Orthogonal mode, also I had forgotten to reset the stream source which explains why the other screen entities were not drawing correctly. However I still have the problem that the entities are only visible only some of the time and when they are visible they seem to be flashing on and off?

Here is the new cpp file.
#include "ScreenEntityParticleSystem.h"// TODO -- Add the gravity vector as a member of this class// Also make render call update if the system is not paused// GetRandomNumber needs to be able to return negative values// Correct the clearing of stack objects// Need to take into account the VELOCITY VAR// Try using an object, the render states will definately be wrong.// Make sure the states are changed before and after the render method// Default constructorScreenEntityParticleSystem::ScreenEntityParticleSystem()							:ScreenEntityBase(){	RECT TempRect;	TempRect.bottom = 0;	TempRect.top = 0;	TempRect.left = 0;	TempRect.right = 0;	ReleaseArea				= TempRect;	ParticleTextureLocation	= "";	Gravity					= false;	ReleaseInterval			= 1.0f;	MaxParticles			= 1;	VBOffset				= 0;	Flush					= 128;	Discard					= 512;	Size					= 1.0f;	VelocityVar				= 1.0f;	Paused					= true;	SupportsPSIZE			= false;	LastTime				= timeGetTime();	LastEmit				= 0.0f;	//TODO -- Is this necessary	//DeadList.clear();	ParticleList.clear();}// Copy constructorScreenEntityParticleSystem::ScreenEntityParticleSystem(ScreenEntityParticleSystem & rhs)													:ScreenEntityBase(rhs){	// Copy all the simple types	ParticleTextureLocation = rhs.GetParticleTextureLocation();	Gravity					= rhs.GetGravity();	ReleaseArea				= rhs.GetReleaseArea();	ReleaseInterval			= rhs.GetReleaseInterval();	MaxParticles			= rhs.GetMaxParticles();	VBOffset				= rhs.GetVBOffset();	Flush					= rhs.GetFlush();	Discard					= rhs.GetDiscard();	MaxPointSize			= rhs.GetMaxPointSize();		Size					= rhs.GetSize();	VelocityVar				= rhs.GetVelocityVar();	Paused					= rhs.GetPaused();	SupportsPSIZE			= rhs.GetSupportsPSIZE();	LastTime				= rhs.GetLastTime();	LastEmit				= rhs.GetLastEmit();	for(int i = 0;i < DeadList.size();i++)		DeadList.pop();	for(i = 0;i < rhs.GetDeadList().size();i++)	{		DeadList.push(rhs.GetDeadList().top());		rhs.GetDeadList().pop();	}	ParticleList.clear();	for(vector<Particle>::iterator walker = rhs.GetParticleList().begin();walker != rhs.GetParticleList().end();walker++)	{		ParticleList.push_back(*walker);	}}// ConstructorScreenEntityParticleSystem::ScreenEntityParticleSystem(RECT cScreenRectangle,														DWORD cFlags,														int cDepthLevel,														string cBehaviourModifier,														string cParticleTexture)														:ScreenEntityBase(cScreenRectangle,																			cFlags,																			cDepthLevel,																			cBehaviourModifier),																			ParticleTextureLocation(cParticleTexture){	LastTime = timeGetTime();	LastEmit = 0.0f;}// DeconstructorScreenEntityParticleSystem::~ScreenEntityParticleSystem(){	// This may also need to be filled in at a later date}// Assignment operatorScreenEntityParticleSystem& ScreenEntityParticleSystem::operator =(ScreenEntityParticleSystem & rhs){	if(this == &rhs)		return *this;	// Call the base class operator so that the full assignment in carried out	ScreenEntityBase::operator=(rhs);	// Copy all the simple types	ParticleTextureLocation = rhs.GetParticleTextureLocation();	Gravity					= rhs.GetGravity();	ReleaseArea				= rhs.GetReleaseArea();	ReleaseInterval			= rhs.GetReleaseInterval();	MaxParticles			= rhs.GetMaxParticles();	VBOffset				= rhs.GetVBOffset();	Flush					= rhs.GetFlush();	Discard					= rhs.GetDiscard();	MaxPointSize			= rhs.GetMaxPointSize();		Size					= rhs.GetSize();	VelocityVar				= rhs.GetVelocityVar();	Paused					= rhs.GetPaused();	SupportsPSIZE			= rhs.GetSupportsPSIZE();	LastTime				= rhs.GetLastTime();	LastEmit				= rhs.GetLastEmit();	for(int i = 0;i < DeadList.size();i++)		DeadList.pop();	for(i = 0;i < rhs.GetDeadList().size();i++)	{		DeadList.push(rhs.GetDeadList().top());		rhs.GetDeadList().pop();	}	ParticleList.clear();	for(vector<Particle>::iterator walker = rhs.GetParticleList().begin();walker != rhs.GetParticleList().end();walker++)	{		ParticleList.push_back(*walker);	}	return *this;}// Initialise the screen entityHRESULT ScreenEntityParticleSystem::Initialise(){	// Create a vertex buffer for the particle system	if(FAILED(GlobalDirect3D9Device->CreateVertexBuffer(Discard*sizeof(PointVertex),														D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY | D3DUSAGE_POINTS,														D3DFVF_TLVERTEXPOINT,														D3DPOOL_DEFAULT,														&VertexBuffer,														NULL)))	{		return E_FAIL;		DXTRACE_MSG("Could not create vertex buffer in particle system");	}	// Get the max point size	D3DCAPS9 d3dcaps;	GlobalDirect3D9Device->GetDeviceCaps(&d3dcaps);	MaxPointSize = d3dcaps.MaxPointSize;	// Check to see if we can change the size of the point sprites	if(d3dcaps.FVFCaps & D3DFVFCAPS_PSIZE)		SupportsPSIZE = true;	else		SupportsPSIZE = false;	// Create the texture object	if(FAILED(D3DXCreateTextureFromFile(GlobalDirect3D9Device,										ParticleTextureLocation.c_str(),										&ParticleTexture)))	{		return E_FAIL;		DXTRACE_MSG("Could not create texture file in particle system");	}	// Initialise the GlobalGravityVector	GlobalGravityVector.x = 0;	GlobalGravityVector.y = 0.5;	// The initialisation has been successful so we set the valid flag to true	Flags = Flags | SCREENENTITY_VALID;	return S_OK;}HRESULT ScreenEntityParticleSystem::HandleMessage(UINT MessageType, WPARAM wParam, LPARAM lParam){	// The particle system is not effected by user input or render calls so just return S_OK	return S_OK;}HRESULT ScreenEntityParticleSystem::Update(){	DXTRACE_MSG("********************************************************");	DXTRACE_MSG("Beginning of update");	OutputInformation();		float CurrentTime = timeGetTime();	float ElapsedTime = (float)((CurrentTime - LastTime) * 0.001);	for(int i = 0;i < ParticleList.size();i++)	{		// If the particle is not dead then we process it		if(!(ParticleList.Dead))		{			// If the particle is dead, put it on the dead list			if(((CurrentTime - ParticleList.StartTime) * 0.001) >= ParticleList.LifeTime)			{				// Tell the Particle that it is dead and put a reference to it on the deadlist				ParticleList.Dead = true;				DeadList.push(i);			}			else			{				// The particle is still in it's active lifetime so we updated the position and velocity				// If gravity is switched on then we apply this here				if(Gravity)				{					ParticleList.CurrentVelocity += GlobalGravityVector * ElapsedTime;				}				// Update the position with respect to the velocity				ParticleList.CurrentPosition += ParticleList.CurrentVelocity * ElapsedTime;				// Update the alpha value				 // TODO -- Finsh this			}		}		else		{			// This particle is dead so it does not need to be updated		}	}	// Now we need to emit new particles	if(((CurrentTime - LastEmit) * 0.001f) > ReleaseInterval)	{		// It is time to release a new particle, first check if there are any on the dead list		if(!(DeadList.empty()))		{			// Restart the dead particle			InitialiseParticle(&ParticleList[DeadList.top()]);			// Remove the reference from the deadlist			DeadList.pop();		}		else		{			// There are no dead particles to reactivate so we will make			// another one if there is enough space on the ParticleList			if(ParticleList.size() <= MaxParticles)			{				Particle newParticle;				InitialiseParticle(&newParticle);				ParticleList.push_back(newParticle);			}			else			{				// There is no space for any more particles so we don't do anything			}		}		LastEmit = CurrentTime;	}	// Last update the LastTime float	LastTime = CurrentTime;	DXTRACE_MSG("End of update");	OutputInformation();	DXTRACE_MSG("********************************************************");	return S_OK;}	HRESULT ScreenEntityParticleSystem::RenderScreenEntity(){	// If the system is not paused, then update the particles before they are rendered	if(!(Paused))		Update();	// Set the render states for using point sprites	GlobalDirect3D9Device->SetRenderState(D3DRS_POINTSPRITEENABLE, TRUE);	//GlobalDirect3D9Device->SetRenderState(D3DRS_POINTSCALEENABLE, TRUE);	GlobalDirect3D9Device->SetRenderState(D3DRS_POINTSIZE, FtoDW(Size));	GlobalDirect3D9Device->SetRenderState(D3DRS_POINTSIZE_MIN, FtoDW(1.0f));	//GlobalDirect3D9Device->SetRenderState(D3DRS_POINTSCALE_A, FtoDW(0.0f));	//GlobalDirect3D9Device->SetRenderState(D3DRS_POINTSCALE_B, FtoDW(0.0f));	//GlobalDirect3D9Device->SetRenderState(D3DRS_POINTSCALE_C, FtoDW(1.0f));	GlobalDirect3D9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);	// Used as a reference to the vertex buffer	PointVertex* Vertices;	DWORD NumParticlesToRender= 0;	// Lock the vertex buffer. Fill the vertex buffer in small chunks, using D3DLOCK_NOOVERWRITE.	// When done filling each chunk, call DrawPrimitive and lock the next chunk. When space runs	// out in the vertex buffer, start over at the beginning using D3DLOCK_DISCARD.	// Move the offset to fill the next chunk of the vertex buffer	VBOffset += Flush;	// If about to overflow the buffer, set it back to 0	if(VBOffset >= Discard)		VBOffset = 0;	// Lock the vertex buffer	HRESULT hr;	if(FAILED(hr = VertexBuffer->Lock(VBOffset*sizeof(PointVertex),	// Offset to lock									Flush*sizeof(PointVertex),	// Size to lock									(VOID **) &Vertices,									VBOffset ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD)))	{		DXTRACE_ERR("Failed to lock the vertex buffer in the render method of the particle system", hr);		return hr;	}	GlobalDirect3D9Device->SetStreamSource(0, VertexBuffer, 0, sizeof(PointVertex));	// Render each particle	for(vector<Particle>::iterator walker = ParticleList.begin(); walker != ParticleList.end();walker++)	{		if(!(walker->Dead))		{			D3DXVECTOR2	vVel(walker->CurrentVelocity);			Vertices->posit = walker->CurrentPosition;			Vertices->color = walker->Colour;			Vertices++;			if(++NumParticlesToRender == Flush)			{				// Done filling this chunk of the vertex buffer so unlock and draw so that the				// next chunk can start being filled				VertexBuffer->Unlock();				GlobalDirect3D9Device->SetFVF(D3DFVF_TLVERTEXPOINT);				if(FAILED(hr = GlobalDirect3D9Device->DrawPrimitive(D3DPT_POINTLIST,																	VBOffset,																	NumParticlesToRender)))				{					DXTRACE_ERR("Draw primitive failed inside the Flush-if in the render method", hr);					return hr;				}				// Lock the next chunk of the vertex buffer. If we are at the end of the vertex buffer				// DISCARD the vertex buffer and start at the beginning. Otherwise, specify NOOVERWRITE				// so we can continue filling the VB while the previous chunk is drawing.				VBOffset += Flush;				// If about to overflow reset the offset to 0				if(VBOffset >= Discard)					VBOffset = 0;				if(FAILED(hr = VertexBuffer->Lock(VBOffset*sizeof(PointVertex),													Flush*sizeof(PointVertex),													(VOID **) &Vertices,													VBOffset ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD)))				{					DXTRACE_ERR("Failed to lock the vertex buffer in the latter part of the render method", hr);					return hr;				}				NumParticlesToRender = 0;			}		}		else		{			// Do nothing as this particle is dead		}	}	// Unlock the vertex buffer	VertexBuffer->Unlock();	// Render any remaining particles	if(NumParticlesToRender)	{		GlobalDirect3D9Device->SetFVF(D3DFVF_TLVERTEXPOINT);		if(FAILED(hr = GlobalDirect3D9Device->DrawPrimitive(D3DPT_POINTLIST,															VBOffset,															NumParticlesToRender)))		{			DXTRACE_ERR("Draw primitive failed inside the final part of the render method", hr);			return hr;		}	}	// Reset render states	GlobalDirect3D9Device->SetRenderState(D3DRS_POINTSPRITEENABLE, FALSE);	GlobalDirect3D9Device->SetRenderState(D3DRS_POINTSCALEENABLE, FALSE);	GlobalDirect3D9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);	GlobalDirect3D9Device->SetFVF(D3DFVF_TLVERTEX);	// Re-set the stream source	GlobalDirect3D9Device->SetStreamSource(0, GlobalDirect3D9VertexBuffer, 0, sizeof(TLVERTEX));	return S_OK;}void ScreenEntityParticleSystem::OutputInformation(){	char output[500];	// Walk through the particle list and for each particle output the colour, position,	// velocity, starttime, lifetime and alpha decay	for(vector<Particle>::iterator walker = ParticleList.begin();walker != ParticleList.end();walker++)	{		sprintf(output, "Position x: %f, Position y: %f, Velocity x: %f, Velocity y: %f, Starttime: %f, LifeTime: %f, AlphaDecay: %f",						walker->CurrentPosition.x,						walker->CurrentPosition.y,						walker->CurrentVelocity.x,						walker->CurrentVelocity.y,						walker->StartTime,						walker->LifeTime,						walker->AlphaDecay);		DXTRACE_MSG(output);	}}void ScreenEntityParticleSystem::Release(){	SAFE_RELEASE(VertexBuffer);	SAFE_RELEASE(ParticleTexture);	//TODO -- Is this necessary	//DeadList.clear();	ParticleList.clear();}void ScreenEntityParticleSystem::ControlEntity(bool start){	if(start)		Paused = false;	else		Paused = true;}void ScreenEntityParticleSystem::InitialiseParticle(Particle* DeadParticle){	// Tell the particle that it is alive!	DeadParticle->Dead = false;	// Place the particle somewhere within the release area	DeadParticle->CurrentPosition.x = GetRandomNumber(ReleaseArea.left, ReleaseArea.right);	DeadParticle->CurrentPosition.y = GetRandomNumber(ReleaseArea.top, ReleaseArea.bottom);	// Set the intial velocity, colour, lifetime and alpha decay in accordance with the type of effect being created	if((Flags & PARTICLE_FIRE) > 0)	{		// ************** VELOCITY ****		// If this is fire then all the particles need to be sent more or less upwards so we		// set the y value to -2 (-y is up) and then add a Random between -10 and 10 divided		// 10 leading to +/-1		DeadParticle->CurrentVelocity.y = -2.0f;		DeadParticle->CurrentVelocity.y += (float)(GetRandomNumber(-10, 10)/10);		// The x is set straight up the middle (0) but we vary it with a random number between		// 10 and -10 which is then divide by 20 giving a maximum of 0.5 either way. That equates		// to a maximum angle of 22.5 degrees which may have to be reduced		DeadParticle->CurrentVelocity.x = 0.0f;		DeadParticle->CurrentVelocity.x += (float)(GetRandomNumber(-10,10)/20);		// ************** COLOUR ****		// Define each colour element (plus the alpha)		float r, g, b, a, random;		// Calculate the random to be 0.2 either way. So get a random number between -10 and		// 10 and then divide it by fifty.		//random = (float)(GetRandomNumber(-10,10)/50); // Random		r = 0.5f;// + random; // Red		g = 0.5f;// + random; // Green		b = 0.5f;// + random; // Blue		a = 0.5f; // Start the alpha at full				// Set the colour		DeadParticle->Colour = D3DXCOLOR(r, g, b, a);		// ************** LIFETIME ****		// Alter the life time by 0.5 either way		DeadParticle->LifeTime = 10.5f;// + (float)((GetRandomNumber(-10,10)/20));		// ************** ALPHA DECAY ****		// Alter the alpha decay by 0.1 either way		DeadParticle->AlphaDecay = 0.1;// + (float)((GetRandomNumber(-10, 10)/100));			}	else if((Flags & PARTICLE_DUST) > 0)	{		DXTRACE_MSG("Particle dust not defined yet!!");	}	else	{		DXTRACE_MSG("Particle system is of an invalid type or flag not set");		PostQuitMessage(0);	}	// Set the start time of the particle	DeadParticle->StartTime = timeGetTime();}// Get and set methodsstring ScreenEntityParticleSystem::GetParticleTextureLocation(){	return ParticleTextureLocation;}void ScreenEntityParticleSystem::SetParticleTextureLocation(string newParticleTextureLocation){	ParticleTextureLocation = newParticleTextureLocation;}bool ScreenEntityParticleSystem::GetGravity(){	return Gravity;}void ScreenEntityParticleSystem::SetGravity(bool newGravity){	Gravity = newGravity;}DWORD ScreenEntityParticleSystem::GetVBOffset(){	return VBOffset;}void ScreenEntityParticleSystem::SetVBOffset(DWORD newVBOffset){	VBOffset = newVBOffset;}RECT ScreenEntityParticleSystem::GetReleaseArea(){	return ReleaseArea;}bool ScreenEntityParticleSystem::GetSupportsPSIZE(){	return SupportsPSIZE;}void ScreenEntityParticleSystem::SetReleaseArea(RECT newReleaseArea){	ReleaseArea = newReleaseArea;}float ScreenEntityParticleSystem::GetReleaseInterval(){	return ReleaseInterval;}void ScreenEntityParticleSystem::SetReleaseInterval(float newReleaseInterval){	ReleaseInterval = newReleaseInterval;}int ScreenEntityParticleSystem::GetMaxParticles(){	return MaxParticles;}void ScreenEntityParticleSystem::SetMaxParticles(int newMaxParticles){	MaxParticles = newMaxParticles;}DWORD ScreenEntityParticleSystem::GetFlush(){	return Flush;}void ScreenEntityParticleSystem::SetFlush(DWORD newFlush){	Flush = newFlush;}DWORD ScreenEntityParticleSystem::GetDiscard(){	return Discard;}void ScreenEntityParticleSystem::SetDicard(DWORD newDiscard){	Discard = newDiscard;}float ScreenEntityParticleSystem::GetMaxPointSize(){	return MaxPointSize;}void ScreenEntityParticleSystem::SetMaxPointSize(float newMaxPointSize){	MaxPointSize = newMaxPointSize;}float ScreenEntityParticleSystem::GetSize(){	return Size;}void ScreenEntityParticleSystem::SetSize(float newSize){	Size = newSize;}float ScreenEntityParticleSystem::GetVelocityVar(){	return VelocityVar;}void ScreenEntityParticleSystem::SetVelocityVar(float newVelocityVar){	VelocityVar = newVelocityVar;}bool ScreenEntityParticleSystem::GetPaused(){	return Paused;}void ScreenEntityParticleSystem::SetPaused(bool newPaused){	Paused = newPaused;}float ScreenEntityParticleSystem::GetLastTime(){	return LastTime;}void ScreenEntityParticleSystem::SetLastTime(float newLastTime){	LastTime = newLastTime;}float ScreenEntityParticleSystem::GetLastEmit(){	return LastEmit;}void ScreenEntityParticleSystem::SetLastEmit(float newLastEmit){	LastEmit = newLastEmit;}stack<int> ScreenEntityParticleSystem::GetDeadList(){	return DeadList;}void ScreenEntityParticleSystem::SetDeadList(stack<int> newDeadList){	for(int i = 0;i < DeadList.size();i++)		DeadList.pop();	for(i = 0;i < newDeadList.size();i++)	{		DeadList.push(newDeadList.top());		newDeadList.pop();	}}vector<Particle> ScreenEntityParticleSystem::GetParticleList(){	return ParticleList;}void ScreenEntityParticleSystem::SetParticleList(vector<Particle> newParticleList){	ParticleList.clear();	for(vector<Particle>::iterator walker = newParticleList.begin();walker != newParticleList.end();walker++)	{		ParticleList.push_back(*walker);	}}

If anybody could find time to have a quick look through I would be extremely apreciative. This is driving me crazy now.

Kind regards.

Mark Coleman

##### Share on other sites
Ok, I have found that I wasn't setting the correct texture to be rendered but I am still getting an annoying flashing of the particles. Now the particles are permanently visible but they seem to be flashing in groups independent of each other, some will be flashing whilst others are not?

I have had a look at some screen shots and it looks like the number of particles in each 'group' (whether flashing or not) is equal to the flush size of one 'batch' of particles which is sent to the vertex buffer in one go. This seems to indicate that there is a problem with my vertex buffer, what do you think?

Has anybody seen this before? Am I wasting my time asking or not? Nobody seems to have any idea..!

Anyway, a huge thanks for any help to solve this extremely annoying problem.

Kind regards.

Mark Coleman

##### Share on other sites
Ok I think I found your problem
I think its on this file right here

ScreenEntityParticleSystem.h

I saw a comment of yours saying amount of alpha that decays each time, the alpha channel is related to the transparencie(however you write this) of the RGB channels so thats why the particles are flashing I think that you made some kinda of loop on this so they are becoming transparent and solid in a great speed so it looks like flashes
Hope this helped a bit
If not I dont know
At least also please take a time to look at my thread and try to solve my small 2 mistakes :)

##### Share on other sites
Thats a good point... I will check that out now...

Regards.

Mark Coleman

• 10
• 17
• 9
• 14
• 41