This tutorial is part of a Collection: 02. DirectX 10 - Braynzar Soft Tutorials
rate up
0
rate down
2138
views
bookmark
26. Heightmap (Terrain)

This lesson will teach you how to load a greyscale bitmap as a heightmap to render some terrain.

There are no files for this tutorial
This lesson builds mainly off the first person camera lesson, but uses a 3rd person camera instead (previous lesson). Here we will learn how to load in a greyscale bitmap file (black, grey, and white bmp image) to add terrain to our grid from the first person camera lesson. The size of the grid depends on the dimensions of the bitmap file, and any dimensions is ok. There are many free programs that make greyscale images, so i will not cover that, but if you want a quick dirty way (the way i actually used) to make a heightmap, use photoshop with the "diffuse clouds" filter. Alright, on to the code. We'll start this lesson with a structure. This structure will hold the width and length (height from bitmap image) of the grid read from the greyscale bitmap file, along with vertex positions for the grid. After that we have our HeightMapLoad() function prototype. Since this function uses the HeightMapInfo structure in it's parameters, we are declaring it after we create the structure. struct HeightMapInfo{ // Heightmap structure int terrainWidth; // Width of heightmap int terrainHeight; // Height (Length) of heightmap D3DXVECTOR3 *heightMap; // Array to store terrain's vertex positions }; bool HeightMapLoad(char* filename, HeightMapInfo &hminfo); This is the full function that loads the bitmap file, and fills our heightmap structure. We can go through it a couple lines a time. bool HeightMapLoad(char* filename, HeightMapInfo &hminfo) { FILE *filePtr; // Point to the current position in the file BITMAPFILEHEADER bitmapFileHeader; // Structure which stores information about file BITMAPINFOHEADER bitmapInfoHeader; // Structure which stores information about image int imageSize, index; unsigned char height; // Open the file filePtr = fopen(filename,"rb"); if (filePtr == NULL) return 0; // Read bitmaps header fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1,filePtr); // Read the info header fread(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr); // Get the width and height (width and length) of the image hminfo.terrainWidth = bitmapInfoHeader.biWidth; hminfo.terrainHeight = bitmapInfoHeader.biHeight; // Size of the image in bytes. the 3 represents RBG (byte, byte, byte) for each pixel imageSize = hminfo.terrainWidth * hminfo.terrainHeight * 3; // Initialize the array which stores the image data unsigned char* bitmapImage = new unsigned char[imageSize]; // Set the file pointer to the beginning of the image data fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET); // Store image data in bitmapImage fread(bitmapImage, 1, imageSize, filePtr); // Close file fclose(filePtr); // Initialize the heightMap array (stores the vertices of our terrain) hminfo.heightMap = new D3DXVECTOR3[hminfo.terrainWidth * hminfo.terrainHeight]; // We use a greyscale image, so all 3 rgb values are the same, but we only need one for the height // So we use this counter to skip the next two components in the image data (we read R, then skip BG) int k=0; // We divide the height by this number to "water down" the terrains height, otherwise the terrain will // appear to be "spikey" and not so smooth. float heightFactor = 10.0f; // Read the image data into our heightMap array for(int j=0; j< hminfo.terrainHeight; j++) { for(int i=0; i< hminfo.terrainWidth; i++) { height = bitmapImage[k]; index = ( hminfo.terrainHeight * j) + i; hminfo.heightMap[index].x = (float)i; hminfo.heightMap[index].y = (float)height / heightFactor; hminfo.heightMap[index].z = (float)j; k+=3; } } delete [] bitmapImage; bitmapImage = 0; return true; } The function starts by declaring a couple variables. The first variable is a file pointer, which points to the current position in our file that we are reading. Next is a BITMAPFILEHEADER structure, which stores information about the FILE we are reading, after that is a BITMAPINFOHEADER which stores information about the IMAGE inside the file. Then we have two integers. the first stores the size of the image in bytes, second is an index used to keep track of our current place in the grid when filling the HeightMap structure with position information. Finally, we have a char which is used to load in the color value for the current read texel. The color value is a value from 0 to 255. Since bitmaps have 3 values (RBG), we will only need one since they will all be the same for each texel (since it's a greyscale image). FILE *filePtr; BITMAPFILEHEADER bitmapFileHeader; BITMAPINFOHEADER bitmapInfoHeader; int imageSize, index; unsigned char height; Now we will open the file to be read. We use the function fopen where the first parameter is the name of the file, and the second is what we will do with the file. rb means read binary. fopen returns a pointer to the beginning of the file, which we store in filePtr. Then we check to make sure filePtr is not null, and return if it was. filePtr = fopen(filename,"rb"); if (filePtr == NULL) return 0; Next we read the bitmaps header information. The header contains two parts, the first part is information about the actual file, which we store in the structure BITMAPFILEHEADER, and the second is information about the image stored in the file, which we will store in the structure BITMAPINFOHEADER. fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1,filePtr); fread(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr); Now we store get some actual information from the header. First we get the dimensions of the image, and store it in our HeightMap structure. We will use the dimensions of the image as the dimensions of our grid. I should make this clear, in a bitmap file, they use "height", but we interpret it as "length" for our grid. the actual "height" we will use for our grid is defined in the RBG values of each texel. hminfo.terrainWidth = bitmapInfoHeader.biWidth; hminfo.terrainHeight = bitmapInfoHeader.biHeight; imageSize = hminfo.terrainWidth * hminfo.terrainHeight * 3; Now we initialize an array of "chars" to hold our image data. After we initialize the array, we set our file pointer to the beginning of the image data stored in the bitmap file. Next we store the entire image into our char array using the function fread(), and we tell it the size of the image, so it knows when to stop reading. Finally, we close the file. unsigned char* bitmapImage = new unsigned char[imageSize]; fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET); fread(bitmapImage, 1, imageSize, filePtr); fclose(filePtr); Next we initialize the heightMap array in our HeightMap structure to be the size of the image's length and width (height and width when looking at the image). hminfo.heightMap = new D3DXVECTOR3[hminfo.terrainWidth * hminfo.terrainHeight]; The integer "k" is used to keep track of our place in the image. Bitmap color components have a range from 0 to 255. This can lead to "spikes" are very choppy terrain. We will use this next float value to "water down" the terrains height to make it more smooth. Now we enter a loop. Two loops actually, the first loop will loop through the "rows" or "height" of the image, and the second loop loops through the "colums" or "width" of the image. First thing we do in the loops is to store the current image value in our height variable, which is the actual height of the current vertex in our grid. Then we get the index of the current vertex, so we know which vertex we should be setting. After that we store the position of our current vertex in our heightmap structure. Here we update our "k" integer. We add 3 to skip the colors we do not need. What i mean is that a bitmap file stores RGB data. Since we are reading in a greyscale image, all three of the RGB components are of the same value. We only need one, so we add 3 to k to skip the G and B components and go to the next R component. int k=0; float heightFactor = 10.0f; for(int j=0; j< hminfo.terrainHeight; j++) { for(int i=0; i< hminfo.terrainWidth; i++) { height = bitmapImage[k]; index = ( hminfo.terrainHeight * j) + i; hminfo.heightMap[index].x = (float)i; hminfo.heightMap[index].y = (float)height / heightFactor; hminfo.heightMap[index].z = (float)j; k+=3; } } Now we delete our bitmapImage array and set it to 0 to save memory, then return! delete [] bitmapImage; bitmapImage = 0; return true; Now we go down to our InitScene() function, where we create our grid. The first thing we do is create a new HeightMapInfo object, then call the HeightMapLoad() function, giving it the bitmap filename we want to load, and the HeightMapInfo object we want the information stored in. We have already made the grid in the lesson on render states, so i won't go through the theory of this, but as you can see, we set the rows and colums of the grid to the dimensions of the bitmap image. then when creating the vertex positions, we just get the position we stored in the heightMap array for the current index. For an exercise, you can change the colors of the terrain depending on the height. this is where you can do that. just test the height of the current vertex, and if it's a certain height, color it a certain color! (or even better, try using textures ;) HeightMapInfo hmInfo; HeightMapLoad("heightmap.bmp", hmInfo); cols = hmInfo.terrainWidth; rows = hmInfo.terrainHeight; NumVertices = rows * cols; NumFaces = (rows-1)*(cols-1)*2; std::vector<Vertex> vertices(NumVertices); for(DWORD i = 0; i < rows; ++i) { for(DWORD j = 0; j < cols; ++j) { float x = ((cols - 1) * 0.5f) - j; vertices[i*cols+j].pos = hmInfo.heightMap[i*cols+j]; vertices[i*cols+j].color = D3DXCOLOR(0.0f, 0.0f, 1.0f, 1.0f); } } Now we have some terrain to play with! ##Exercise:## 1. Change the color of the terrain depending on the height. 2. Try texturing the terrain, then try to change the texture of the terrain depending on it's height. (hint: you can set the texture coords when getting the index list, since the index list does one "quad" at a time) 3. Try making a first person camera to walk around on the terrain. You will have to find the current triangle you are on, then get the height of the cameras x and z position on that triangle. Here's the final code: main.cpp #include <Windows.h> #include <d3d10.h> #include <d3dx10.h> #include <string> #include <vector> #include <dinput.h> #include <fstream> #include <istream> #pragma comment (lib, "dinput8.lib") #pragma comment (lib, "dxguid.lib") #pragma comment(lib, "D3D10.lib") #pragma comment(lib, "d3dx10d.lib") using namespace std; LPCTSTR WndClassName = L"firstwindow"; HWND hwnd = NULL; const int Width = 300; const int Height = 300; bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed); HRESULT hr; ID3D10Device* d3dDevice; IDXGISwapChain* SwapChain; ID3D10RenderTargetView* RenderTargetView; ID3D10Effect* FX; ID3D10InputLayout* VertexLayout; ID3D10Buffer* VertexBuffer; ID3D10Buffer* IndexBuffer; ID3D10EffectTechnique* Technique; ID3D10DepthStencilView* DepthStencilView; ID3D10Texture2D* DepthStencilBuffer; ID3D10ShaderResourceView* DiffuseMapResourceView; ID3D10EffectShaderResourceVariable* fxDiffuseMapVar; D3DXMATRIX WVP; ID3D10EffectMatrixVariable* fxWVPVar; D3DXMATRIX World; D3DXMATRIX View; D3DXMATRIX Projection; IDirectInputDevice8* DIKeyboard; IDirectInputDevice8* DIMouse; DIMOUSESTATE mouseLastState; LPDIRECTINPUT8 DirectInput; ID3D10RasterizerState* WireFrame; DWORD NumVertices; DWORD NumFaces; DWORD rows = 100; DWORD cols = 100; float moveLeftRight = 0.0f; float moveBackForward = 0.0f; D3DXVECTOR3 Position = D3DXVECTOR3( 100.0f, 50.0f, 0.0f ); D3DXVECTOR3 Target = D3DXVECTOR3( 0.0f, 0.0f, 1.0f); D3DXVECTOR3 Up = D3DXVECTOR3( 0.0f, 1.0f, 0.0f ); D3DXVECTOR3 DefaultForward = D3DXVECTOR3(0.0f,0.0f,1.0f); D3DXVECTOR3 DefaultRight = D3DXVECTOR3(1.0f,0.0f,0.0f); D3DXVECTOR3 Forward = D3DXVECTOR3(0.0f,0.0f,1.0f); D3DXVECTOR3 Right = D3DXVECTOR3(1.0f,0.0f,0.0f); D3DXMATRIX rotationMatrix; float yaw = 0.0f; float pitch = 0.0f; void UpdateCamera(); bool InitializeDirect3dApp(HINSTANCE hInstance); bool InitDirectInput(HINSTANCE hInstance); void DetectInput(); bool InitScene(); void DrawScene(); bool ReleaseObjects(); int messageloop(); /************************************Heightmap Stuff****************************************************/ struct HeightMapInfo{ // Heightmap structure int terrainWidth; // Width of heightmap int terrainHeight; // Height (Length) of heightmap D3DXVECTOR3 *heightMap; // Array to store terrain's vertex positions }; bool HeightMapLoad(char* filename, HeightMapInfo &hminfo); /*******************************************************************************************************/ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); struct Vertex { Vertex(){} Vertex(float x, float y, float z, float fr, float fg, float fb, float fa) : pos(x,y,z), color(fr, fg, fb, fa) {} D3DXVECTOR3 pos; D3DXCOLOR color; }; int WINAPI WinMain(HINSTANCE hInstance, //Main windows function HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { if(!InitializeWindow(hInstance, nShowCmd, Width, Height, true)) { MessageBox(0, L"Window Initialization - Failed", L"Error", MB_OK); return 0; } if(!InitializeDirect3dApp(hInstance)) { MessageBox(0, L"Direct3D Initialization - Failed", L"Error", MB_OK); return 0; } if(!InitDirectInput(hInstance)) { MessageBox(0, L"Direct Input Initialization - Failed", L"Error", MB_OK); return 0; } if(!InitScene()) { MessageBox(0, L"Scene Initialization - Failed", L"Error", MB_OK); return 0; } messageloop(); if(!ReleaseObjects()) { MessageBox(0, L"Object Releasing - Failed", L"Error", MB_OK); return 0; } return 0; } /************************************Heightmap Stuff****************************************************/ bool HeightMapLoad(char* filename, HeightMapInfo &hminfo) { FILE *filePtr; // Point to the current position in the file BITMAPFILEHEADER bitmapFileHeader; // Structure which stores information about file BITMAPINFOHEADER bitmapInfoHeader; // Structure which stores information about image int imageSize, index; unsigned char height; // Open the file filePtr = fopen(filename,"rb"); if (filePtr == NULL) return 0; // Read bitmaps header fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1,filePtr); // Read the info header fread(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr); // Get the width and height (width and length) of the image hminfo.terrainWidth = bitmapInfoHeader.biWidth; hminfo.terrainHeight = bitmapInfoHeader.biHeight; // Size of the image in bytes. the 3 represents RBG (byte, byte, byte) for each pixel imageSize = hminfo.terrainWidth * hminfo.terrainHeight * 3; // Initialize the array which stores the image data unsigned char* bitmapImage = new unsigned char[imageSize]; // Set the file pointer to the beginning of the image data fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET); // Store image data in bitmapImage fread(bitmapImage, 1, imageSize, filePtr); // Close file fclose(filePtr); // Initialize the heightMap array (stores the vertices of our terrain) hminfo.heightMap = new D3DXVECTOR3[hminfo.terrainWidth * hminfo.terrainHeight]; // We use a greyscale image, so all 3 rgb values are the same, but we only need one for the height // So we use this counter to skip the next two components in the image data (we read R, then skip BG) int k=0; // We divide the height by this number to "water down" the terrains height, otherwise the terrain will // appear to be "spikey" and not so smooth. float heightFactor = 10.0f; // Read the image data into our heightMap array for(int j=0; j< hminfo.terrainHeight; j++) { for(int i=0; i< hminfo.terrainWidth; i++) { height = bitmapImage[k]; index = ( hminfo.terrainHeight * j) + i; hminfo.heightMap[index].x = (float)i; hminfo.heightMap[index].y = (float)height / heightFactor; hminfo.heightMap[index].z = (float)j; k+=3; } } delete [] bitmapImage; bitmapImage = 0; return true; } /*******************************************************************************************************/ bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed) { typedef struct _WNDCLASS { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; } WNDCLASS; WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = NULL; wc.cbWndExtra = NULL; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 2); wc.lpszMenuName = NULL; wc.lpszClassName = WndClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if (!RegisterClassEx(&wc)) { MessageBox(NULL, L"Error registering class", L"Error", MB_OK | MB_ICONERROR); return 1; } hwnd = CreateWindowEx( NULL, WndClassName, L"Window Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, NULL, NULL, hInstance, NULL ); if (!hwnd) { MessageBox(NULL, L"Error creating window", L"Error", MB_OK | MB_ICONERROR); return 1; } ShowWindow(hwnd, ShowWnd); UpdateWindow(hwnd); return true; } bool InitializeDirect3dApp(HINSTANCE hInstance) { UINT createDeviceFlags = 0; D3D10_DRIVER_TYPE driverTypes[] = { D3D10_DRIVER_TYPE_HARDWARE, D3D10_DRIVER_TYPE_REFERENCE, }; UINT numDriverTypes = sizeof( driverTypes ) / sizeof( driverTypes[0] ); DXGI_SWAP_CHAIN_DESC scd; scd.BufferDesc.Width = Width; scd.BufferDesc.Height = Height; scd.BufferDesc.RefreshRate.Numerator = 60; scd.BufferDesc.RefreshRate.Denominator = 1; scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; scd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; scd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; //no multisampling scd.SampleDesc.Count = 1; scd.SampleDesc.Quality = 0; scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; scd.BufferCount = 1; scd.OutputWindow = hwnd; scd.Windowed = true; scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; scd.Flags = 0; D3D10CreateDeviceAndSwapChain(0, D3D10_DRIVER_TYPE_HARDWARE, 0, 0, D3D10_SDK_VERSION, &scd, &SwapChain, &d3dDevice); ID3D10Texture2D* backBuffer; SwapChain->GetBuffer(0, _uuidof(ID3D10Texture2D), reinterpret_cast<void**>(&backBuffer)); d3dDevice->CreateRenderTargetView(backBuffer, 0, &RenderTargetView); backBuffer->Release(); D3D10_TEXTURE2D_DESC depthStencilDesc; depthStencilDesc.Width = Width; depthStencilDesc.Height = Height; depthStencilDesc.MipLevels = 1; depthStencilDesc.ArraySize = 1; depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depthStencilDesc.SampleDesc.Count = 1; depthStencilDesc.SampleDesc.Quality = 0; depthStencilDesc.Usage = D3D10_USAGE_DEFAULT; depthStencilDesc.BindFlags = D3D10_BIND_DEPTH_STENCIL; depthStencilDesc.CPUAccessFlags = 0; depthStencilDesc.MiscFlags = 0; d3dDevice->CreateTexture2D(&depthStencilDesc, NULL, &DepthStencilBuffer); d3dDevice->CreateDepthStencilView(DepthStencilBuffer, NULL, &DepthStencilView); d3dDevice->OMSetRenderTargets(1, &RenderTargetView, DepthStencilView); // Setup the viewport D3D10_VIEWPORT vp; vp.Width = Width; vp.Height = Height; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = 0; vp.TopLeftY = 0; d3dDevice->RSSetViewports( 1, &vp ); D3DXMatrixIdentity( &World ); D3DXMatrixLookAtLH( &View, &Position, &Target, &Up ); return true; } bool InitDirectInput(HINSTANCE hInstance) { DirectInput8Create(hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&DirectInput, NULL); DirectInput->CreateDevice(GUID_SysKeyboard, &DIKeyboard, NULL); DirectInput->CreateDevice(GUID_SysMouse, &DIMouse, NULL); DIKeyboard->SetDataFormat(&c_dfDIKeyboard); DIKeyboard->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); DIMouse->SetDataFormat(&c_dfDIMouse); DIMouse->SetCooperativeLevel(hwnd, DISCL_EXCLUSIVE | DISCL_NOWINKEY | DISCL_FOREGROUND); return true; } void UpdateCamera() { D3DXMatrixRotationYawPitchRoll( &rotationMatrix, yaw, pitch, 0 ); D3DXVec3TransformCoord( &Target, &DefaultForward, &rotationMatrix ); D3DXVec3Normalize( &Target, &Target ); /* D3DXMATRIX RotateYTempMatrix; D3DXMatrixRotationY(&RotateYTempMatrix, yaw); // First-Person Camera D3DXVec3TransformNormal(&Right, &DefaultRight, &RotateYTempMatrix); D3DXVec3TransformNormal(&Up, &Up, &RotateYTempMatrix); D3DXVec3TransformNormal(&Forward, &DefaultForward, &RotateYTempMatrix); */ // Free-Look Camera D3DXVec3TransformNormal(&Right, &DefaultRight, &rotationMatrix); D3DXVec3TransformNormal(&Forward, &DefaultForward, &rotationMatrix); D3DXVec3Cross(&Up, &Forward, &Right); Position += moveLeftRight*Right; Position += moveBackForward*Forward; moveLeftRight = 0.0f; moveBackForward = 0.0f; Target = Position + Target; D3DXMatrixLookAtLH( &View, &Position, &Target, &Up ); } void DetectInput() { float speed = 0.5f; DIMOUSESTATE mouseCurrState; BYTE keyboardState[256]; DIKeyboard->Acquire(); DIMouse->Acquire(); DIMouse->GetDeviceState(sizeof(DIMOUSESTATE), &mouseCurrState); DIKeyboard->GetDeviceState(sizeof(keyboardState),(LPVOID)&keyboardState); if(keyboardState[DIK_ESCAPE] & 0x80) PostMessage(hwnd, WM_DESTROY, 0, 0); if(keyboardState[DIK_A] & 0x80) { moveLeftRight -= speed; } if(keyboardState[DIK_D] & 0x80) { moveLeftRight += speed; } if(keyboardState[DIK_W] & 0x80) { moveBackForward += speed; } if(keyboardState[DIK_S] & 0x80) { moveBackForward -= speed; } if((mouseCurrState.lX != mouseLastState.lX) || (mouseCurrState.lY != mouseLastState.lY)) { yaw += mouseLastState.lX * 0.001f; pitch += mouseCurrState.lY * 0.001f; mouseLastState = mouseCurrState; } UpdateCamera(); return; } bool InitScene() { /************************************Heightmap Stuff****************************************************/ HeightMapInfo hmInfo; HeightMapLoad("heightmap.bmp", hmInfo); cols = hmInfo.terrainWidth; rows = hmInfo.terrainHeight; NumVertices = rows * cols; NumFaces = (rows-1)*(cols-1)*2; std::vector<Vertex> vertices(NumVertices); for(DWORD i = 0; i < rows; ++i) { for(DWORD j = 0; j < cols; ++j) { float x = ((cols - 1) * 0.5f) - j; vertices[i*cols+j].pos = hmInfo.heightMap[i*cols+j]; vertices[i*cols+j].color = D3DXCOLOR(0.0f, 0.0f, 1.0f, 1.0f); } } /*******************************************************************************************************/ D3D10_BUFFER_DESC bd; bd.Usage = D3D10_USAGE_IMMUTABLE; bd.ByteWidth = sizeof( Vertex ) * NumVertices; bd.BindFlags = D3D10_BIND_VERTEX_BUFFER; bd.CPUAccessFlags = 0; bd.MiscFlags = 0; D3D10_SUBRESOURCE_DATA InitData; InitData.pSysMem = &vertices[0]; d3dDevice->CreateBuffer( &bd, &InitData, &VertexBuffer ); std::vector<DWORD> indices(NumFaces * 3); int k = 0; for(DWORD i = 0; i < rows-1; ++i) { for(DWORD j = 0; j < cols-1; ++j) { indices[k+5] = i*cols+j; indices[k+4] = i*cols+j+1; indices[k+3] = (i+1)*cols+j; indices[k+2] = (i+1)*cols+j; indices[k+1] = i*cols+j+1; indices[k+0] = (i+1)*cols+j+1; k += 6; // next quad } } D3D10_BUFFER_DESC ibd; ibd.Usage = D3D10_USAGE_IMMUTABLE; ibd.ByteWidth = sizeof(DWORD) * NumFaces * 3; ibd.BindFlags = D3D10_BIND_INDEX_BUFFER; ibd.CPUAccessFlags = 0; ibd.MiscFlags = 0; D3D10_SUBRESOURCE_DATA iinitData; iinitData.pSysMem = &indices[0]; d3dDevice->CreateBuffer(&ibd, &iinitData, &IndexBuffer); UINT stride = sizeof( Vertex ); UINT offset = 0; d3dDevice->IASetVertexBuffers( 0, 1, &VertexBuffer, &stride, &offset ); d3dDevice->IASetIndexBuffer(IndexBuffer, DXGI_FORMAT_R32_UINT, 0); D3D10_INPUT_ELEMENT_DESC layout[] = { {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0}, }; ID3D10Blob* compilationErrors = 0; HRESULT hr = 0; hr = D3DX10CreateEffectFromFile( L"vertex.fx", NULL, NULL, "fx_4_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, d3dDevice, NULL, NULL, &FX, &compilationErrors, NULL ); if(FAILED(hr)) { MessageBoxA(0, (char*)compilationErrors->GetBufferPointer(), 0, 0); compilationErrors->Release(); return false; } D3DX10CreateShaderResourceViewFromFile(d3dDevice, L"braynzar.jpg", 0, 0, &DiffuseMapResourceView, 0 ); Technique = FX->GetTechniqueByName( "Tech" ); fxWVPVar = FX->GetVariableByName("WVP")->AsMatrix(); fxDiffuseMapVar = FX->GetVariableByName("DiffuseMap")->AsShaderResource(); D3D10_PASS_DESC PassDesc; Technique->GetPassByIndex( 0 )->GetDesc( &PassDesc ); d3dDevice->CreateInputLayout( layout, 2, PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &VertexLayout ); d3dDevice->IASetInputLayout( VertexLayout ); d3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); D3D10_RASTERIZER_DESC wfdesc; ZeroMemory(&wfdesc, sizeof(D3D10_RASTERIZER_DESC)); wfdesc.FillMode = D3D10_FILL_WIREFRAME; wfdesc.CullMode = D3D10_CULL_FRONT; wfdesc.FrontCounterClockwise = true; hr = d3dDevice->CreateRasterizerState(&wfdesc, &WireFrame); if(FAILED(hr)) { MessageBox(0, L"RS STATE Creation - Failed", L"Error", MB_OK); return false; } return true; } bool ReleaseObjects() { if( d3dDevice ) d3dDevice->ClearState(); if( VertexBuffer ) VertexBuffer->Release(); if( IndexBuffer ) IndexBuffer->Release(); if( VertexLayout ) VertexLayout->Release(); if( FX ) FX->Release(); if( RenderTargetView ) RenderTargetView->Release(); if( SwapChain ) SwapChain->Release(); if( d3dDevice ) d3dDevice->Release(); if( DiffuseMapResourceView ) DiffuseMapResourceView->Release(); DIKeyboard->Unacquire(); DIMouse->Unacquire(); DirectInput->Release(); return true; } void DrawScene() { //Draw Scene Here D3DXCOLOR bgColor( 0.0f, 0.0f, 0.0f, 1.0f); d3dDevice->ClearRenderTargetView( RenderTargetView, bgColor ); d3dDevice->ClearDepthStencilView(DepthStencilView, D3D10_CLEAR_DEPTH|D3D10_CLEAR_STENCIL, 1.0f, 0); D3DXMatrixPerspectiveFovLH(&Projection, 0.4f*3.14f, (float)Width/Height, 1.0f, 1000.0f); D3D10_TECHNIQUE_DESC techDesc; Technique->GetDesc( &techDesc ); d3dDevice->RSSetState(NULL); //Draw stuff without wireframe d3dDevice->RSSetState(WireFrame); WVP = World * View * Projection; fxWVPVar->SetMatrix((float*)&WVP); //draw wireframe plane for( UINT p = 0; p < techDesc.Passes; ++p ) { Technique->GetPassByIndex( p )->Apply( 0 ); d3dDevice->DrawIndexed(NumFaces*3, 0, 0); } SwapChain->Present( 0, 0 ); } int messageloop(){ MSG msg; ZeroMemory(&msg, sizeof(MSG)); while(true) { BOOL PeekMessageL( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg ); if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } else{ // run game code DetectInput(); DrawScene(); } } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) { case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); } vertex.fx struct Light { float3 dir; float4 ambient; float4 diffuse; }; cbuffer cbPerFrame { Light light; }; cbuffer cbPerObject { float4x4 WVP; }; Texture2D DiffuseMap; TextureCube SkyMap; SamplerState TriLinearSample { Filter = MIN_MAG_MIP_LINEAR; }; struct VS_OUTPUT //output structure for vertex shader { float4 Pos : SV_POSITION; float2 texCoord : TEXCOORD; float3 normal : NORMAL; }; struct SKYMAP_VS_OUTPUT //output structure for skymap vertex shader { float4 Pos : SV_POSITION; float3 texCoord : TEXCOORD; }; // Vertex Shader VS_OUTPUT VS(float3 inPos : POSITION, float2 inTexCoord : TEXCOORD, float3 normal : NORMAL) { VS_OUTPUT output = (VS_OUTPUT)0; output.Pos = mul(float4(inPos, 1.0f), WVP); output.normal = mul(normal, WVP); output.texCoord = inTexCoord; return output; //send color and position to pixel shader } SKYMAP_VS_OUTPUT SKYMAP_VS(float3 inPos : POSITION, float2 inTexCoord : TEXCOORD, float3 normal : NORMAL) { SKYMAP_VS_OUTPUT output = (SKYMAP_VS_OUTPUT)0; output.Pos = mul(float4(inPos, 1.0f), WVP).xyww; output.texCoord = inPos; return output; //send color and position to pixel shader } // Pixel Shader float4 PS(VS_OUTPUT input) : SV_Target { input.normal = normalize(input.normal); float4 diffuse = DiffuseMap.Sample( TriLinearSample, input.texCoord ); float3 finalColor; finalColor = diffuse * light.ambient; finalColor += saturate(dot(light.dir, input.normal) * light.diffuse * diffuse); return float4(finalColor, diffuse.a); } float4 SKYMAP_PS(SKYMAP_VS_OUTPUT input) : SV_Target { return SkyMap.Sample(TriLinearSample, input.texCoord); } RasterizerState NoCulling { CullMode = None; }; DepthStencilState LessEqualDSS { DepthFunc = LESS_EQUAL; }; technique10 Tech { pass P0 { SetVertexShader( CompileShader( vs_4_0, VS() ) ); SetPixelShader( CompileShader( ps_4_0, PS() ) ); } } technique10 SkyMapTech { pass P0 { SetVertexShader( CompileShader( vs_4_0, SKYMAP_VS() ) ); SetPixelShader( CompileShader( ps_4_0, SKYMAP_PS() ) ); SetRasterizerState(NoCulling); SetDepthStencilState(LessEqualDSS, 0); } }