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

Adding some color and the Z-Buffer

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


You might already have a rotating terrain, but it definitely would be better looking filled with some colors instead of just plain white lines. One idea to do this, is to use natural colors, like the ones that we find in the mountains. At the bottom we have blue lakes, then the green trees, the brown mountain and finally snow topped peaks. To keep this tutorial general for every image, you can’t expect every image to have a lake at height 0, and a mountain peak at height 255 (the maximum value for a .bmp pixel). Imagine an image with height values only between 50 and 200, this image would then probably produce a terrain without any lakes or snow topped peaks.
 
To stay general, we first have to detect the minimum and maximum heights in our image. We will store these in the following global variables, which we initialize with the opposite values:

 short unsigned int MinimumHeight = 255;
 short unsigned int MaximumHeight = 0;

When you run your program, you are now going to check if the current pixel’s height is below the current MinimumHeight or above the current MaximumHeight so we can work out what the true Minimum and Maximum values are. We are going to do this in a new function

 void SetHeightLimits(void) //new
 {
     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;

                if (height < MinimumHeight)
                    {
                    MinimumHeight = height;
                }
                if (height > MaximumHeight)
                {
                    MaximumHeight = height;
                }
            }
        }
    }
}

of course we need to call this function so near the beginning of your WinMain function add

 SetHeightLimits();

With these variables filled, you can specify the 4 regions of your colors:



Now when you declare your vertices and their colors in the VertexDeclaration method, you are going to define the desired colors to the correct height regions as follows:

 if (height < MinimumHeight + (MaximumHeight - MinimumHeight) /4) //new
 {
     cv_Vertices[y*WIDTH + x].color = 0xff0000ff;
 }
 else if (height < MinimumHeight + (MaximumHeight - MinimumHeight) * 2/4)
 {
     cv_Vertices[y*WIDTH + x].color = 0xff228b22;
 }
 else if (height < MinimumHeight + (MaximumHeight - MinimumHeight) * 3/4)
 {
     cv_Vertices[y*WIDTH + x].color = 0xffa52a2a;
 }
 else
 {
     cv_Vertices[y*WIDTH + x].color = 0xffffffff;
 }

When your run this code, you will indeed see a nicely colored network of lines. When we want to see the whole colored terrain, we just have to remove this line :

 p_dx_Device->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);

When you execute this, take a few moments to rotate the terrain a couple of times. Pay special attention to the middle area. You will see that sometimes the middle peaks get overdrawn by the ‘invisible’ lake behind it. This is simply because we have not yet defined a ‘Z-buffer’! This is nothing more than an array where your video card keeps track of the depth coordinate of every pixel that should be drawn on your screen (so in our case, a 500x500 matrix!). Every time your card receives a triangle to draw, it checks whether the triangle’s pixels are closer to the screen than the pixels already present in the Z-buffer. If they are closer, the Z-buffer’s contents is updated with these pixels for that region.
 
Of course, this whole process if fully automated. All you have to do is to define the Z-buffer when you create your device. To do this, add these 2 lines to your PresentParameters :

 dx_PresParams.EnableAutoDepthStencil = TRUE;
 dx_PresParams.AutoDepthStencilFormat = D3DFMT_D16;

Here you create a Z-buffer with a precision of 16 bits. What this means in short: Every distance is presented between 0 and 1, with 0 being the near plane (1f in our case) and 1 being the far plane (250f in our case). With 16 bits, you have 2^16 = 65536 possible distances between them. Most cards support 32 bits, but my guess is 16bit precision will be more than enough for our application. The last line activates the previously defined Z-buffer. Run this code.
 
Not as expected, indeed. What could be wrong? We didn’t initialize our buffer. Imagine the whole buffer filled with zeroes. When we would draw a triangle, it would be further away from the viewer than what was previously defined in the Z-buffer, so our triangle is discarded. So in fact, we have to first fill our buffer with ones. To do this automatically every update of our screen, start with it in the device.Clear method :

 p_dx_Device->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

Now you’ll see the terrain rotating as expected!

Note: you don't seem to need this next step (I assume it is set to true by default) but it doesn't hurt to add it.

To force enabling of the Zbuffer add the following line to the end of the InitializeDevice function

 p_dx_Device->SetRenderState(D3DRS_ZENABLE, true);




DirectX Tutorial 14 - Adding colors

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!




Although now we’ve got some colors in our terrain, it doesn’t really look any better than it did last chapter.. What we need at this moment are shadows. No shadows without lights, so let’s move to the next chapter!

The 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();

short unsigned int MinimumHeight = 255; //new
short unsigned int MaximumHeight = 0;    //new

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;
     dx_PresParams.EnableAutoDepthStencil = TRUE;        
     dx_PresParams.AutoDepthStencilFormat = D3DFMT_D16;

     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_ZENABLE, true);            
    

     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->Clear(0, NULL, D3DCLEAR_ZBUFFER, 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;

                if (height < MinimumHeight + (MaximumHeight - MinimumHeight) /4) //new
                {
                    cv_Vertices[y*WIDTH + x].color = 0xff0000ff;
                }
                else if (height < MinimumHeight + (MaximumHeight - MinimumHeight) * 2/4)
                {
                    cv_Vertices[y*WIDTH + x].color = 0xff228b22;
                }
                else if (height < MinimumHeight + (MaximumHeight - MinimumHeight) * 3/4)
                {
                    cv_Vertices[y*WIDTH + x].color = 0xffa52a2a;
                }
                else
                {
                    cv_Vertices[y*WIDTH + x].color = 0xffffffff;
                } //new fin
            }
         }
    }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;
     }
}

void SetHeightLimits(void)
{
    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;

                if (height < MinimumHeight)
                {
                    MinimumHeight = height;
                }
                if (height > MaximumHeight)
                {
                    MaximumHeight = height;
                }
            }
        }
    }
}

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

     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!