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);
}