XNA for C#
DirectX 9 for C#
DirectX 9 for C++
DirectX 9 for VB
Forum
   
My XNA Book
      
       Go to section on this site

Additional info


Latest Forum posts

 Account settings
  Posted by: Anonymous
  When: 07/05/2014 at 09:48:39

 forced subtitle
  Posted by: Applefly
  When: 07/05/2014 at 06:00:48

 convert DVD into PMS
  Posted by: Applefly
  When: 07/05/2014 at 05:55:25

 DVD to Digital Copy easily
  Posted by: VIKIVannessa
  When: 05/05/2014 at 06:52:29

 DVD on Xbox 360/Xbox One Console
  Posted by: VIKIVannessa
  When: 05/05/2014 at 06:51:47

 Extract .Srt Subtitles
  Posted by: Applefly
  When: 04/05/2014 at 03:54:38

 Encode Movie collection
  Posted by: Applefly
  When: 04/05/2014 at 03:52:41

 Convert DVD to WMV
  Posted by: Applefly
  When: 29/04/2014 at 05:53:50

 rip DVDs into digital files
  Posted by: Applefly
  When: 29/04/2014 at 05:51:20

 iTunes movies/music to Kindle Fire
  Posted by: ciciyu80
  When: 29/04/2014 at 05:10:20


Ads

Importing height data from .bmp files

My thanks to Richard Sharpe for porting this chapter to C++!


Since this is one of the tutorials in C# I have tried to rewrite for C++, this is a small chapter on importing height maps from a .bmp file instead of .raw files. You can easily edit/create your own .bmp files using MS Paint or other standard software.
 
The reason why Riemer chose the .raw file to start with, is that every byte you read from the file is useful information. A .bmp file includes lots of other information, such as the filesize, the width and height of the image and so on. This makes importing data from .bmp a little more difficult than importing from .raw files, since now we have to cut away all the useless info. I will include a small summary on the structure of .bmp-files. If you're not interested in this, simply copy the code you find at the bottom of this page.
 
In a .bmp-file, the actual pixel-data is preceded by a header. All values are between 0 and 255, but you don't find decimal values in a .bmp file, you find the ASCII representations of the values. As an example, when you open any .bmp with a simple text-editor like notepad, the first 2 letters always are BM. These correspond to the numbers 66 and 77 in the ASCII table. (Try opening a text editor and hold ALT while typing 66 or 77.) The rest of the file probably won't be readable, because in the ASCII table most numbers between 0 and 255 don't correspondent to a letter/number. When we use the ReadByte()method, it returns the ASCII value of the byte read. So, if we would read the first byte of any .bmp-file (always a B), it would return 66.
  
Here is a description of the header :
ByteNrValue
1-266 77Always BM
3-6X X X XFilesize
7-100 0 0 0Always 0
11-14X X X XOffset to pixeldata
15-1840 0 0 0Always 40
19-22X X X XImage width in pixels
23-26X X X XImage height in pixels
27-281 0Always 1
29-3224 0 0 0Bits per pixel
33-360 0 0 0Compression, usually 0
37-534x(0 0 0 0)No longer used

The most important entries include the image width, height and the bytenr where the actual pixel-data can be found. The actual pixeldata can be put immediately after the header, thus at byte 54. This is usually done, for example MS paint puts it there. The pixel data contains 24 bits per pixel, this corresponds to 3 bytes, which each can contain a value between 0 and 255. There is one byte for the Red, the Green and the Blue value for each pixel.
 
So, if we want to use the pixeldata as heightdata, first we have to find the offset to the actual data, and then simply sum the 3 colorvalues to become the gray value of the pixel, which will be our height.

Lets start by changing our constants so they pick up their values from the bitmap files. (I am also declaring them using C++ standard (sorry Riemer))

Replace

 #define WIDTH    64
 #define HEIGHT    64

with

 short unsigned const int offset = set_offset();
 short unsigned const int WIDTH  = set_width();
 short unsigned const int HEIGHT = set_height();

As you can see I have a new constant to hold the offset value.  So to set it we are going to use the following function

 int set_offset (void)
 {
     std::ifstream f_DataFile;
     short unsigned int offset;
     short dummy;
 
     f_DataFile.open("heightmap.bmp", std::ios::binary);
     
     
     for (int i = 0; i < 10; i++)
     {
         dummy = f_DataFile.get();
     }
 
     offset = f_DataFile.get();
     offset += f_DataFile.get()*256;
     offset += f_DataFile.get()*256*256;
     offset += f_DataFile.get()*256*256*256;
 
     f_DataFile.close();
     
     return offset;
 }

you will probably want to try to keep your functions and constants together so add this function to the top of the others and insert a prototype above the constant declaration.

 int set_offset (void);

Now we need to create the functions to get the height and width of the terrain file.

 int set_height (void)
 {
     std::ifstream f_DataFile;
     short unsigned int HEIGHT;
     short dummy;
 
     f_DataFile.open("heightmap.bmp", std::ios::binary);
 
     if (f_DataFile.is_open())
     {
         for (int i = 0; i < 22; i++)
         {
             dummy = f_DataFile.get();
         }
 
         HEIGHT = f_DataFile.get();
         HEIGHT += f_DataFile.get()*256;
         HEIGHT += f_DataFile.get()*256*256;
         HEIGHT += f_DataFile.get()*256*256*256;
 
         f_DataFile.close();
     }
     return HEIGHT;
 }
 
 int set_width (void)
 {
     std::ifstream f_DataFile;
     short unsigned int WIDTH;
     short dummy;
 
     f_DataFile.open("heightmap.bmp", std::ios::binary);
 
     if (f_DataFile.is_open())
     {
 
         for (int i = 0; i < 18; i++)
         {
             dummy = f_DataFile.get();
         }
 
         WIDTH = f_DataFile.get();
         WIDTH += f_DataFile.get()*256;
         WIDTH += f_DataFile.get()*256*256;
         WIDTH += f_DataFile.get()*256*256*256;
 
         f_DataFile.close();
     }
     return WIDTH;
 }

and of course the prototypes at the top of your code:

 int set_height (void);
 int set_width (void);

First in the FillVertices function we need to change the cv_Vertices array to be a pointer to an array.  This allows the array to have it's size set at runtime rather than at compilation.  

 OURCUSTOMVERTEX *cv_Vertices = new OURCUSTOMVERTEX[WIDTH*WIDTH];

Now we need to import the height values.  To do this change the FillVertices Function from the line

 std::ifstream f_DataFile;
 

to the line

 f_DataFile.close();
 

To the fiollowing

 std::ifstream f_DataFile;
 short unsigned int dummy;
 f_DataFile.open("heightmap.bmp", std::ios::binary);
 if (f_DataFile.is_open())
 {

    for (int i = 0;i< (offset);i++)    {
        dummy = f_DataFile.get();
    }

    for (int x=0;x< WIDTH;x++)    {

        for (int y=0; y< HEIGHT;y++)        {
                int height = f_DataFile.get();
                height += f_DataFile.get();
                height += f_DataFile.get();
                height /= 8;
                cv_Vertices[y*WIDTH + x].x = -x;
                cv_Vertices[y*WIDTH + x].y = y;
                cv_Vertices[y*WIDTH + x].z = height;
                cv_Vertices[y*WIDTH + x].color = 0xffffffff;
        }
    }
}else{
    MessageBox(han_Window,"HeighData file not found!","FillVertices()",MB_OK);
}

f_DataFile.close();


Also just before the return statement you need to delete the pointer to free up the memory

 delete [] cv_Vertices;
 

OK nearly there.  Again we need to change the s_Indices variable into a pointer so it can be set at runtime so in FillIndices function declare it as

 short *s_Indices = new short[(WIDTH-1)*(HEIGHT-1)*6];
 

Also you need to delete the pointer just before the return statement

 delete [] s_Indices;
 

This is it! Now download a new example terrain here (http://users.pandora.be/riemer/files/heightmap.bmp) and place it in your ‘Debug’ directory. Because this is twice the size of our first map, we are going to position the camera a bit further from the center. Also notice that the far plane has been adjusted to 300, because otherwise every pixel further away from the camera than 100 would not be displayed:

 D3DXVECTOR3 m_EyePos(80, 0, 120);
 D3DXVECTOR3 m_TargetPos(-20, 0, 0);
 D3DXVECTOR3 m_UpVector(0, 0, 1);
 
 D3DXMatrixPerspectiveFovLH(&m_Projection, D3DX_PI/4, 500/500, 1, 300);
 

NOTES: I have had a bit of a play with this.  For some reason it seems to work fine with pretty much any size square bitmap (of course you will need to reposition the camera) but it crashes with rectangles.  I am still learning so I may work out why soon but if you know why please let me know.  

I will be trying to convert the next chapter into C++ shortly so watch this space (just need to write it up).  Please see the completed code bellow for this tutorial.




DirectX Tutorial 13 - Importing .bmp files

If you appreciate the amount of time I spend creating and updating
these pages, feel free to donate -- any amount is welcome !



Click here to go to the forum on this chapter!

Or click on one of the topics on this chapter to go there:
  • Aspect Ratio Issues
          In the SetUpCamera function, you should use floats...
  • DX Terrain VS XNA Terrain
          Im using Directx with C++ and my terrain using the...
  • Static-ish Map (Singleton?)
          This is for the people who want the Map as a Singl...
  • Error
          When i try and run the code i get "Run-Time Check ...
  • Why rectangular terrains don't work
          The problem appears to be in the dimensioning of t...
  • Blank Screen
          Hi, I've found all the tutorials really useful so...



    The complete code for this chapter:


    #include<windows.h>
    #include<d3d9.h>
    #include<d3dx9.h>
    #include<dinput.h>
    #include<fstream>
    int set_offset (void);
    int set_height (void);
    int set_width (void);

    short unsigned const int offset = set_offset();
    short unsigned const int WIDTH  = set_width();
    short unsigned const int HEIGHT = set_height();

    struct OURCUSTOMVERTEX
    {
        float x,y,z;
        DWORD color;
    };

    int int_AppRunning = 1;


    float flt_Angle = 0;

    int set_offset (void)
    {
        std::ifstream f_DataFile;
        short unsigned int offset;
        short dummy;

        f_DataFile.open("heightmap.bmp", std::ios::binary);
        
        
        for (int i = 0; i < 10; i++)
        {
            dummy = f_DataFile.get();
        }

        offset = f_DataFile.get();
        offset += f_DataFile.get()*256;
        offset += f_DataFile.get()*256*256;
        offset += f_DataFile.get()*256*256*256;

        f_DataFile.close();
        
        return offset;
    }

    int set_height (void)
    {
        std::ifstream f_DataFile;
        short unsigned int HEIGHT;
        short dummy;

        f_DataFile.open("heightmap.bmp", std::ios::binary);

        if (f_DataFile.is_open())
        {
            for (int i = 0; i < 22; i++)
            {
                dummy = f_DataFile.get();
            }

            HEIGHT = f_DataFile.get();
            HEIGHT += f_DataFile.get()*256;
            HEIGHT += f_DataFile.get()*256*256;
            HEIGHT += f_DataFile.get()*256*256*256;

            f_DataFile.close();
        }
        return HEIGHT;
    }

    int set_width (void)
    {
        std::ifstream f_DataFile;
        short unsigned int WIDTH;
        short dummy;

        f_DataFile.open("heightmap.bmp", std::ios::binary);

        if (f_DataFile.is_open())
        {

            for (int i = 0; i < 18; i++)
            {
                dummy = f_DataFile.get();
            }

            WIDTH = f_DataFile.get();
            WIDTH += f_DataFile.get()*256;
            WIDTH += f_DataFile.get()*256*256;
            WIDTH += f_DataFile.get()*256*256*256;

            f_DataFile.close();
        }
        return WIDTH;
    }

    LRESULT CALLBACK OurWindowProcedure(HWND han_Wind,UINT uint_Message,WPARAM parameter1,LPARAM parameter2)
    {
        return DefWindowProc(han_Wind,uint_Message,parameter1,parameter2);
    }


    HWND NewWindow(LPCTSTR str_Title,int int_XPos, int int_YPos, int int_Width, int int_Height)
    {
        WNDCLASSEX wnd_Structure;

        wnd_Structure.cbSize = sizeof(WNDCLASSEX);
        wnd_Structure.style = CS_HREDRAW | CS_VREDRAW;
        wnd_Structure.lpfnWndProc = OurWindowProcedure;
        wnd_Structure.cbClsExtra = 0;
        wnd_Structure.cbWndExtra = 0;
        wnd_Structure.hInstance = GetModuleHandle(NULL);
        wnd_Structure.hIcon = NULL;
        wnd_Structure.hCursor = NULL;
        wnd_Structure.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);
        wnd_Structure.lpszMenuName = NULL;
        wnd_Structure.lpszClassName = "WindowClassName";
        wnd_Structure.hIconSm = LoadIcon(NULL,IDI_APPLICATION);

        RegisterClassEx(&wnd_Structure);

        return CreateWindowEx(WS_EX_CONTROLPARENT, "WindowClassName", str_Title, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE, int_XPos, int_YPos, int_Width, int_Height, NULL, NULL, GetModuleHandle(NULL), NULL);
    }

    LPDIRECT3DDEVICE9 InitializeDevice(HWND han_WindowToBindTo)
    {
        LPDIRECT3D9 p_dx_Object;
        LPDIRECT3DDEVICE9 p_dx_Device;

        p_dx_Object = Direct3DCreate9(D3D_SDK_VERSION);
        if (p_dx_Object == NULL)
        {
            MessageBox(han_WindowToBindTo,"DirectX Runtime library not installed!","InitializeDevice()",MB_OK);
        }

        D3DPRESENT_PARAMETERS dx_PresParams;
        ZeroMemory( &dx_PresParams, sizeof(dx_PresParams) );
        dx_PresParams.Windowed = TRUE;
        dx_PresParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
        dx_PresParams.BackBufferFormat = D3DFMT_UNKNOWN;

        if (FAILED(p_dx_Object->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, han_WindowToBindTo, D3DCREATE_HARDWARE_VERTEXPROCESSING, &dx_PresParams, &p_dx_Device)))
        {
            if (FAILED(p_dx_Object->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, han_WindowToBindTo, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &dx_PresParams, &p_dx_Device)))
            {
                MessageBox(han_WindowToBindTo,"Failed to create even the reference device!","InitializeDevice()",MB_OK);
            }
      }

        p_dx_Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
        p_dx_Device->SetRenderState(D3DRS_LIGHTING,false);
        p_dx_Device->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);

        return p_dx_Device;
    }

    void DrawScene(LPDIRECT3DDEVICE9 p_dx_Device, LPDIRECT3DVERTEXBUFFER9 p_dx_VertexBuffer, LPDIRECT3DINDEXBUFFER9 p_dx_IndexBuffer)
    {
        p_dx_Device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);
        p_dx_Device->BeginScene();

        p_dx_Device->SetStreamSource( 0, p_dx_VertexBuffer, 0, sizeof(OURCUSTOMVERTEX) );
      p_dx_Device->SetFVF(D3DFVF_XYZ|D3DFVF_DIFFUSE);
        p_dx_Device->SetIndices(p_dx_IndexBuffer);


        D3DXMATRIX m_Rotation;
        D3DXMatrixRotationZ(&m_Rotation, flt_Angle);
        D3DXMATRIX m_Translation;
        D3DXMatrixTranslation(&m_Translation,32,-32,0);

        D3DXMATRIX m_World;
        D3DXMatrixMultiply(&m_World, &m_Translation, &m_Rotation);
        p_dx_Device->SetTransform(D3DTS_WORLD, &m_World);


        p_dx_Device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,WIDTH*HEIGHT,0,(WIDTH-1)*(HEIGHT-1)*2);

        p_dx_Device->EndScene();
        p_dx_Device->Present(NULL, NULL, NULL, NULL);
    }

    LPDIRECT3DVERTEXBUFFER9 FillVertices(HWND han_Window, LPDIRECT3DDEVICE9 p_dx_Device)
    {
        

        LPDIRECT3DVERTEXBUFFER9 p_dx_VertexBuffer;
        OURCUSTOMVERTEX *cv_Vertices = new OURCUSTOMVERTEX[WIDTH*WIDTH];

        std::ifstream f_DataFile;
        short unsigned int dummy;
        f_DataFile.open("heightmap.bmp", std::ios::binary);
        if (f_DataFile.is_open())
        {

            for (int i = 0;i< (offset);i++)        {
                dummy = f_DataFile.get();
            }

            for (int x=0;x< WIDTH;x++)        {

                for (int y=0; y< HEIGHT;y++)            {
                    int height = f_DataFile.get();
                    height += f_DataFile.get();
                    height += f_DataFile.get();
                    height /= 8;
                    cv_Vertices[y*WIDTH + x].x = -x;
                    cv_Vertices[y*WIDTH + x].y = y;
                    cv_Vertices[y*WIDTH + x].z = height;
                    cv_Vertices[y*WIDTH + x].color = 0xffffffff;
                }
            }
        }else{
            MessageBox(han_Window,"HeighData file not found!","FillVertices()",MB_OK);
        }

        f_DataFile.close();

        if (FAILED(p_dx_Device->CreateVertexBuffer(WIDTH*HEIGHT*sizeof(OURCUSTOMVERTEX), 0, D3DFVF_XYZ|D3DFVF_DIFFUSE, D3DPOOL_DEFAULT, &p_dx_VertexBuffer, NULL)))
        {        
            MessageBox(han_Window,"Error while creating VertexBuffer","FillVertices()",MB_OK);
        }

        VOID* p_Vertices;
        if (FAILED(p_dx_VertexBuffer->Lock(0, WIDTH*HEIGHT*sizeof(OURCUSTOMVERTEX), (void**)&p_Vertices, 0)))
        {
            MessageBox(han_Window,"Error trying to lock","FillVertices()",MB_OK);
        }else{
            memcpy(p_Vertices, cv_Vertices, WIDTH*HEIGHT*sizeof(OURCUSTOMVERTEX));
            p_dx_VertexBuffer->Unlock();
        }
        delete [] cv_Vertices;
        return p_dx_VertexBuffer;
    }

    LPDIRECT3DINDEXBUFFER9 FillIndices(HWND han_Window, LPDIRECT3DDEVICE9 p_dx_Device)
    {
        LPDIRECT3DINDEXBUFFER9 p_dx_IndexBuffer;
        short *s_Indices = new short[(WIDTH-1)*(HEIGHT-1)*6];



        for (int x=0;x< WIDTH-1;x++)    {


            for (int y=0; y< HEIGHT-1;y++)        {
                s_Indices[(x+y*(WIDTH-1))*6+2] = x+y*WIDTH;
                s_Indices[(x+y*(WIDTH-1))*6+1] = (x+1)+y*WIDTH;
                s_Indices[(x+y*(WIDTH-1))*6] = (x+1)+(y+1)*WIDTH;

                s_Indices[(x+y*(WIDTH-1))*6+3] = (x+1)+(y+1)*WIDTH;
                s_Indices[(x+y*(WIDTH-1))*6+4] = x+y*WIDTH;
                s_Indices[(x+y*(WIDTH-1))*6+5] = x+(y+1)*WIDTH;
            }
        }

        if (FAILED(p_dx_Device->CreateIndexBuffer((WIDTH-1)*(HEIGHT-1)*6*sizeof(short),D3DUSAGE_WRITEONLY,D3DFMT_INDEX16,D3DPOOL_MANAGED,&p_dx_IndexBuffer,NULL)))
        {
            MessageBox(han_Window,"Error while creating IndexBuffer","FillIndices()",MB_OK);
        }

        VOID* p_Indices;
        if (FAILED(p_dx_IndexBuffer->Lock(0, (WIDTH-1)*(HEIGHT-1)*6*sizeof(short), (void**)&p_Indices, 0)))
        {
            MessageBox(han_Window,"Error trying to lock","FillIndices()",MB_OK);
        }else{
            memcpy(p_Indices, s_Indices, (WIDTH-1)*(HEIGHT-1)*6*sizeof(short));
            p_dx_IndexBuffer->Unlock();
        }
        delete [] s_Indices;
        return p_dx_IndexBuffer;
    }

    void SetUpCamera(LPDIRECT3DDEVICE9 p_dx_Device)
    {
        D3DXVECTOR3 m_EyePos(80, 0, 120);
        D3DXVECTOR3 m_TargetPos(-20, 0, 0);
        D3DXVECTOR3 m_UpVector(0, 0, 1);
        D3DXMATRIXA16 m_View;
        D3DXMatrixLookAtLH(&m_View, &m_EyePos, &m_TargetPos, &m_UpVector);
        p_dx_Device->SetTransform(D3DTS_VIEW, &m_View);

        D3DXMATRIX m_Projection;
        D3DXMatrixPerspectiveFovLH(&m_Projection, D3DX_PI/4, 500/500, 1, 250);
        p_dx_Device->SetTransform(D3DTS_PROJECTION, &m_Projection);
    }



    LPDIRECTINPUTDEVICE8 InitializeKeyboard(HWND han_Window)
    {
        LPDIRECTINPUT8 p_dx_KeybObject;
        LPDIRECTINPUTDEVICE8 p_dx_KeybDevice;

        DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&p_dx_KeybObject, NULL);
        p_dx_KeybObject->CreateDevice(GUID_SysKeyboard, &p_dx_KeybDevice, NULL);

        p_dx_KeybDevice->SetDataFormat(&c_dfDIKeyboard);
        p_dx_KeybDevice->SetCooperativeLevel(han_Window, DISCL_FOREGROUND|DISCL_NONEXCLUSIVE);
        p_dx_KeybDevice->Acquire();

        return p_dx_KeybDevice;
    }

    void ReadKeyboard(LPDIRECTINPUTDEVICE8 p_Keyb)
    {
        char chr_KeybState[256];
        p_Keyb->GetDeviceState(sizeof(chr_KeybState),(LPVOID)&chr_KeybState);

        if (chr_KeybState[DIK_ESCAPE]/128)
        {
            int_AppRunning = 0;
        }

        if (chr_KeybState[DIK_DELETE]/128)
        {
            flt_Angle += (float)0.03;
        }

        if (chr_KeybState[DIK_NEXT]/128)
        {
            flt_Angle -= (float)0.03;
        }
    }


    int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPreviousInstance,LPSTR lpcmdline,int nCmdShow)
    {
        MSG msg_Message;

        HWND han_Window = NewWindow("DirectX C++ Tutorial",100,100,500,500);
        LPDIRECT3DDEVICE9 p_Device = InitializeDevice(han_Window);
        LPDIRECT3DVERTEXBUFFER9 p_dx_VB = FillVertices(han_Window, p_Device);
        LPDIRECT3DINDEXBUFFER9 p_dx_IB = FillIndices(han_Window, p_Device);
        SetUpCamera(p_Device);


        LPDIRECTINPUTDEVICE8 p_KeybDevice = InitializeKeyboard(han_Window);


        while(int_AppRunning)
        {
            if(PeekMessage(&msg_Message,han_Window,0,0,PM_REMOVE))
            {
                DispatchMessage(&msg_Message);
            }

            ReadKeyboard(p_KeybDevice);

            DrawScene(p_Device, p_dx_VB, p_dx_IB);
        }


        p_KeybDevice->Release();

        p_dx_VB->Release();
        p_dx_IB->Release();
        p_Device->Release();
        DestroyWindow(han_Window);

        return 0;
    }


    Google
     
    Webwww.riemers.net


    If you appreciate the amount of time I spend creating and updating
    these pages, feel free to donate -- any amount is welcome !



    - Website design & XNA + DirectX code : Riemer Grootjans -
    ©2003 - 2011 Riemer Grootjans
  • Translations

    This site in English
    This site in Korean
    This site in Czech

    Microsoft MVP Award



    2007 - 2011 MVP Award
    DirectX - XNA

    Contents

    News
    Home
    Forum
    XNA 2.0 Recipes Book (8)
    XNA 3.0 Recipes Book (8)
    Downloads
    Extra Reading (3)
    Matrices: geometrical
    Matrix Mathematics
    Homogenous matrices
    Community Projects (1)
    Tutorials (160)
    XNA 4.0 using C# (89)
    DirectX using C# (54)
    DirectX using C++ (15)
    Series 1: Terrain (15)
    Opening a window
    Ending the game loop
    Linking to the Device
    Clearing your window
    Drawing a triangle
    Culling
    Camera
    Rotation - Translation
    Indices
    Terrain creation
    Terrain from file
    DirectInput
    Importing .bmp files
    Adding colors
    DirectX Light basics
    DirectX using VB (2)
    -- Expand all --


    Thank you!

    Support this site --
    any amount is welcome !

    Stay up-to-date

    I don't have the time to keep a News section, so stay informed about the updates by clicking on this RSS file!