Sign in to follow this  

Particle system in DX10

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

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello, I have been learning a bit about DX10, so I decided to tackle the example on the particle system in the DX10 tutorial that comes in the SDK, I read it and then started to code a version of if that does not use DXUT (as the example in the tutorial does), however all I am getting a black screen instead of the "fireworks" display I get when I run the code of the tutorial. I have checked my code but something still eludes me. Could someone please help me? Here's my CPP code:
#include <windows.h>
#include <tchar.h>
#include <d3d10.h>
#include <d3dx10.h>

#define MAX_PARTICLES 30000
#define SAFE_DELETE_ARRAY(p) { if (p) { delete[] (p);   (p)=NULL; } }

HINSTANCE g_hInst;	//global handle for the app
HWND g_WndHandle;		//global variable for window handle

int g_Width = 640;
int g_Height = 480;

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
bool InitWindow(HINSTANCE, int, int);

ID3D10Device *g_pD3DDevice = NULL;
IDXGISwapChain *g_pSwapChain = NULL;
ID3D10RenderTargetView *g_pRenderTargetView = NULL;
ID3D10Texture2D	*g_pDepthStencil = NULL;
ID3D10DepthStencilView *g_pDepthStencilView = NULL;
ID3D10Effect *g_pEffect10 = NULL;
ID3D10InputLayout *g_pParticleVertexLayout = NULL;
ID3D10Buffer *g_pParticleStart;
ID3D10Buffer *g_pParticleStreamTo;
ID3D10Buffer *g_pParticleDrawFrom;
ID3D10ShaderResourceView *g_pParticleTexRV;
ID3D10Texture1D *g_pRandomTexture;
ID3D10ShaderResourceView *g_pRandomTexRV;

ID3D10EffectTechnique *g_pRenderParticles;
ID3D10EffectTechnique *g_pAdvanceParticles;
ID3D10EffectMatrixVariable *g_pmWorldViewProj;
ID3D10EffectMatrixVariable *g_pmInvView;
ID3D10EffectScalarVariable *g_pfGlobalTime;
ID3D10EffectScalarVariable *g_pfElapsedTime;
ID3D10EffectScalarVariable *g_pSecondsPerFirework;
ID3D10EffectScalarVariable *g_pNumEmber1s;
ID3D10EffectScalarVariable *g_pMaxEmber2s;
ID3D10EffectVectorVariable *g_pvFrameGravity;
ID3D10EffectShaderResourceVariable *g_pDiffuseTex;
ID3D10EffectShaderResourceVariable *g_pRandomTex;

D3DXMATRIX ViewMatrix;
D3DXMATRIX ProjMatrix;
bool g_bFirst = true;

struct PARTICLE_VERTEX
{
	D3DXVECTOR3 Pos;
	D3DXVECTOR3 vel;
	float Timer;
	UINT Type;
};

bool InitDirect3D();
bool InitParticleSystem();
void ShutDownDirect3D();
void Render();
void PreFrame();
void DoFrame(float, float);
void PostFrame();
HRESULT CreateParticleBuffer(ID3D10Device*);
HRESULT CreateRandomTexture(ID3D10Device*);
bool AdvanceParticles(ID3D10Device*, float, float, D3DXVECTOR4);
bool RenderParticles(ID3D10Device*, D3DXMATRIX&, D3DXMATRIX&);

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	//initialize the window
	if(!InitWindow(hInstance, g_Width, g_Height))
	{
		return false;
	}

	if(!InitDirect3D())
	{
		ShutDownDirect3D();
		return false;
	}

	//main message loop
	MSG msg = {0};
	while(WM_QUIT != msg.message)
	{
		while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE)
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		//additional game logic...
		Render();
	}

	ShutDownDirect3D();

	return (int)msg.wParam;
}

bool InitWindow(HINSTANCE hInstance, int width, int height)
{
	WNDCLASSEX wcex;

	//Fill in the WNDCLASSEX structure, This describes how the window will look to the system
	wcex.cbSize = sizeof(WNDCLASSEX);	//size of the structure
	wcex.style = CS_HREDRAW | CS_VREDRAW;	//the class style
	wcex.lpfnWndProc = (WNDPROC)WndProc;	//the window procedure callback
	wcex.cbClsExtra = 0;					//extra bytes to allocate for this class
	wcex.cbWndExtra = 0;					//extra bytes to alloate for this instance
	wcex.hInstance = hInstance;				//handle to the application instance
	wcex.hIcon = 0;							//icon to associate with the application
	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);	//the default cursor to use
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);	//the background color
	wcex.lpszMenuName = NULL;	//the resource name for the menu
	wcex.lpszClassName = TEXT("DirectXExample");	//class name being created
	wcex.hIconSm = 0;	//handle to small icon
	RegisterClassEx(&wcex);

	//resize the window
	RECT rect = {0, 0, width, height};
	AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);

	//create the window from the class above
	g_WndHandle = CreateWindow(TEXT("DirectXExample"),
		TEXT("DirectXExample"),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		rect.right - rect.left,
		rect.bottom - rect.top,
		NULL,
		NULL,
		hInstance,
		NULL);

	if(!g_WndHandle)
	{
		return false;
	}

	//display the window on the screen
	ShowWindow(g_WndHandle, SW_SHOW);
	UpdateWindow(g_WndHandle);

	return true;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	//check any available messages from the queue
	switch(message)
	{
		//allow the user to press the Escape key to end the application
		case WM_KEYDOWN:	
			switch(wParam)
			{
				//check if the use hit the escape key
				case VK_ESCAPE:
					PostQuitMessage(0);
					break;
			}
			break;
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
	}

	//always return the message to the default widow procedure for further processing
	return DefWindowProc(hWnd, message, wParam, lParam);
}

bool InitDirect3D()
{
	DXGI_SWAP_CHAIN_DESC swapChainDesc;
	ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));

	//fill the values
	swapChainDesc.BufferCount = 1;
	swapChainDesc.BufferDesc.Width = g_Width;
	swapChainDesc.BufferDesc.Height = g_Height;
	swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
	swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
	swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	swapChainDesc.OutputWindow = g_WndHandle;
	swapChainDesc.SampleDesc.Count = 1;
	swapChainDesc.SampleDesc.Quality = 0;
	swapChainDesc.Windowed = TRUE;

	//create the D3D device and swap chain
	HRESULT hr = D3D10CreateDevice(NULL,
		D3D10_DRIVER_TYPE_HARDWARE,
		NULL,
		0,
		D3D10_SDK_VERSION,
		&g_pD3DDevice);

	if(FAILED(hr))
		return false;

	IDXGIDevice *pDXGIDevice;
	hr = g_pD3DDevice->QueryInterface(__uuidof(IDXGIDevice), (void **)&pDXGIDevice);
	
	if(FAILED(hr))
		return false;

	IDXGIAdapter *pDXGIAdapter;
	hr = pDXGIDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&pDXGIAdapter);
	
	if(FAILED(hr))
		return false;

	IDXGIFactory *pIDXGIFactory;
	hr = pDXGIAdapter->GetParent(__uuidof(IDXGIFactory), (void **)&pIDXGIFactory);

	if(FAILED(hr))
		return false;

	hr = pIDXGIFactory->CreateSwapChain(g_pD3DDevice, &swapChainDesc, &g_pSwapChain);

	if(FAILED(hr))
		return false;

	//get the backbuffer from the swap chain
	ID3D10Texture2D *pBackBuffer;
	hr = g_pSwapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*)&pBackBuffer);
	
	if(FAILED(hr))
		return false;

	//create the render target view
	hr = g_pD3DDevice->CreateRenderTargetView(pBackBuffer, NULL, &g_pRenderTargetView);

	if(FAILED(hr))
		return false;

	//release the backbuffer
	pBackBuffer->Release();

	//make sure render target was created
	if(FAILED(hr))
		return false;

	// Create depth stencil texture
    D3D10_TEXTURE2D_DESC descDepth;
    descDepth.Width = g_Width;
    descDepth.Height = g_Height;
    descDepth.MipLevels = 1;
    descDepth.ArraySize = 1;
    descDepth.Format = DXGI_FORMAT_D32_FLOAT;
    descDepth.SampleDesc.Count = 1;
    descDepth.SampleDesc.Quality = 0;
    descDepth.Usage = D3D10_USAGE_DEFAULT;
    descDepth.BindFlags = D3D10_BIND_DEPTH_STENCIL;
    descDepth.CPUAccessFlags = 0;
    descDepth.MiscFlags = 0;
    hr = g_pD3DDevice->CreateTexture2D(&descDepth, NULL, &g_pDepthStencil);
    if(FAILED(hr))
        return false;

    // Create the depth stencil view
    D3D10_DEPTH_STENCIL_VIEW_DESC descDSV;
    descDSV.Format = descDepth.Format;
    descDSV.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2D;
    descDSV.Texture2D.MipSlice = 0;
    hr = g_pD3DDevice->CreateDepthStencilView(g_pDepthStencil, &descDSV, &g_pDepthStencilView);
    if(FAILED(hr))
        return false;

	g_pD3DDevice->OMSetRenderTargets(1, &g_pRenderTargetView, g_pDepthStencilView);
	//g_pD3DDevice->OMSetRenderTargets(1, &g_pRenderTargetView, NULL);

	//create and set the viewport
	D3D10_VIEWPORT viewPort;
	viewPort.Width = g_Width;
	viewPort.Height = g_Height;
	viewPort.MinDepth = 0.0f;
	viewPort.MaxDepth = 1.0f;
	viewPort.TopLeftX = 0;
	viewPort.TopLeftY = 0;
	g_pD3DDevice->RSSetViewports(1, &viewPort);

	D3DXVECTOR3 Eye(0.0f, 0.0f, -170.0f);
    D3DXVECTOR3 At(0.0f, 70.0f, 0.0f);
    D3DXVECTOR3 Up(0.0f, 1.0f, 0.0f);
    D3DXMatrixLookAtLH(&ViewMatrix, &Eye, &At, &Up);

	D3DXMatrixPerspectiveFovLH(&ProjMatrix, (float)D3DX_PI * 0.25f, g_Width / (float)g_Height, 0.1f, 100.0f);

	if(!InitParticleSystem())
		return false;
	
	return true;
}

void Render()
{
	if(g_pD3DDevice != NULL)
	{
		PreFrame();

		static DWORD timeLastFrameStarted = 0;
		static float t = 0.0f;
		static DWORD dwTimeStart = 0;
        
		DWORD dwTimeCur = GetTickCount();
        
		if(dwTimeStart == 0)
		{
			dwTimeStart = dwTimeCur;
			timeLastFrameStarted = dwTimeStart;
		}
			
        float timeElapsed = (dwTimeCur - timeLastFrameStarted) / 1000.0f;
		t = (dwTimeCur - dwTimeStart) / 1000.0f;
		timeLastFrameStarted = dwTimeCur;
		
		DoFrame(t, timeElapsed);
		
		PostFrame();
	}
}

void PreFrame()
{
	g_pD3DDevice->ClearRenderTargetView(g_pRenderTargetView, D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f));
	
	g_pD3DDevice->ClearDepthStencilView(g_pDepthStencilView, D3D10_CLEAR_DEPTH, 1.0f, 0);
}

void DoFrame(float fGlobalTime, float fElapsedTime)
{
	g_pD3DDevice->IASetInputLayout(g_pParticleVertexLayout);
	
	D3DXVECTOR4 vGravity = D3DXVECTOR4(0.0f, -9.8f, 0.0f, 0.0f);

	AdvanceParticles(g_pD3DDevice, fGlobalTime, fElapsedTime, vGravity);

	RenderParticles(g_pD3DDevice, ViewMatrix, ProjMatrix);
}

void PostFrame()
{
	g_pSwapChain->Present(0, 0);
}

void ShutDownDirect3D()
{
	//release render target
	if(g_pRenderTargetView)
	{
		g_pRenderTargetView->Release();
	}

	//release the effect
	if(g_pEffect10)
	{
		g_pEffect10->Release();
	}

	if(g_pParticleVertexLayout)
	{
		g_pParticleVertexLayout->Release();
	}

	if(g_pParticleStart)
	{
		g_pParticleStart->Release();
	}

	if(g_pParticleStreamTo)
	{
		g_pParticleStreamTo->Release();
	}

	if(g_pParticleDrawFrom)
	{
		g_pParticleDrawFrom->Release();	
	}

	if(g_pRandomTexture)
	{
		g_pRandomTexture->Release();
	}

	if(g_pRandomTexRV)
	{
		g_pRandomTexRV->Release();
	}
	
	if(g_pParticleTexRV)
	{
		g_pParticleTexRV->Release();
	}

	//release the swapchain
	if(g_pSwapChain)
	{
		g_pSwapChain->Release();
	}

	//release the D3D Device
	if(g_pD3DDevice)
	{
		g_pD3DDevice->Release();
	}
}

bool InitParticleSystem()
{
	HRESULT hr = D3DX10CreateEffectFromFile(L"ParticleSys.fx",
		NULL,
		NULL,
		"fx_4_0",
		D3D10_SHADER_ENABLE_STRICTNESS,
		0,
		g_pD3DDevice,
		NULL,
		NULL,
		&g_pEffect10,
		NULL,
		NULL);

	if(FAILED(hr))
	{
		MessageBox(NULL, L"The FX file cannot be located.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK);
		return false;
	}
		
	g_pRenderParticles = g_pEffect10->GetTechniqueByName("RenderParticles");
	g_pAdvanceParticles = g_pEffect10->GetTechniqueByName("AdvanceParticles");

	g_pmWorldViewProj = g_pEffect10->GetVariableByName("g_mWorldViewProj")->AsMatrix();
	g_pmInvView = g_pEffect10->GetVariableByName("g_mInvView")->AsMatrix();
	g_pfGlobalTime = g_pEffect10->GetVariableByName("g_fGlobalTime")->AsScalar();
	g_pfElapsedTime = g_pEffect10->GetVariableByName("g_fElapsedTime")->AsScalar();
	g_pSecondsPerFirework = g_pEffect10->GetVariableByName("g_fSecondsPerFirework")->AsScalar();
	g_pNumEmber1s = g_pEffect10->GetVariableByName("g_iNumEmber1s")->AsScalar();
	g_pMaxEmber2s = g_pEffect10->GetVariableByName("g_fMaxEmber2s")->AsScalar();
	g_pvFrameGravity = g_pEffect10->GetVariableByName("g_vFrameGravity")->AsVector();
	g_pDiffuseTex = g_pEffect10->GetVariableByName("g_txDiffuse")->AsShaderResource();
	g_pRandomTex = g_pEffect10->GetVariableByName("g_txRandom")->AsShaderResource();

	D3D10_INPUT_ELEMENT_DESC layout[] =
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },  
        { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
		{ "TIMER", 0, DXGI_FORMAT_R32_FLOAT, 0, 24, D3D10_INPUT_PER_VERTEX_DATA, 0 },
		{ "TYPE", 0, DXGI_FORMAT_R32_UINT, 0, 28, D3D10_INPUT_PER_VERTEX_DATA, 0 },
	};

	UINT numElements = sizeof(layout) / sizeof(layout[0]);

	D3D10_PASS_DESC passDesc;
    g_pAdvanceParticles->GetPassByIndex(0)->GetDesc(&passDesc);
    hr = g_pD3DDevice->CreateInputLayout(layout, numElements, passDesc.pIAInputSignature,
		passDesc.IAInputSignatureSize, &g_pParticleVertexLayout);
    
	if(FAILED(hr))
        return false;

	D3DXCOLOR colorMtrlDiffuse(1.0f, 1.0f, 1.0f, 1.0f);
	D3DXCOLOR colorMtrlAmbient(0.35f, 0.35f, 0.35f, 0.35f);

	hr = CreateParticleBuffer(g_pD3DDevice);

	if(FAILED(hr))
        return false;

	hr = D3DX10CreateShaderResourceViewFromFile(g_pD3DDevice,
		L"particle.dds",
		NULL,
		NULL,
		&g_pParticleTexRV,
		NULL);

	if(FAILED(hr))
        return false;

	hr = CreateRandomTexture(g_pD3DDevice);

	if(FAILED(hr))
        return false;

	return true;
}

HRESULT CreateParticleBuffer(ID3D10Device *pD3Ddevice)
{
	HRESULT hr = S_OK;

	D3D10_BUFFER_DESC vbdesc;
	vbdesc.ByteWidth = (UINT)sizeof(PARTICLE_VERTEX);
	vbdesc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
	vbdesc.CPUAccessFlags = 0;
	vbdesc.MiscFlags = 0;
	vbdesc.Usage = D3D10_USAGE_DEFAULT;

	D3D10_SUBRESOURCE_DATA vbInitdata;
	ZeroMemory(&vbInitdata, sizeof(D3D10_SUBRESOURCE_DATA));

	PARTICLE_VERTEX vertStart;
	vertStart.Pos = D3DXVECTOR3(0.0f , 0.0f, 0.0f);
	vertStart.vel = D3DXVECTOR3(0.0f, 40.0f, 0.0f);
	vertStart.Timer = 0.0f;
	vertStart.Type = 0;

	vbInitdata.pSysMem = &vertStart;
	vbInitdata.SysMemPitch = (UINT)sizeof(PARTICLE_VERTEX);

	hr = pD3Ddevice->CreateBuffer(&vbdesc, &vbInitdata, &g_pParticleStart);

	vbdesc.ByteWidth = (UINT)(sizeof(PARTICLE_VERTEX) * MAX_PARTICLES);
	vbdesc.BindFlags |= D3D10_BIND_STREAM_OUTPUT;

	hr = pD3Ddevice->CreateBuffer(&vbdesc, NULL, &g_pParticleDrawFrom);
	hr = pD3Ddevice->CreateBuffer(&vbdesc, NULL, &g_pParticleStreamTo);

	return hr;
}

HRESULT CreateRandomTexture(ID3D10Device *pD3DDevice)
{
	HRESULT hr = S_OK;

	int iNumRandValues = 1024;
    srand(timeGetTime());
	
    D3D10_SUBRESOURCE_DATA InitData;
    InitData.pSysMem = new float[iNumRandValues * 4];
    if(!InitData.pSysMem)
        return E_OUTOFMEMORY;
    InitData.SysMemPitch = iNumRandValues * 4 * sizeof(float);
    InitData.SysMemSlicePitch = iNumRandValues * 4 * sizeof(float);
    for(int i = 0; i < iNumRandValues * 4; i++)
    {
        ((float*)InitData.pSysMem)[i] = float((rand() % 10000) - 5000);
    }

    
	D3D10_TEXTURE1D_DESC dstex;
    dstex.Width = iNumRandValues;
    dstex.MipLevels = 1;
    dstex.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    dstex.Usage = D3D10_USAGE_DEFAULT;
    dstex.BindFlags = D3D10_BIND_SHADER_RESOURCE;
    dstex.CPUAccessFlags = 0;
    dstex.MiscFlags = 0;
    dstex.ArraySize = 1;
    
	hr = pD3DDevice->CreateTexture1D(&dstex, &InitData, &g_pRandomTexture);
    
	SAFE_DELETE_ARRAY(InitData.pSysMem);

    // Create the resource view
    D3D10_SHADER_RESOURCE_VIEW_DESC SRVDesc;
    ZeroMemory(&SRVDesc, sizeof(SRVDesc));
    SRVDesc.Format = dstex.Format;
    SRVDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE1D;
    SRVDesc.Texture2D.MipLevels = dstex.MipLevels;
    hr = pD3DDevice->CreateShaderResourceView(g_pRandomTexture, &SRVDesc, &g_pRandomTexRV);

    return hr;
}

bool AdvanceParticles(ID3D10Device *pD3DDevice, float fGlobalTime, float fElapsedTime, D3DXVECTOR4 vGravity)
{
	ID3D10Buffer *pBuffers[1];

	if(g_bFirst)
		pBuffers[0] = g_pParticleStart;
	else
		pBuffers[0] = g_pParticleDrawFrom;

	UINT stride[1] = { sizeof( PARTICLE_VERTEX) };
    UINT offset[1] = {0};

	pD3DDevice->IASetVertexBuffers(0, 1, pBuffers, stride, offset);
	pD3DDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_POINTLIST);

	pBuffers[0] = g_pParticleStreamTo;
	pD3DDevice->SOSetTargets(1, pBuffers, offset);

	g_pfGlobalTime->SetFloat(fGlobalTime);
	g_pfElapsedTime->SetFloat(fElapsedTime);
	vGravity *= fElapsedTime;
	g_pvFrameGravity->SetFloatVector((float*)&vGravity);
	g_pRandomTex->SetResource(g_pRandomTexRV);
	g_pSecondsPerFirework->SetFloat(0.4f);
	g_pNumEmber1s->SetInt(100);
	g_pMaxEmber2s->SetFloat(15.0f);

	D3D10_TECHNIQUE_DESC techDesc;
	g_pAdvanceParticles->GetDesc(&techDesc);

	for(UINT p = 0; p < techDesc.Passes; ++p)
	{
		g_pAdvanceParticles->GetPassByIndex(p)->Apply(0);
		if(g_bFirst)
			pD3DDevice->Draw(1, 0);
		else
			pD3DDevice->DrawAuto();
	}

	pBuffers[0] = NULL;
	pD3DDevice->SOSetTargets(1, pBuffers, offset);

	ID3D10Buffer *pTemp = g_pParticleDrawFrom;
    g_pParticleDrawFrom = g_pParticleStreamTo;
    g_pParticleStreamTo = pTemp;
	
	g_bFirst = false;
	
	return true;
}

bool RenderParticles(ID3D10Device *pD3DDevice, D3DXMATRIX &mView, D3DXMATRIX &mProj)
{
	D3DXMATRIX mWorldView;
    D3DXMATRIX mWorldViewProj;

	ID3D10Buffer *pBuffers[1] = { g_pParticleDrawFrom };
	UINT stride[1] = { sizeof(PARTICLE_VERTEX) };
    UINT offset[1] = {0};
	pD3DDevice->IASetVertexBuffers(0, 1, pBuffers, stride, offset);
	pD3DDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_POINTLIST);
	
	D3DXMatrixMultiply(&mWorldViewProj, &mView, &mProj);
	g_pmWorldViewProj->SetMatrix((float*)mWorldViewProj);
	g_pDiffuseTex->SetResource(g_pParticleTexRV);
	D3DXMATRIX mInvView;
    D3DXMatrixInverse(&mInvView, NULL, &mView);
	g_pmInvView->SetMatrix((float*)mInvView);

	D3D10_TECHNIQUE_DESC techDesc;
    g_pRenderParticles->GetDesc(&techDesc);

	for(UINT p = 0; p < techDesc.Passes; ++p)
	{
		g_pRenderParticles->GetPassByIndex(p)->Apply(0);
		pD3DDevice->DrawAuto();
	}

	return true;
}
And the shader:
//
// Explanation of different particle types
//
#define PT_LAUNCHER 0 //Firework Launcher - launches a PT_SHELL every so many seconds
#define PT_SHELL    1 //Unexploded shell - flies from the origin and explodes into many PT_EMBERXs
#define PT_EMBER1   2 //basic particle - after it's emitted from the shell, it dies
#define PT_EMBER2   3 //after it's emitted, it explodes again into many PT_EMBER1s
#define PT_EMBER3   4 //just a differently colored ember1
#define P_SHELLLIFE 3.0
#define P_EMBER1LIFE 2.5
#define P_EMBER2LIFE 1.5
#define P_EMBER3LIFE 2.0

struct VSParticleIn
{
	float3 pos : POSITION;
	float3 vel : NORMAL;
	float Timer : TIMER;
	uint Type : TYPE;
};

struct VSParticleDrawOut
{
	float3 pos : POSITION;
	float4 color : COLOR0;
	float radius : RADIUS;
};

struct PSSceneIn
{
	float4 pos : SV_POSITION;
	float2 tex : TEXTURE0;
	float4 color : COLOR0;
};

cbuffer cb0
{
	matrix g_mWorldViewProj;
	matrix g_mInvView;
	float g_fGlobalTime;
	float g_fElapsedTime;
	float g_fSecondsPerFirework = 1.0f;
	int g_iNumEmber1s = 30;
	float g_fMaxEmber2s = 15.0f;
	float4 g_vFrameGravity; 
};

cbuffer cbImmutable
{
	float3 g_positions[4] =
	{
		float3(-1.0f, 1.0f, 0.0f),
		float3(1.0f, 1.0f, 0.0f),
		float3(-1.0f, -1.0f, 0.0f),
		float3(1.0f, -1.0f, 0.0f),
	};
	float2 g_texcoords[4] =
	{
		float2(0.0f, 1.0f),
		float2(1.0f, 1.0f),
		float2(0.0f, 0.0f),
		float2(1.0f, 0.0f),
	};
};

Texture2D g_txDiffuse;
SamplerState g_samLinear
{
	Filter = MIN_MAG_MIP_LINEAR;
	AddressU = Clamp;
	AddressV = Clamp;
};

Texture1D g_txRandom;
SamplerState g_samPoint
{
	Filter = MIN_MAG_MIP_POINT;
	AddressU = Wrap;
};

BlendState AdditiveBlending
{
	AlphaToCoverageEnable = FALSE;
	BlendEnable[0] = TRUE;
	SrcBlend = SRC_ALPHA;
	DestBlend = ONE;
	BlendOp = ADD;
	SrcBlendAlpha = ZERO;
	DestBlendAlpha = ZERO;
	BlendOpAlpha = ADD;
	RenderTargetWriteMask[0] = 0x0F;
};

BlendState NoBlending
{
	AlphaToCoverageEnable = FALSE;
	BlendEnable[0] = FALSE;
};

DepthStencilState DisableDepth
{
	DepthEnable = FALSE;
	DepthWriteMask = ZERO;
};

VSParticleDrawOut VSSceneMain(VSParticleIn input)
{
	VSParticleDrawOut output = (VSParticleDrawOut)0;
	
	output.pos = input.pos;
	output.radius = 1.5f;
	
	if(input.Type == PT_LAUNCHER)
	{
		output.color = float4(1.0f, 0.1f, 0.1f, 1.0f);
		output.radius = 1.0f;
	}
	else if(input.Type == PT_SHELL)
	{
		output.color = float4(0.1f, 1.0f, 1.0f, 1.0f);
		output.radius = 1.0f;
	}
	else if(input.Type == PT_EMBER1)
	{
		output.color = float4(1.0f, 1.0f, 0.1f, 1.0f);
		output.color *= (input.Timer / P_EMBER1LIFE);
	}
	else if(input.Type == PT_EMBER2)
	{
		output.color = float4(1.0f, 0.1f, 1.0f, 1.0f);
	}
	else if(input.Type == PT_EMBER3)
	{
		output.color = float4(1.0f, 0.1f, 0.1f, 1.0f);
		output.color *= (input.Timer / P_EMBER3LIFE);
	}
	
	return output;
}

VSParticleIn VSPassThroughMain(VSParticleIn input)
{
	return input;
}

float3 RandomDir(float fOffset)
{
	float tCoord = (g_fGlobalTime + fOffset) / 300.0f;
	return g_txRandom.SampleLevel(g_samPoint, tCoord, 0.0f);
}

void GSGenericHandler(VSParticleIn input, inout PointStream<VSParticleIn> ParticleOutputStream)
{
	input.pos += input.vel * g_fElapsedTime;
	input.vel += g_vFrameGravity;
	input.Timer -= g_fElapsedTime;
	ParticleOutputStream.Append(input);
}

void GSLauncherHandler(VSParticleIn input, inout PointStream<VSParticleIn> ParticleOutputStream)
{
	if(input.Timer <= 0)
	{
		float3 vRandom = normalize(RandomDir(input.Type));
	
		VSParticleIn output;
		output.pos = input.pos + input.vel * g_fElapsedTime;
		output.vel = input.vel + vRandom * 8.0f;
		output.Timer = P_SHELLLIFE + vRandom.y * 0.5f;
		output.Type = PT_SHELL;
		ParticleOutputStream.Append(output);
		
		input.Timer = g_fSecondsPerFirework + vRandom.x * 0.4f;
	}
	else
	{
		input.Timer -= g_fElapsedTime;
	}
	
	ParticleOutputStream.Append(input);
}

void GSShellHandler(VSParticleIn input, inout PointStream<VSParticleIn> ParticleOutputStream)
{
	if(input.Timer <= 0)
	{
		VSParticleIn output;
		
		float3 vRandom = float3(0.0f, 0.0f, 0.0f);
		
		for(int i = 0; i < g_iNumEmber1s; i++)
		{
			vRandom = normalize(RandomDir(input.Type + i));
			output.pos = input.pos + input.vel * g_fElapsedTime;
			output.vel = input.vel + vRandom * 15.0f;
			output.Timer = P_EMBER1LIFE;
			output.Type = PT_EMBER1;
			ParticleOutputStream.Append(output);
		}
		
		for(int i = 0; i < abs(vRandom.x) * g_fMaxEmber2s; i++)
		{
			vRandom = normalize(RandomDir(input.Type + i));
			output.pos = input.pos + input.vel * g_fElapsedTime;
			output.vel = input.vel + vRandom * 10.0f;
			output.Timer = P_EMBER2LIFE * 0.4f * vRandom.x;
			output.Type = PT_EMBER2;
			ParticleOutputStream.Append(output);
		}
	}
	else
	{
		GSGenericHandler(input, ParticleOutputStream);
	}
}

void GSEmber1Handler(VSParticleIn input, inout PointStream<VSParticleIn> ParticleOutputStream)
{
	if(input.Timer > 0)
	{
		GSGenericHandler(input, ParticleOutputStream);
	}
}

void GSEmber2Handler(VSParticleIn input, inout PointStream<VSParticleIn> ParticleOutputStream)
{
	if(input.Timer <= 0)
	{
		VSParticleIn output;
		
		for(int i = 0; i < 10; i++)
		{
			output.pos = input.pos + input.vel * g_fElapsedTime;
			output.vel = input.vel + normalize(RandomDir(input.Type + i)) * 10.0f;
			output.Timer = P_EMBER3LIFE;
			output.Type = PT_EMBER3;
			ParticleOutputStream.Append(output);
		}
	}
	else
	{
		GSGenericHandler(input, ParticleOutputStream);
	}
}

[maxvertexcount(128)]
void GSAdvanceParticlesMain(point VSParticleIn input[1], inout PointStream<VSParticleIn> ParticleOutputStream)
{
	if(input[0].Type == PT_LAUNCHER)
	{
		GSLauncherHandler(input[0], ParticleOutputStream);
	}
	else if(input[0].Type == PT_SHELL)
	{
		GSShellHandler(input[0], ParticleOutputStream);
	}
	else if(input[0].Type == PT_EMBER1 || input[0].Type == PT_EMBER3)
	{
		GSEmber1Handler(input[0], ParticleOutputStream);
	}
	else if(input[0].Type == PT_EMBER2)
	{
		GSEmber2Handler(input[0], ParticleOutputStream);
	}
}

[maxvertexcount(4)]
void GSSceneMain(point VSParticleDrawOut input[1], inout TriangleStream<PSSceneIn> SpriteStream)
{
	PSSceneIn output;
	
	for(int i = 0; i < 4; i++)
	{
		float3 position = g_positions[i] * input[0].radius;
		position = mul(position, (float3x3)g_mInvView) + input[0].pos;
		output.pos = mul(float4(position, 1.0f), g_mWorldViewProj);
		
		output.color = input[0].color;
		output.tex = g_texcoords[i];
		SpriteStream.Append(output);
	}
	SpriteStream.RestartStrip();
}

float4 PSSceneMain(PSSceneIn input) : SV_Target
{
	return g_txDiffuse.Sample(g_samLinear, input.tex) * input.color;
}

technique10 RenderParticles
{
	pass p0
	{
		SetVertexShader(CompileShader(vs_4_0, VSSceneMain()));
		SetGeometryShader(CompileShader(gs_4_0, GSSceneMain()));
		SetPixelShader(CompileShader(ps_4_0, PSSceneMain()));
		
		SetBlendState(AdditiveBlending, float4(0.0f, 0.0f, 0.0f, 0.0f), 0xFFFFFFFF);
		SetDepthStencilState(DisableDepth, 0);
	}
}

GeometryShader gsStreamOut = ConstructGSWithSO(CompileShader(gs_4_0, GSAdvanceParticlesMain()), "POSITION.xyz; NORMAL.xyz; TIMER.x; TYPE.x");
technique10 AdvanceParticles
{
	pass p0
	{
		SetVertexShader(CompileShader(vs_4_0, VSPassThroughMain()));
		SetGeometryShader(gsStreamOut);
		SetPixelShader(NULL);
		
		SetDepthStencilState(DisableDepth, 0);
	}
}

Share this post


Link to post
Share on other sites

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

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this