Kinda lost in Particle Engine :(

Started by
11 comments, last by POLSKASH 18 years, 10 months ago
Having finished my first 2D game, I'm trying to make the leap into particle engines. I've been trying to write my own engine, pulling in ideas from 2 different sources. My engine compiles right now, but nothing is drawing to the screen in my little test application. I'm basically using Jim Adam's Roleplaying book for rendering help, and another online tutorial for the design ideas. What I don't like about this book is that he gives you an overview of C++ and all this other bullcrap about writing a story for games, and attempts to teach you about particles in 2 pages. WTF? Anyways, I don't fully understand what the D3DMATERIAL9 object does in his example, and all these SetRenderState calls. Basically I'm just lost at the moment, but I really don't want to throw away what I have, I've worked hard looking at both sources and putting this together. I really think I'm on the right track though. Any suggestions/pointers/advice and clue as to why this is not working as it is would be appreciated. Here's what I have: Particle Two structs and my particle system class

#ifndef ParticleSystem_H_	
#define ParticleSystem_H_

#include <d3d9.h>
#include <D3DX9.h>

#define D3D_OVERLOADS
#define VERTEX_FVF (D3DFVF_XYZ | D3DFVF_TEX1)

struct sVertex
{
    void Set( float fX, float fY, float fZ, float fU, float fV ) 
    {
        x = fX; y = fY; z = fZ;
		u = fU; v = fV;
    }
	float x, y, z;
	float u, v;
};

struct Particle
{
    D3DXVECTOR3 Position;         // Position of particle in world-space
    D3DXVECTOR3 OldPos;           // Position in last frame
    D3DXVECTOR3 Velocity;         // Velocity along the three axes
    D3DXVECTOR3 Acceleration;     // Acceleration along the three axes
    int Age;                    // Age of particle (sum of TimePassed passed to UpdateSystem)
    float Size;                 // Size of particle
    bool Alive;                 // Does this particle need to be updated?
	sVertex Verts[4];
};

class ParticleSystem
{
protected:
	 IDirect3DDevice9*	D3D_device;
     Particle *Particles;
     int NumParts;
	 D3DXVECTOR3 SystemOrigin;  // origin of system in world-space (origin param of constructor)

public:
	ParticleSystem(int numparts, float xOrig, float yOrig, float zOrig, int width, int height, IDirect3DDevice9* device); 
    virtual ~ParticleSystem();                         // destructor

	virtual void UpdateOrigin(float x, float y, float z){SystemOrigin.x = x; SystemOrigin.y = y; SystemOrigin.z = z;}

	virtual void ActivateSystem();
    virtual void ResetSystem(float xOrig, float yOrig, float zOrig); // Initializes system, don't have to overwrite
    virtual void SetParticleDefaults(int i) = 0;    // resets particle; must overwrite
    virtual void UpdateSystem() = 0;    // must overwrite
    virtual void RenderSystem() = 0;   // must overwrite

	IDirect3DTexture9*	srcTexture;
	IDirect3DTexture9*	loadTexture(char* filename, D3DCOLOR key);
	
};

#endif

ParticleSystem function definitions

#include "ParticleSystem.h"

ParticleSystem::ParticleSystem(int numparts, float xOrig, float yOrig, float zOrig, int width, int height, IDirect3DDevice9* device)
{ 
	D3D_device = device;
    NumParts = numparts;                    // store number of particles
    Particles = new Particle[NumParts];    // allocate particles array
	for(int i = 0; i < NumParts; i++)
	{
		Particles.Verts[0].Set( 0.0f, (float)height, 0.0f, 0.0f, 0.0f );
		Particles.Verts[1].Set( (float)width, (float)height, 0.0f, 1.0f, 0.0f );
		Particles.Verts[2].Set( 0.0f, 0.0f, 0.0f, 0.0f, 1.0f );
		Particles.Verts[3].Set( (float)width, 0.0f, 0.0f, 1.0f, 1.0f );
	}	

    SystemOrigin.x = xOrig;// store system origin
	SystemOrigin.y = yOrig;
	SystemOrigin.z = zOrig;
}

ParticleSystem::~ParticleSystem()
{
    if (Particles) { delete[] Particles; Particles = NULL; }
}

void ParticleSystem::ActivateSystem()
{
	for(int i = 0; i < NumParts; i++)
	{
		Particles.Alive = true;
	}
}

void ParticleSystem::ResetSystem(float xOrig, float yOrig, float zOrig)
{
	SystemOrigin.x = xOrig;
	SystemOrigin.y = yOrig;
	SystemOrigin.z = zOrig;

    for (int i=0; i < NumParts; i++)
        SetParticleDefaults(i);
}

IDirect3DTexture9* ParticleSystem::loadTexture(char* filename,D3DCOLOR key)
{
    // Second parameter is the color key. Use 0xFFFF00FF for magenta. 0xFF000000 for black.
    // Use 0x00000000 for no 'color key' substitution

	IDirect3DTexture9*	texture;
    D3DXIMAGE_INFO SrcInfo; // Optional 


    if(FAILED(D3DXCreateTextureFromFileEx( D3D_device, filename, 0, 0, 1, 0, 
          D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, D3DX_FILTER_NONE, D3DX_DEFAULT, 
          key, &SrcInfo , NULL, &texture)))
		  {
			MessageBox(NULL, "Failed to load paddle texture", "Error!",
			MB_ICONEXCLAMATION | MB_OK);
	}

	return texture;
}

I create a SparkSystem class to attempt to use my ParticleSystem class, but nothing is drawing.

#ifndef SparkSystem_H_
#define SparkSystem_H_

#include "ParticleSystem.h"

class SparkSystem : public ParticleSystem
{
public:
	SparkSystem(int numparts, char* filename, IDirect3DDevice9* device, float xOrig, float yOrig, float zOrig, int width, int height);
	virtual ~SparkSystem();

    //float Width, Depth, Ground; // size of system. Can be freely changed after initialization.

    virtual void SetParticleDefaults(int i);
    virtual void UpdateSystem();
    virtual void RenderSystem();

private:

	IDirect3DTexture9*	ParticleTexture;
};

#endif

And the definitions for the SparkSystem class.

#include "SparkSystem.h"

SparkSystem::SparkSystem(int numparts, char* filename, IDirect3DDevice9* device, float xOrig, float yOrig, float zOrig, int width, int height) : ParticleSystem(numparts, xOrig, yOrig, zOrig, width, height, device)
{
	ResetSystem(xOrig, yOrig, zOrig);
	srcTexture = loadTexture(filename, 0x00000000);
}

SparkSystem::~SparkSystem()
{
	if(Particles)
	{
		delete[] Particles; 
		Particles = NULL; 
	}
	srcTexture->Release();
}

void SparkSystem::SetParticleDefaults(int i)
{
    // position the particles somewhere near the system origin (same height, within
    // rectangle specified by Width and Depth). Give them random speed downwards:
    Particles.Position = D3DXVECTOR3(
        SystemOrigin.x, SystemOrigin.y, SystemOrigin.z);
    Particles.Velocity = D3DXVECTOR3(
        1, 2, 0);
    Particles.Acceleration = D3DXVECTOR3(0, 0, 0);
    Particles.Size = 1.0f;
	Particles.Age = 0;
	Particles.Alive = false;
}

void SparkSystem::UpdateSystem()
{
	for (int i=0; i < NumParts; i++)
    {
		Particles.Age += 1;
        Particles.Position += Particles.Velocity * 1;
        Particles.Velocity += Particles.Acceleration;
        if (Particles.Age >= 500) 
			SetParticleDefaults(i);
    }
}

void SparkSystem::RenderSystem()
{
	D3DXMATRIX matWorld, matView, matTransposed;
	D3DXMATRIX matTrans, matScale;
	D3DMATERIAL9 d3dm;
	IDirect3DVertexBuffer9* vertBuffer;
	BYTE* Ptr;

	D3D_device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
	D3D_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
	D3D_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);

	D3D_device->SetRenderState(D3DRS_AMBIENT, 0xffffffff);

	for(int i = 0; i < NumParts; i++)
	{
		if(Particles.Alive)
		{
			D3D_device->CreateVertexBuffer(sizeof(sVertex)*4, 0, VERTEX_FVF, D3DPOOL_DEFAULT, &vertBuffer,NULL);
			vertBuffer->Lock(0, 0, (void**)&Ptr,0);
			for(int j = 0; j < 4; j++)
				memcpy(Ptr, (void*)&Particles.Verts[j], sizeof(sVertex));
			vertBuffer->Unlock();

			D3D_device->SetStreamSource(0, vertBuffer, 0, sizeof(sVertex));
			D3D_device->SetTexture(0, srcTexture);

			ZeroMemory(&d3dm, sizeof(D3DMATERIAL9));
			d3dm.Diffuse.r = d3dm.Ambient.r = 1.0f;
			d3dm.Diffuse.g = d3dm.Ambient.g = 1.0f;
			d3dm.Diffuse.b = d3dm.Ambient.b = 1.0f;
			d3dm.Diffuse.a = d3dm.Ambient.a = 1.0f;

			D3D_device->SetMaterial(&d3dm);

			D3DXMatrixScaling(&matScale, Particles.Size, Particles.Size, Particles.Size);
			D3DXMatrixTranslation(&matTrans,Particles.Position.x,Particles.Position.y,Particles.Position.z);
			D3D_device->GetTransform(D3DTS_VIEW, &matView);
			D3DXMatrixTranspose(&matTransposed, &matView);

			D3DXMatrixMultiply(&matWorld, &matScale, &matTransposed);
			D3DXMatrixMultiply(&matWorld, &matWorld, &matTrans);

			D3D_device->SetTransform(D3DTS_WORLD, &matWorld);
			D3D_device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
		}
	}
	D3D_device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
}

There are some things so stupid that only an intellect could believe them.
Advertisement
"struct sVertex" can't have functions in it. I remember reading that somewhere, but I can't seem to find it in DirectX's help files at the moment. The only render state that I would think you wouldn't be familiar with would be D3DBLEND_ONE, which basically just takes the black out of your texture image.

You'd probably have a better chance searching GameDev.net for a better explanation of particle systems. I'm not familiar with the way this one is done. I usually do it a little different.
Hmm, are you sure it can't? Another user was helping me initializing the array and told me to use that function. However, I see what you might be getting at. The sizeof(sVertex) won't be yielding the correct size for the vertex buffer if there is an extra function in there right?
There are some things so stupid that only an intellect could believe them.
Yeah, that's what I was getting at. Also, since you're in 2D, did you set the appropriate render states and texture stage states at initialization? I assumed you did, but that could also be a problem.
Which states are those? I wasn't aware of special states when working in 2D.
There are some things so stupid that only an intellect could believe them.
These are the usual ones I set accordingly.

//this stuff is usually used in 3D//that you want to turn off(Device)->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE);(Device)->SetRenderState(D3DRS_LIGHTING,FALSE);//these are only needed if you plan on//using the alpha channel in your textures(Device)->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);(Device)->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);(Device)->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);(Device)->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_MODULATE);
A function will be fine in the vertex structure. If it was a virtual function, it wouldn't work, but a regular function is fine.
Why isn't anything drawing?! This is depressing. I'm using a .png texture with some transparency, and these are my currently set render states:

D3D_device->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);
D3D_device->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
D3D_device->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);

My main function is straight-forward, nothing special:

#define WIN32_LEAN_AND_MEAN#include "WindowsClass.h"#include <D3DX9.h>#include <ctime>#pragma comment(lib,"d3d9.lib")#pragma comment(lib,"d3dx9.lib")#pragma comment(lib,"dxguid.lib")#include "SparkSystem.h"WindowsClass* window;MSG Msg;HWND hWnd;HINSTANCE hInst;clock_t time_now;clock_t movement_timer;IDirect3D9*				D3D;IDirect3DDevice9*		D3D_device;D3DDISPLAYMODE			d3ddm;D3DPRESENT_PARAMETERS	d3dpp;bool isPlaying = true;void initD3D();void uninitD3D();int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd ){	hInst = hInstance;	window = new WindowsClass( hInstance );	hWnd = window->getHWND();	initD3D();	ParticleSystem* sparks = new SparkSystem(4, "spark.png", D3D_device, 200, 200, 0, 16, 16);	sparks->ActivateSystem();	while(Msg.message != WM_QUIT && isPlaying)	{		if(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))		{			TranslateMessage(&Msg);			DispatchMessage(&Msg);		}		else		{			time_now = clock();			if(time_now - movement_timer > CLK_TCK/40) 			{				movement_timer = time_now;							}						D3D_device->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_RGBA(255,255,255,0),1.0f,0);			D3D_device->BeginScene();			sparks->UpdateSystem();			sparks->RenderSystem();						D3D_device->EndScene();			D3D_device->Present(NULL,NULL,NULL,NULL);		}	}	return 0;}void initD3D(){	CoInitialize(NULL);		if((D3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL)	{		MessageBox(NULL, "Creating D3D Object Failed!", "Error!",        MB_ICONEXCLAMATION | MB_OK);	}	//Set up display mode	d3ddm.Width = 640;	d3ddm.Height = 480;	d3ddm.RefreshRate = 0;	d3ddm.Format = D3DFMT_R5G6B5;	//Set up presentation paramters	d3dpp.Windowed = TRUE;	//d3dpp.SwapEffect = D3DSWAPEFFECT_FLIP;	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;	d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;	d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;	d3dpp.BackBufferFormat = d3ddm.Format;	d3dpp.BackBufferCount = 1;	d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;    d3dpp.MultiSampleQuality = 0;    d3dpp.hDeviceWindow = hWnd;	d3dpp.Flags = 0;	d3dpp.EnableAutoDepthStencil = FALSE;	d3dpp.BackBufferWidth = 640;	d3dpp.BackBufferHeight = 480;	//Acquire device	if(FAILED(D3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd,D3DCREATE_SOFTWARE_VERTEXPROCESSING,							&d3dpp,&D3D_device )))	{		MessageBox(NULL, "Creating Device Failed", "Error!",        MB_ICONEXCLAMATION | MB_OK);	}}void uinitD3D(){	CoUninitialize();	UnregisterClass("GameClass", hInst);}
There are some things so stupid that only an intellect could believe them.
Try adding the following render states:
pDevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_SELECTARG1);
pDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pDevice->SetTextureStageState(0,D3DTSS_ALPHAOP,D3DTOP_SELECTARG1);
pDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);

pDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE);
pDevice->SetRenderState(D3DRS_LIGHTING,FALSE);

Cull mode might be throwing it off, as might the color+alpha sources. Are you drawing anything else? What color is your clear color? A common mistake is to draw nothing else, and have a black clear color, then when your particles show up (incorrectly, but) black you don't see anything.
I added those additional lines, but to no avail. What do you mean what color I am using for my clear color? My texture is opaque blue in the center, and gradually becomes transparent going towards the edges, but it is never fully transparent.
There are some things so stupid that only an intellect could believe them.

This topic is closed to new replies.

Advertisement