A Simple 3D Room made with DirectX and C++

Today, while messing around with an old backup disk, I found a DirectX 9 project I made back in 2005. It is an application that renders a 3D room and allows the user to navigate inside it in first person camera. I developed this application to test a 3D engine I was developing at the time. The video below shows the 3D application in action.

 

I will just share the main file module of the project. To get the rest of the 3D engine files, consider following me on twitter or facebook, or ask in the contact page. Don’t hesitate to leave a comment if you a have a doubt or suggestion.

How it works

You navigate in the room using the arrow keys. The possible moviments are:

  • Move forward/backward
  • Move left/right
  • Strafe left/right
  • Look down/up

I developed the application using C++ and DirectX 9. The application is based on a 3D engine I was developing at the time. The main aim of the engine was to facilitate the creation of windows applications and at the same time make easy the creation of cool 3D games. The engine facilitated setting up DirectX, playing sounds and music, loading 3D models, creating worlds, and making navigations in first person as well as in third person (just to name a few).

The Code

The main module is divided in two files: main.cpp and main.h. Below is the code for the main.h file.

#ifndef __MAIN_H__
#define __MAIN_H__

#include <windows.h>
#include "application.h"
#include "graphics.h"
#include "camera.h"

typedef struct {
 float x,y,z;
 float u,v;
}VERTEX;

#define VERTEX_FVF		D3DFVF_XYZ | D3DFVF_TEX1

#define WORLD_X			10
#define WORLD_Y			9

#define NUM_WALL_VERTICES	24
#define NUM_CEIL_VERTICES	6
#define NUM_FLOOR_VERTICES	6

#define NUM_CEIL_TRI		(NUM_CEIL_VERTICES / 3)
#define NUM_WALL_TRI		(NUM_WALL_VERTICES / 3)
#define NUM_FLOOR_TRI		(NUM_FLOOR_VERTICES / 3)

#define WALL_VB_SIZE		NUM_WALL_VERTICES * sizeof(VERTEX)
#define CEIL_VB_SIZE		NUM_CEIL_VERTICES * sizeof(VERTEX)
#define FLOOR_VB_SIZE		NUM_FLOOR_VERTICES * sizeof(VERTEX)

#define KEYDOWN(x)		(GetAsyncKeyState(x) & 0x8000)

class c3DRoom : public cApplication {
 cGraphics *m_pGfx;
 cCAMERA   m_Camera;
 IDirect3DVertexBuffer9 *m_pVBWall;
 IDirect3DVertexBuffer9 *m_pVBCeil;
 IDirect3DVertexBuffer9 *m_pVBFloor;
 IDirect3DTexture9	*m_pTextures[3];
 BOOL m_bFullScreen;

public:
 c3DRoom();
 ~c3DRoom();

 BOOL InitScene();
 BOOL Init();
 void CheckInput();
 void RenderScene();
 BOOL DoFrame();
 BOOL ShutDown();
};
#endif

Next is the code for the main.cpp file.

#include "main.h"

VERTEX WallVtx[NUM_WALL_VERTICES] = {
 // 1o Triângulo, face frontal
 -1,1,-1,0,0,
 1,1,-1,1,0,
 -1,-1,-1,0,1,
 // 2o Triângulo, face frontal
 1,1,-1,1,0,
 1,-1,-1,1,1,
 -1,-1,-1,0,1,
 // 1o Triângulo, face lateral direita
 1,1,-1,0,0,
 1,1,1,1,0,
 1,-1,-1,0,1,
 // 2o Triângulo, face lateral direita
 1,1,1,1,0,
 1,-1,1,1,1,
 1,-1,-1,0,1,
 // 1o Triângulo, face lateral esquerda
 -1,1,-1,1,0,
 -1,-1,1,0,1,
 -1,1,1,0,0,
 // 2o Triângulo, face lateral esquerda
 -1,1,-1,1,0,
 -1,-1,-1,1,1,
 -1,-1,1,0,1,
 // 1o Triângulo, face traseira
 1,1,1,0,0,
 -1,1,1,1,0,
 1,-1,1,0,1,
 // 2o Triângulo, face traseira
 1,-1,1,0,1,
 -1,1,1,1,0,
 -1,-1,1,1,1
};

VERTEX CeilVtx[NUM_CEIL_VERTICES] = {
 // 1o Triângulo
 1,1,1,1,1,
 -1,1,1,0,1,
 -1,1,-1,0,0,
 // 2o Triângulo
 -1,1,-1,0,0,
 1,1,-1,1,0,
 1,1,1,1,1
};

VERTEX FloorVtx[NUM_CEIL_VERTICES] = {
 // 1o Triângulo
 -1,0,1,0,0,
 1,0,1,1,0,
 -1,0,-1,0,1,
 // 2o Triângulo
 1,0,1,1,0,
 1,0,-1,1,1,
 -1,0,-1,0,1
};

int iWallMap[WORLD_Y][WORLD_X] = {
 1,1,1,1,1,1,1,1,1,1,
 1,0,0,0,0,0,0,0,0,1,
 1,0,1,0,0,0,0,1,0,1,
 1,0,1,0,0,0,0,1,0,1,
 1,0,0,0,0,0,0,1,0,1,
 1,0,1,0,0,0,0,1,0,1,
 1,0,1,0,1,1,1,1,0,1,
 1,0,0,0,0,0,0,0,0,1,
 1,1,1,1,1,1,1,1,1,1};

c3DRoom::c3DRoom()
{
  m_pTextures[0] = m_pTextures[1] = m_pTextures[2] = NULL;
  m_pVBWall  = NULL;
  m_pVBCeil  = NULL;
  m_pVBFloor = NULL;

  m_dwWidth = 400;
  m_dwHeight = 350;
  m_dwStyle = WS_OVERLAPPEDWINDOW;
  m_dwExStyle = 0;
  m_bFullScreen = FALSE;

  strcpy(m_szClass,"3DRoomClass");
  strcpy(m_szCaption,"3D Room by Leonel Machava");
}

BOOL c3DRoom::InitScene()
{
  IDirect3DDevice9 *pDevice;
  D3DXMATRIX matWorld,matView,matProj;
  RECT rcClient;

  pDevice = Graphics()->GetDevice();

  if( FAILED(D3DXCreateTextureFromFile(pDevice,"Media\\wall2.bmp",&m_pTextures[0])) ||
    FAILED(D3DXCreateTextureFromFile(pDevice,"Media\\floor.bmp",&m_pTextures[1])) ||
    FAILED(D3DXCreateTextureFromFile(pDevice,"Media\\ceil1.bmp",&m_pTextures[2])) )
    return( FALSE );

  if( FAILED(pDevice->CreateVertexBuffer(WALL_VB_SIZE,D3DUSAGE_WRITEONLY,VERTEX_FVF,
                                         D3DPOOL_MANAGED,&m_pVBWall,NULL)) )
    return( FALSE );

  if( FAILED(pDevice->CreateVertexBuffer(CEIL_VB_SIZE,D3DUSAGE_WRITEONLY,VERTEX_FVF,
                                         D3DPOOL_MANAGED,&m_pVBCeil,NULL)) )
    return( FALSE );

  if( FAILED(pDevice->CreateVertexBuffer(FLOOR_VB_SIZE,D3DUSAGE_WRITEONLY,VERTEX_FVF,
                                         D3DPOOL_MANAGED,&m_pVBFloor,NULL)) )
   return( FALSE );

  Graphics()->FillVertexBuffer(m_pVBWall,WallVtx,0,WALL_VB_SIZE);
  Graphics()->FillVertexBuffer(m_pVBCeil,CeilVtx,0,CEIL_VB_SIZE);
  Graphics()->FillVertexBuffer(m_pVBFloor,FloorVtx,0,FLOOR_VB_SIZE);

  pDevice->SetRenderState(D3DRS_LIGHTING,FALSE);
  pDevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
  pDevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
  pDevice->SetSamplerState(0,D3DSAMP_MIPFILTER,D3DTEXF_LINEAR);

  GetClientRect(GetHWnd(),&rcClient);
  m_Camera.GetMatrix(&matView);

  D3DXMatrixIdentity(&matWorld);
  D3DXMatrixPerspectiveFovLH(&matProj,D3DX_PI / 4.0,(float)rcClient.right / rcClient.bottom,1.0,1000.f);

  pDevice->SetTransform(D3DTS_WORLD,&matWorld);
  pDevice->SetTransform(D3DTS_VIEW,&matView);
  pDevice->SetTransform(D3DTS_PROJECTION,&matProj);

  return( TRUE );
}

BOOL c3DRoom::Init()
{
  m_pGfx = new cGraphics;

  m_Camera.SetPosition(1,0,-7);

  if( !Graphics()->Init(GetHWnd(),0,0,TRUE,D3DFMT_UNKNOWN,D3DFMT_D16) )
  {
    MessageBox(GetHWnd(),"Não foi possível inicializar os gráficos","Erro",MB_OK);
    return( FALSE );
  }

  if( !InitScene() )
  {
    MessageBox(GetHWnd(),"Não foi possível inicializar a cena","Erro",MB_OK);
    return( FALSE );
  }

  return( TRUE );
}

BOOL c3DRoom::ShutDown()
{
  SAFE_RELEASE(m_pTextures[0]);
  SAFE_RELEASE(m_pTextures[1]);
  SAFE_RELEASE(m_pTextures[2]);
  SAFE_RELEASE(m_pVBWall);
  SAFE_RELEASE(m_pVBCeil);
  SAFE_RELEASE(m_pVBFloor);

  Graphics()->Close();

  delete m_pGfx;

  return( TRUE );
}

void c3DRoom::CheckInput()
{
  if( KEYDOWN(VK_UP) )	m_Camera.Walk(0.10f);

  if( KEYDOWN(VK_DOWN) )	m_Camera.Walk(-0.10f);

  if( KEYDOWN(VK_LEFT) )	m_Camera.Yaw(-0.02f);

  if( KEYDOWN(VK_RIGHT) ) m_Camera.Yaw(0.02f);

  if( KEYDOWN(VK_PRIOR) )	m_Camera.Pitch(0.02f);

  if( KEYDOWN(VK_NEXT) )	m_Camera.Pitch(-0.02f);

  if( KEYDOWN('A') )		m_Camera.Strafe(-0.1f);

  if( KEYDOWN('D') )		m_Camera.Strafe(0.1f);
}

void c3DRoom::RenderScene()
{
  float xPos,zPos;
  int i,j;

  IDirect3DDevice9 *pDevice;
  D3DXMATRIX matView,matWorld;

  Graphics()->ClearAll(D3DCOLOR_XRGB(0,0xF0,0),1.0);
  Graphics()->BeginScene();

  pDevice = Graphics()->GetDevice();
  pDevice->SetFVF(VERTEX_FVF);

  m_Camera.GetMatrix(&matView);
  pDevice->SetTransform(D3DTS_VIEW,&matView);

  pDevice->SetStreamSource(0,m_pVBFloor,0,sizeof(VERTEX));
  pDevice->SetTexture(0,m_pTextures[1]);

  for( i = 0,zPos = 5; i < WORLD_Y; i++ )
  {
    xPos = -5;
    for( j = 0; j < WORLD_X; j++ )
    {
      D3DXMatrixTranslation(&matWorld,xPos,-1,zPos);
//    if( 0 == iWallMap[i][j] )
      {
        pDevice->SetTransform(D3DTS_WORLD,&matWorld);
        pDevice->DrawPrimitive(D3DPT_TRIANGLELIST,0,NUM_FLOOR_TRI);
      }
      xPos += 2;
    }
    zPos -= 2;
  }

  pDevice->SetStreamSource(0,m_pVBCeil,0,sizeof(VERTEX));
  pDevice->SetTexture(0,m_pTextures[2]);

  for( i = 0,zPos = 5; i < WORLD_Y; i++ )
  {
    xPos = -5;
    for( j = 0; j < WORLD_X; j++ )
    {
      D3DXMatrixTranslation(&matWorld,xPos,0,zPos);
//    if( 0 == iWallMap[i][j] )
      {
        pDevice->SetTransform(D3DTS_WORLD,&matWorld);
        pDevice->DrawPrimitive(D3DPT_TRIANGLELIST,0,NUM_CEIL_TRI);
      }
    xPos += 2;
    }
    zPos -= 2;
  }

  pDevice->SetStreamSource(0,m_pVBWall,0,sizeof(VERTEX));
  pDevice->SetTexture(0,m_pTextures[0]);

  for( i = 0,zPos = 5; i < WORLD_Y; i++ )
  {
    xPos = -5;
    for( j = 0; j < WORLD_X; j++ )
    {
      D3DXMatrixTranslation(&matWorld,xPos,0,zPos);
      if( iWallMap[i][j] != 0 )
      {
        pDevice->SetTransform(D3DTS_WORLD,&matWorld);
        pDevice->DrawPrimitive(D3DPT_TRIANGLELIST,0,NUM_WALL_TRI);
      }
      xPos += 2;
    }
    zPos -= 2;
  }

  pDevice->SetTexture(0,NULL);
  Graphics()->EndScene();
  Graphics()->Flip();
}

BOOL c3DRoom::DoFrame()
{
  CheckInput();
  RenderScene();
  return( TRUE );
}

c3DRoom::~c3DRoom()
{
  //NOP
}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
 LPSTR lpCmdLine,int nShowCmd)
{
  c3DRoom cApp;

  return( cApp.Run() );
}

In the next days I will share other projects of mine that I made a while ago. If you want to stay updated, you can subscribe by email.

If you like this article please share it by clicking one of the buttons below, follow me on twitter, or register on my RSS to receive article updates. Thanks!

Leave a comment ?

16 Comments.

  1. wow… you do know a lot of languages o.o…
    despite the fact that it looks like another screensaver… and that im afraid of direct X xD… well… its a pretty nice work >w<
    you know?, i whink you could make great things if you focus on a single project and, why not? some money too xd

    • Hello Fabyola. Thank you!
      The main purpose of this site is to give back to the community from where I have learnt a lot (and I am still learning).
      I do have other projects that are more inclined to $$$. Anyway, if you have any idea or want to partner me with I am open.

  2. Dude!!!
    Its just awsome! :-)
    I am a beginner in game programming.
    And I use python & pygame for that(Sorry, no c++ % directx & stuff).
    So if you have some spair time, please please convert this (cool) 3d room project to python.

  3. Very very nice room, and the code is easy to understand. I hope I am as near as good as you. By the way, do you still have the full engine. I would like to compile and play with until I can learn a lot of things in directx and also learn other people’s style of programming.

    I really love your code.

  4. Can you write in english with coments to be more legible the code And in the web explain in a tutorial step by step what is evertything, including the directx 9 stuff?
    It will be super!! thanks!

    And other thing, how we can restrict the walls and dont pass trough them? what will be the code? thanks!

    • Ok, marco, I will do it when I have some spare time.

      For the player to not pass through the walls, we must employ “collision detection”. There are many ways of doing this. For this simple case we can use “bounding box collision detection”.

  5. thanks. it will be a lot of help!!!

  6. The collision detection that you say is a direct x statement ?

  7. what is application and graphics.h?

  8. Hi is there a posibility to get the complete source code so I could learn from it, thanks…

  9. hello
    are you can get complete source code for learning or .exe file.
    thank you.

  10. Hello,
    I Am a begginer in both C++ and 3D world. I am amazed after looking into this project(3D room). I am so excited to create a simple 3D room with objects in it. Could you please guide me in doing this.

Reply to Oxford ¬
Cancel reply

Notify me of followup comments via e-mail. You can also subscribe without commenting.