Voxel game help

Started by
3 comments, last by Hawkblood 10 years, 2 months ago

I want to create a simple voxel based game. I have followed tutorials on how to draw textured cubes in OpenGL and DirectX. I tried creating a simple world made from cubes. In both DirectX and OpenGL I have the problem that after drawing a reasonable amount of cubes the game slows down to unplayable levels. In directX If I draw 40000 cubes the game slows down to 10fps. Both my cpu and gpu are barely being used. I already updated my drivers and searched the internet for a solution without ant results.

Does anyone know what causes this slowdown? I will include my directX version of the game.

Game.cpp


#include "Game.h"

Game::Game(HWND hWnd)
{
	vBuffer = NULL;
	initD3D(hWnd);
	camLocation = D3DXVECTOR3(0.0f,2.0f,0.0f);
	camRotation = D3DXVECTOR3(0.0f,PI,0.0f);
	lightDirection = D3DXVECTOR3(0.1f,0.1f,-0.1f);
	frames = 0;
	time(&startTime);
}

void Game::initD3D(HWND hWnd)
{
	//Create handle to Direct3D
	d3d = Direct3DCreate9(D3D_SDK_VERSION);

	D3DPRESENT_PARAMETERS d3dpp;

	ZeroMemory(&d3dpp, sizeof(d3dpp));
	d3dpp.Windowed = FALSE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.hDeviceWindow = hWnd;
	d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
	d3dpp.BackBufferWidth = SCREENWIDTH;
	d3dpp.BackBufferHeight = SCREENHEIGHT;
	d3dpp.EnableAutoDepthStencil = TRUE;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

	d3d->CreateDevice(D3DADAPTER_DEFAULT,
		D3DDEVTYPE_HAL,
		hWnd,
		D3DCREATE_SOFTWARE_VERTEXPROCESSING,
		&d3dpp,
		&d3ddev);

	//Set font size and type
	D3DXCreateFont(d3ddev,    // the Direct3D Device
               40, 30,    // font size twenty with the default width
               FW_NORMAL,    // normal font weight
               1,    // no mipmap levels
               FALSE,    // not italic
               DEFAULT_CHARSET,    // default character set
               OUT_DEFAULT_PRECIS,    // default precision
               DEFAULT_QUALITY,    // default quality
               DEFAULT_PITCH || FF_DONTCARE,    // more defaults...
               "Cooper Black",    // typeface "Arial"
			   &d3dfont);    // address of the font object created above

	initGraphics();
	initLight();
	initWorld();

	d3ddev->SetRenderState(D3DRS_LIGHTING, TRUE);
	d3ddev->SetRenderState(D3DRS_ZENABLE, TRUE);
	d3ddev->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(80,80,80));
	d3ddev->SetRenderState(D3DRS_NORMALIZENORMALS, TRUE);

	d3ddev->SetSamplerState(0, D3DSAMP_MAXANISOTROPY, 8);    // anisotropic level
    d3ddev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC);    // minification
    d3ddev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);    // magnification
    d3ddev->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);    // mipmap

	return;
}

void Game::releaseD3D()
{
	delete[] block;
	vBuffer->Release();
	d3ddev->Release();
	d3d->Release();
}

void Game::initGame(HWND hWnd)
{

}

void Game::GO()
{
	getFPS();
	GetCursorPos(&mousePos);
	SetCursorPos(MIDSCREENX,MIDSCREENY);
	mousePos.x -= MIDSCREENX;
	mousePos.y -= MIDSCREENY;

	if(mousePos.x != 0)
	{
		camRotation.x += mousePos.x / 100.0f;
	}
	if(mousePos.y != 0)
	{
		camRotation.y -= mousePos.y / 100.0f;
		if(camRotation.y > PI * 1.5f)
			camRotation.y = PI * 1.5f;
		else if(camRotation.y < PI * 0.5f)
			camRotation.y = PI * 0.5f;
	}
	if(KEY_DOWN(0x57))//W
	{
		camLocation.x -= sin(camRotation.x) * 0.3f;
		camLocation.z += cos(camRotation.x) * 0.3f;
	}
	else if(KEY_DOWN(0x53))//S
	{
		camLocation.x += sin(camRotation.x) * 0.2f;
		camLocation.z -= cos(camRotation.x) * 0.2f;
	}
	if(KEY_DOWN(0x41))//A
	{
		camLocation.x += sin(camRotation.x + 0.5f * PI) * 0.2f;
		camLocation.z -= cos(camRotation.x + 0.5f * PI) * 0.2f;
	}
	else if(KEY_DOWN(0x44))//D
	{
		camLocation.x -= sin(camRotation.x + 0.5f * PI) * 0.2f;
		camLocation.z += cos(camRotation.x + 0.5f * PI) * 0.2f;
	}


	D3DLIGHT9 light;
	D3DMATERIAL9 material;

	ZeroMemory(&light, sizeof(light));
	light.Type = D3DLIGHT_DIRECTIONAL;
	light.Diffuse = D3DXCOLOR(0.8f,0.8f,0.8f,1.0f);
	light.Direction = lightDirection;


	d3ddev->SetLight(0, &light);
	d3ddev->LightEnable(0,TRUE);

	ZeroMemory(&material, sizeof(D3DMATERIAL9));
	material.Diffuse = D3DXCOLOR(1.0f,1.0f,1.0f,1.0f);
	material.Ambient = material.Diffuse;

	d3ddev->SetMaterial(&material);


	renderFrame();
}

void Game::renderFrame()
{
	d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
	d3ddev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);

	d3ddev->BeginScene();

		// select which vertex format we are using
		d3ddev->SetFVF(CUSTOMFVF);
		static float index = 0.0f; index += 0.01f;

		D3DXMATRIX matViewTranslate,matViewX,matViewY;
		D3DXMatrixTranslation(&matViewTranslate,camLocation.x,camLocation.y,camLocation.z);
		D3DXMatrixRotationYawPitchRoll(&matViewX,camRotation.x,0.0f,0.0f);
		D3DXMatrixRotationYawPitchRoll(&matViewY,0.0f,camRotation.y,0.0f);
		d3ddev->SetTransform(D3DTS_VIEW,&(matViewTranslate * matViewX * matViewY));

		D3DXMATRIX matProjection;
		D3DXMatrixPerspectiveFovLH(&matProjection,D3DXToRadian(45),(FLOAT)SCREENWIDTH / (FLOAT)SCREENHEIGHT,1.0f,100.0f);
		d3ddev->SetTransform(D3DTS_PROJECTION,&matProjection);

		renderWorld();

		using namespace std;
		static RECT textbox; SetRect(&textbox,0,0,SCREENWIDTH,50);
		ostringstream convert;
		convert << FPS;
		string result = convert.str();
		LPCSTR text = result.c_str();
		d3dfont->DrawTextA(NULL,text,-1,&textbox,DT_VCENTER | DT_NOCLIP,D3DCOLOR_XRGB(255,255,255));

	d3ddev->EndScene();

	d3ddev->Present(NULL, NULL, NULL, NULL);
}

void Game::initGraphics()
{
	D3DXCreateTextureFromFile(d3ddev,TEXT("wood.png"),&texture);
	D3DXCreateTextureFromFile(d3ddev,TEXT("stone.png"),&texture2);
	D3DXCreateTextureFromFile(d3ddev,TEXT("grass.png"),&texture3);

	CUSTOMVERTEX vertices[] =
	{
		{ -0.5f, -0.5f, 0.5f,  0.0f, 0.0f, 1.0f,  0.0f, 0.0f, },    // side 1
        { 0.5f, -0.5f, 0.5f,  0.0f, 0.0f, 1.0f,  1.0f, 0.0f, },
        { -0.5f, 0.5f, 0.5f,  0.0f, 0.0f, 1.0f,  0.0f, 1.0f, },
        { 0.5f, 0.5f, 0.5f,  0.0f, 0.0f, 1.0f,  1.0f, 1.0f, },

        { -0.5f, -0.5f, -0.5f,  0.0f, 0.0f, -1.0f,  0.0f, 0.0f, },    // side 2
        { -0.5f, 0.5f, -0.5f,  0.0f, 0.0f, -1.0f,  0.0f, 1.0f, },
        { 0.5f, -0.5f, -0.5f,  0.0f, 0.0f, -1.0f,  1.0f, 0.0f, },
        { 0.5f, 0.5f, -0.5f,  0.0f, 0.0f, -1.0f,  1.0f, 1.0f, },

        { -0.5f, 0.5f, -0.5f,  0.0f, 1.0f, 0.0f,  0.0f, 0.0f, },    // side 3
        { -0.5f, 0.5f, 0.5f,  0.0f, 1.0f, 0.0f,  0.0f, 1.0f, },
        { 0.5f, 0.5f, -0.5f,  0.0f, 1.0f, 0.0f,  1.0f, 0.0f, },
        { 0.5f, 0.5f, 0.5f,  0.0f, 1.0f, 0.0f,  1.0f, 1.0f, },

        { -0.5f, -0.5f, -0.5f,  0.0f, -1.0f, 0.0f,  0.0f, 0.0f, },    // side 4
        { 0.5f, -0.5f, -0.5f,  0.0f, -1.0f, 0.0f,  1.0f, 0.0f, },
        { -0.5f, -0.5f, 0.5f,  0.0f, -1.0f, 0.0f,  0.0f, 1.0f, },
        { 0.5f, -0.5f, 0.5f,  0.0f, -1.0f, 0.0f,  1.0f, 1.0f, },

        { 0.5f, -0.5f, -0.5f,  1.0f, 0.0f, 0.0f,  0.0f, 0.0f, },    // side 5
        { 0.5f, 0.5f, -0.5f,  1.0f, 0.0f, 0.0f,  0.0f, 1.0f, },
        { 0.5f, -0.5f, 0.5f,  1.0f, 0.0f, 0.0f,  1.0f, 0.0f, },
        { 0.5f, 0.5f, 0.5f,  1.0f, 0.0f, 0.0f,  1.0f, 1.0f, },

        { -0.5f, -0.5f, -0.5f,  -1.0f, 0.0f, 0.0f,  0.0f, 0.0f, },    // side 6
        { -0.5f, -0.5f, 0.5f,  -1.0f, 0.0f, 0.0f,  1.0f, 0.0f, },
        { -0.5f, 0.5f, -0.5f,  -1.0f, 0.0f, 0.0f,  0.0f, 1.0f, },
        { -0.5f, 0.5f, 0.5f,  -1.0f, 0.0f, 0.0f,  1.0f, 1.0f, },
	};
	d3ddev->CreateVertexBuffer(24*sizeof(CUSTOMVERTEX),0,CUSTOMFVF,D3DPOOL_MANAGED,&vBuffer,NULL);

	VOID* pVoid;

	vBuffer->Lock(0,0,(void**)&pVoid,0);
	memcpy(pVoid,vertices,sizeof(vertices));
	vBuffer->Unlock();

	short indices[] =
	{
		0, 1, 2,    // side 1
        2, 1, 3,
        4, 5, 6,    // side 2
        6, 5, 7,
        8, 9, 10,    // side 3
        10, 9, 11,
        12, 13, 14,    // side 4
        14, 13, 15,
        16, 17, 18,    // side 5
        18, 17, 19,
        20, 21, 22,    // side 6
        22, 21, 23,
	};
	d3ddev->CreateIndexBuffer(36*sizeof(short),0,D3DFMT_INDEX16,D3DPOOL_MANAGED,&iBuffer,NULL);
	
	iBuffer->Lock(0,0,(void**)&pVoid,0);
	memcpy(pVoid, indices, sizeof(indices));
	iBuffer->Unlock();
}

void Game::initLight()
{
	D3DLIGHT9 light;
	D3DMATERIAL9 material;

	ZeroMemory(&light, sizeof(light));
	light.Type = D3DLIGHT_DIRECTIONAL;
	light.Diffuse = D3DXCOLOR(0.5f,0.5f,0.5f,1.0f);
	light.Direction = lightDirection;


	d3ddev->SetLight(0, &light);
	d3ddev->LightEnable(0,TRUE);

	ZeroMemory(&material, sizeof(D3DMATERIAL9));
	material.Diffuse = D3DXCOLOR(1.0f,1.0f,1.0f,1.0f);
	material.Ambient = material.Diffuse;

	d3ddev->SetMaterial(&material);
}

void Game::renderWorld()
{
	D3DXMATRIX matTranslate;
	d3ddev->SetStreamSource(0, vBuffer, 0, sizeof(CUSTOMVERTEX));
	d3ddev->SetIndices(iBuffer);

	for(int index = 0;index < 40000;index++)
	{
		
		D3DXMatrixTranslation(&matTranslate,block[index].x,0.0f,block[index].z);
		d3ddev->SetTransform(D3DTS_WORLD, &(matTranslate));
		if(block[index].y == 0)
			d3ddev->SetTexture(0, texture);
		else if(block[index].y == 1)
			d3ddev->SetTexture(0, texture2);
		else if(block[index].y == 2)
			d3ddev->SetTexture(0, texture3);

		d3ddev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 24, 0, 12);
	}
	d3ddev->SetTexture(0,texture2);
	D3DXMatrixTranslation(&matTranslate,0.0f,-1.0f,-5.0);
	d3ddev->SetTransform(D3DTS_WORLD, &(matTranslate));
	d3ddev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 24, 0, 12);
}

void Game::initWorld()
{
	int iBlocks = 200;
	block = new D3DXVECTOR3[iBlocks * iBlocks];
	for(int index = 0;index < iBlocks;index++)
	{
		float x = -100.0f + (float)index * 1.0f;
		for(int index2 = 0;index2 < iBlocks;index2++)
		{
			char random = rand() % 3;
			float z = -100.0f + (float)index2 * 1.0f;
			block[iBlocks*index + index2].x = x;
			block[iBlocks*index + index2].y = random;
			block[iBlocks*index + index2].z = z;
		}
	}
}

Game.h


#include <Windows.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <time.h>
#include <math.h>
#include <cmath>
#include <string>
#include <sstream>

#include "Global.h"

#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")

class Game
{
public:
	Game(HWND hWnd);

	void initD3D(HWND hWnd);
	void releaseD3D();
	void initGame(HWND hWnd);
	void GO();
	void renderFrame();
	void initGraphics();
	void initLight();
	void initWorld();
	void renderWorld();

	struct CUSTOMVERTEX
	{
		float x,y,z;
		D3DVECTOR normal;
		float u,v;
	};
	struct D3DVECTOR
	{
		float x,y,z;
	}D3DVECTOR, *LPD3DVECTOR;

	LPDIRECT3D9 d3d;
	LPDIRECT3DDEVICE9 d3ddev;
	LPDIRECT3DVERTEXBUFFER9 vBuffer;
	LPDIRECT3DINDEXBUFFER9 iBuffer;
	LPDIRECT3DTEXTURE9 texture,texture2,texture3;

	D3DXVECTOR3 camLocation,camRotation,lightDirection;
	POINT mousePos;
	D3DXVECTOR3* block;
	LPD3DXFONT d3dfont;

	time_t startTime,curTime;
	int frames,FPS;
	void getFPS()
	{
		time(&curTime);
		frames++;
		time_t timeDiff = difftime(curTime,startTime);
		if(timeDiff >= 1)
		{
			FPS = frames;
			frames = 0;
			startTime = curTime;
		}
	}
};

Window.cpp


#include "Game.h"


LRESULT CALLBACK WindowProc(HWND hWnd,
							UINT message,
							WPARAM wParam,
							LPARAM lParam);

int WINAPI WinMain( HINSTANCE hInstance,
				   HINSTANCE hPrevInstance,
				   LPSTR lpCmdLine,
				   int nCmdShow)
{
	WNDCLASSEX wc;
	HWND hWnd;

	ZeroMemory(&wc, sizeof(WNDCLASSEX));

	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WindowProc;
	wc.hInstance = hInstance;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.lpszClassName = TEXT("NTHV");

	RegisterClassEx(&wc);

	hWnd = CreateWindowEx(NULL,
		TEXT("NTHV"),
		TEXT("NHTV"),
		WS_OVERLAPPEDWINDOW,
		0,0,SCREENWIDTH,SCREENHEIGHT,
		NULL,
		NULL,
		hInstance,
		NULL);

	ShowWindow(hWnd, nCmdShow);
	ShowCursor(false);

	Game theGame(hWnd);

	MSG msg;

	while(TRUE)
    {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
                break;

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
		if(KEY_DOWN(VK_ESCAPE))
			PostMessage(hWnd, WM_DESTROY, 0, 0);

		theGame.GO();
    }
	theGame.releaseD3D();
	return msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
		case WM_DESTROY:
            {
                PostQuitMessage(0);
                return 0;
            } break;
	}

	return DefWindowProc(hWnd, message, wParam, lParam);
}
Advertisement
This doesn’t look like “voxel” to me. The idea is to generate a complete mesh (or blocks of meshes) that represent the surface of an overall object. What you have is: each block is an independent mesh. Even though you are using the same source stream, you are losing the “vertex-belt-fed-machinegun” that is DirectX. Each time the function calls are made to DrawIndexedPrimitive(..), the pipeline resets for a “new” object to be rendered. https://sites.google.com/site/letsmakeavoxelengine/ is a good place to start.

Draw calls are expensive, doubly so in DirectX. You're also setting the same texture and matrix on every call, and then making the expensive draw call for just 24 vertices. If you bump that up to 240 vertices per draw call, your 10fps isn't going to turn into 1fps. It might still be the same 10fps.

as has been noted, it is important to try to bring down the number of draw calls.

one way this is often done is by organizing the voxel terrain into "chunks" (for example 16x16x16 voxels), and then building meshes for these chunks (where all the voxels contribute triangles to these meshes, typically with the triangles sorted by texture, and with some logic to eliminate internal triangles).

when it comes to rendering, the meshes for the chunk are drawn.

and, if something is changed, then the associated mesh is rebuilt.

GBG, the code doesn't even have voxels in the sense you are referring. He is drawing EVERY box.

This topic is closed to new replies.

Advertisement