This tutorial is part of a Collection: 03. DirectX 11 - Braynzar Soft Tutorials
rate up
0
rate down
8407
views
bookmark
30. Heightmap (Terrain)

Here we will learn how to load a grayscale bmp image as a heightmap. This lesson builds directly off the last lesson, the Free-Look Camera.

1331 downloads
##Introduction## This lesson is built directly off the last lesson, loading a heightmap (terrain) This lesson is not a very difficult one. We will create a function which loads in a grayscale bmp image (where all RGB channels are the same for each texel), and use the values of the R channel in each texel to define the height of each vertex in our terrain. This lesson builds directly off the Free-Look Camera lesson's code. There are a lot of programs that can create a heightmap. The way I created mine though is by using the diffuse clouds filter in photoshop. This is not a very good way to do it though. You will see in the terrain that there are a couple spikes. The best way is to use a program dedicated to making heightmaps. ##Include The Vector Header## We need to include this header file so that we can use vectors, which are dynamic arrays, so we can create our "grid" or terrain. #include <vector> ##New Globals## NumFaces and NumVertices are the number of faces and vertices in our terrain. These will depend on the dimensions of our grayscale bmp image (heightmap). int NumFaces = 0; int NumVertices = 0; ##The HeightMapInfo Structure## This is a custom structure which will hold information about our hightmap. Right now we are only storing the dimensions of the heightmap, and the position of each vertex in our grid. In this lesson, we will define the normals of each vertex at run time. I will mention now, and in a bit, that this is not a good way to do it. It takes VERY long to compute the normals at runtime. What we could do (which i'll aslo explain below), is store the normal information in the RGB channels of the image, and the hieght of the vertex in the alpha (A) channel. Let this be an exercise ;) struct HeightMapInfo{ // Heightmap structure int terrainWidth; // Width of heightmap int terrainHeight; // Height (Length) of heightmap XMFLOAT3 *heightMap; // Array to store terrain's vertex positions }; ##HeightMapLoad() Function Prototype## This is the prototype of our HeightMapLoad() function. As you can see, it takes two arguments. The first argument is the name of the bmp file we are going to load, and the second is a pointer to a HeightMapInfo structure, which will be loaded with the information of the heightmap image. bool HeightMapLoad(char* filename, HeightMapInfo &hminfo); ##The HeightMapLoad() Function## This is the entire funtion that loads a grayscale bmp image, and stores the heightmap information in a HeightMapInfo structure. 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 XMFLOAT3[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 XMFLOAT3[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; ##The InitScene() Function - Loading The Heightmap## Let's go down to the function where we initialize our scene. Here we will initialize a new HeightMapInfo object and call it hmInfo. Then we call the HeightMapLoad() function with the bitmaps filename, and pass it our hmInfo object, so that it can be loaded with the information of the heightmap. HeightMapInfo hmInfo; HeightMapLoad("heightmap.bmp", hmInfo); // Load the heightmap and store it into hmInfo ##Creating The Vertices Of The Grid## Now we will create the vertex list for our grid, using the information from the heightmap image we stored in hmInfo. First we create and initialize two integers. They are the width and length (cols, rows) of the grid (vertices). Next we define the number of vertices and faces in our grid using the dimensions of our heightmap (rows, cols). Since rows and cols are the number of vertices for the width and lenth of our grid, we need to subtract one from each then multiply them together to get the number of "quads" in our grid. We need the number of triangles though, and there are two triangles in each quad, so we multiply that by 2 to get our final answer, NumFaces. After we define those, we need to initialize a vector to hold all of our vertices. Now we enter a loop, which will loop through each columb and row of the grid, and use the information stored in hmInfo to define the position of each vertex. Notice how we are defining then ormal here. We are only initializing it, so that it points directly up. If we don't initialize it, sometimes we get problems with the release version since the debug version a lot of times assumes a value is set, even when it's not, but the release version does not. On top of that, we set the normal pointing up so that we can still use light on it, although all faces of the terrain will be lit equally. If you have the normal defined in the heightmap file, you could easily just set the normal here for each vertex the same way you set the position of each vertex (which is what you will want to do when your releasing your game, because of the long ass time it takes to compute the normals at run time). int cols = hmInfo.terrainWidth; int rows = hmInfo.terrainHeight; //Create the grid NumVertices = rows * cols; NumFaces = (rows-1)*(cols-1)*2; std::vector<Vertex> v(NumVertices); for(DWORD i = 0; i < rows; ++i) { for(DWORD j = 0; j < cols; ++j) { v[i*cols+j].pos = hmInfo.heightMap[i*cols+j]; v[i*cols+j].normal = XMFLOAT3(0.0f, 1.0f, 0.0f); } } ##Index List / Texture Coordinates## Now we will create an index list. At the same time, we will be creating texture coordinates for each vertex. We are creating the texture coords for each vertex at the same time as we create the index list because texture coordinates are easiest to define when dealing with quads instead of individual vertices like above. I want to explain the texture coordinates so you understand. You can see we are adding texUIndex and texVIndex to the texture coordinates. This is because if we only set the texture coordinates for the same on all quads, the the next quad will overwrite the texture coordinates of the vertices sharing the two quads. So to get around this, we will use something called "wrapping", which i explained in the texture lesson. This is where you use values higher than 1 or lower than zero for texture coordinates, and it just repeats the texture. std::vector<DWORD> indices(NumFaces * 3); int k = 0; int texUIndex = 0; int texVIndex = 0; for(DWORD i = 0; i < rows-1; i++) { for(DWORD j = 0; j < cols-1; j++) { indices[k] = i*cols+j; // Bottom left of quad v[i*cols+j].texCoord = XMFLOAT2(texUIndex + 0.0f, texVIndex + 1.0f); indices[k+1] = i*cols+j+1; // Bottom right of quad v[i*cols+j+1].texCoord = XMFLOAT2(texUIndex + 1.0f, texVIndex + 1.0f); indices[k+2] = (i+1)*cols+j; // Top left of quad v[(i+1)*cols+j].texCoord = XMFLOAT2(texUIndex + 0.0f, texVIndex + 0.0f); indices[k+3] = (i+1)*cols+j; // Top left of quad v[(i+1)*cols+j].texCoord = XMFLOAT2(texUIndex + 0.0f, texVIndex + 0.0f); indices[k+4] = i*cols+j+1; // Bottom right of quad v[i*cols+j+1].texCoord = XMFLOAT2(texUIndex + 1.0f, texVIndex + 1.0f); indices[k+5] = (i+1)*cols+j+1; // Top right of quad v[(i+1)*cols+j+1].texCoord = XMFLOAT2(texUIndex + 1.0f, texVIndex + 0.0f); k += 6; // next quad texUIndex++; } texUIndex = 0; texVIndex++; } ##Computing The Normals (Normal Averaging)## Alright, This is where we compute the normals of each vertex in our heightmap using the technique called normal averaging. This was covered in the lesson on loading obj models, lesson 21, so i'm not going to walk through this code here. However, i want to talk about this. This section of code makes loading the app VERY fucking slow, so if your sitting there forever when waiting for the program to load when debugging, this is why it's taking so damn long. You will want to build a release version, then run that release version if you want, but it still takes me at least a minute for my release version to load. In your game, you obviously don't want to do this, so the way around this is to use pre-computed normals. In your heightmap, you are only using one channel (the red (R) channel if your using this lessons load heightmaps function). What you can do, is store the heightmap information in the alpha channel (A) of the heightmap image, and the normal information in the other three channels (RGB). As an excersise for this lesson, you can compute the normals in code, then store the normals and height information back into an image such as bmp, then change your LoadHeightMap() function so that it loads the height data from the alpha channel, and the normal data from the RGB channels. This way, you will not have to compute the normals for each vertex every time you run your app ;) //////////////////////Compute Normals/////////////////////////// //Now we will compute the normals for each vertex using normal averaging std::vector<XMFLOAT3> tempNormal; //normalized and unnormalized normals XMFLOAT3 unnormalized = XMFLOAT3(0.0f, 0.0f, 0.0f); //Used to get vectors (sides) from the position of the verts float vecX, vecY, vecZ; //Two edges of our triangle XMVECTOR edge1 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); XMVECTOR edge2 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); //Compute face normals for(int i = 0; i < NumFaces; ++i) { //Get the vector describing one edge of our triangle (edge 0,2) vecX = v[indices[(i*3)]].pos.x - v[indices[(i*3)+2]].pos.x; vecY = v[indices[(i*3)]].pos.y - v[indices[(i*3)+2]].pos.y; vecZ = v[indices[(i*3)]].pos.z - v[indices[(i*3)+2]].pos.z; edge1 = XMVectorSet(vecX, vecY, vecZ, 0.0f); //Create our first edge //Get the vector describing another edge of our triangle (edge 2,1) vecX = v[indices[(i*3)+2]].pos.x - v[indices[(i*3)+1]].pos.x; vecY = v[indices[(i*3)+2]].pos.y - v[indices[(i*3)+1]].pos.y; vecZ = v[indices[(i*3)+2]].pos.z - v[indices[(i*3)+1]].pos.z; edge2 = XMVectorSet(vecX, vecY, vecZ, 0.0f); //Create our second edge //Cross multiply the two edge vectors to get the un-normalized face normal XMStoreFloat3(&unnormalized, XMVector3Cross(edge1, edge2)); tempNormal.push_back(unnormalized); //Save unormalized normal (for normal averaging) } //Compute vertex normals (normal Averaging) XMVECTOR normalSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); int facesUsing = 0; float tX; float tY; float tZ; //Go through each vertex for(int i = 0; i < NumVertices; ++i) { //Check which triangles use this vertex for(int j = 0; j < NumFaces; ++j) { if(indices[j*3] == i || indices[(j*3)+1] == i || indices[(j*3)+2] == i) { tX = XMVectorGetX(normalSum) + tempNormal[j].x; tY = XMVectorGetY(normalSum) + tempNormal[j].y; tZ = XMVectorGetZ(normalSum) + tempNormal[j].z; normalSum = XMVectorSet(tX, tY, tZ, 0.0f); //If a face is using the vertex, add the unormalized face normal to the normalSum facesUsing++; } } //Get the actual normal by dividing the normalSum by the number of faces sharing the vertex normalSum = normalSum / facesUsing; //Normalize the normalSum vector normalSum = XMVector3Normalize(normalSum); //Store the normal in our current vertex v[i].normal.x = XMVectorGetX(normalSum); v[i].normal.y = XMVectorGetY(normalSum); v[i].normal.z = XMVectorGetZ(normalSum); //Clear normalSum and facesUsing for next vertex normalSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); facesUsing = 0; } ##Creating The Vertex and Index Buffers## This isn't new to this lesson, but i like to show everything i've changed or added from previous lessons. Since we are not using a single quad for the ground anymore, we need to change the number of indices and vertices when creating our vertex and index buffer, to match our vertex and index lists created above. I can't remember if i've explained storing vectors in buffers in an earlier lesson or not, but if not, i'll quickly explain here. When using a vector as the buffers data, you have to supply a pointer to the FIRST element in the vector (or the first element you wish to start reading from that is). Look below to see how it's done: D3D11_BUFFER_DESC indexBufferDesc; ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) ); indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; /************************************New Stuff****************************************************/ indexBufferDesc.ByteWidth = sizeof(DWORD) * NumFaces * 3; /*************************************************************************************************/ indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDesc.CPUAccessFlags = 0; indexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA iinitData; /************************************New Stuff****************************************************/ iinitData.pSysMem = &indices[0]; /*************************************************************************************************/ d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &squareIndexBuffer); D3D11_BUFFER_DESC vertexBufferDesc; ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) ); vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; /************************************New Stuff****************************************************/ vertexBufferDesc.ByteWidth = sizeof( Vertex ) * NumVertices; /*************************************************************************************************/ vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertexBufferDesc.CPUAccessFlags = 0; vertexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA vertexBufferData; ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) ); /************************************New Stuff****************************************************/ vertexBufferData.pSysMem = &v[0]; /*************************************************************************************************/ hr = d3d11Device->CreateBuffer( &vertexBufferDesc, &vertexBufferData, &squareVertBuffer); ##The UpdateScene() Function## This is also not new to the lesson, but i wanted to show you that i changed the scale and position of the terrain. Otherwise it would be WAY too big, and the camera would have started at the corner under the terrain. Change this to whatever you want though. That also reminds me, the camera is slightly slower than i would probably like, so if your interested, you can change the speed of the camera in the detectinput function. void UpdateScene(double time) { //Reset groundWorld groundWorld = XMMatrixIdentity(); /************************************New Stuff****************************************************/ //Define terrains's world space matrix Scale = XMMatrixScaling( 10.0f, 10.0f, 10.0f ); Translation = XMMatrixTranslation( -100.0f, -100.0f, -100.0f ); /************************************New Stuff****************************************************/ //Set terrains's world space using the transformations groundWorld = Scale * Translation; } ##How Many Indices To Draw?## The last thing we need to do is change the number of indices to draw. This is easily done by multiplying the number of faces by three, since there are three indices (vertices) per triangle! d3d11DevCon->DrawIndexed( NumFaces * 3, 0, 0 ); All Done! ##Exercise:## 1. Change textures depending on terrain height ;) 2. Compute the normals like we do above, then store the normals and height information into a new bmp (or other file type) image. 3. Try loading a different format other than BMP. Here's the final code: main.cpp //Include and link appropriate libraries and headers// #pragma comment(lib, "d3d11.lib") #pragma comment(lib, "d3dx11.lib") #pragma comment(lib, "d3dx10.lib") #pragma comment (lib, "D3D10_1.lib") #pragma comment (lib, "DXGI.lib") #pragma comment (lib, "D2D1.lib") #pragma comment (lib, "dwrite.lib") #pragma comment (lib, "dinput8.lib") #pragma comment (lib, "dxguid.lib") #include <windows.h> #include <d3d11.h> #include <d3dx11.h> #include <d3dx10.h> #include <xnamath.h> #include <D3D10_1.h> #include <DXGI.h> #include <D2D1.h> #include <sstream> #include <dwrite.h> #include <dinput.h> /************************************New Stuff****************************************************/ #include <vector> /*************************************************************************************************/ //Global Declarations - Interfaces// IDXGISwapChain* SwapChain; ID3D11Device* d3d11Device; ID3D11DeviceContext* d3d11DevCon; ID3D11RenderTargetView* renderTargetView; ID3D11Buffer* squareIndexBuffer; ID3D11DepthStencilView* depthStencilView; ID3D11Texture2D* depthStencilBuffer; ID3D11Buffer* squareVertBuffer; ID3D11VertexShader* VS; ID3D11PixelShader* PS; ID3D11PixelShader* D2D_PS; ID3D10Blob* D2D_PS_Buffer; ID3D10Blob* VS_Buffer; ID3D10Blob* PS_Buffer; ID3D11InputLayout* vertLayout; ID3D11Buffer* cbPerObjectBuffer; ID3D11BlendState* Transparency; ID3D11RasterizerState* CCWcullMode; ID3D11RasterizerState* CWcullMode; ID3D11ShaderResourceView* CubesTexture; ID3D11SamplerState* CubesTexSamplerState; ID3D11Buffer* cbPerFrameBuffer; ID3D10Device1 *d3d101Device; IDXGIKeyedMutex *keyedMutex11; IDXGIKeyedMutex *keyedMutex10; ID2D1RenderTarget *D2DRenderTarget; ID2D1SolidColorBrush *Brush; ID3D11Texture2D *BackBuffer11; ID3D11Texture2D *sharedTex11; ID3D11Buffer *d2dVertBuffer; ID3D11Buffer *d2dIndexBuffer; ID3D11ShaderResourceView *d2dTexture; IDWriteFactory *DWriteFactory; IDWriteTextFormat *TextFormat; IDirectInputDevice8* DIKeyboard; IDirectInputDevice8* DIMouse; std::wstring printText; //Global Declarations - Others// LPCTSTR WndClassName = L"firstwindow"; HWND hwnd = NULL; HRESULT hr; int Width = 300; int Height = 300; DIMOUSESTATE mouseLastState; LPDIRECTINPUT8 DirectInput; float rotx = 0; float rotz = 0; float scaleX = 1.0f; float scaleY = 1.0f; XMMATRIX Rotationx; XMMATRIX Rotationz; XMMATRIX WVP; XMMATRIX cube1World; XMMATRIX cube2World; XMMATRIX camView; XMMATRIX camProjection; XMMATRIX d2dWorld; XMVECTOR camPosition; XMVECTOR camTarget; XMVECTOR camUp; XMVECTOR DefaultForward = XMVectorSet(0.0f,0.0f,1.0f, 0.0f); XMVECTOR DefaultRight = XMVectorSet(1.0f,0.0f,0.0f, 0.0f); XMVECTOR camForward = XMVectorSet(0.0f,0.0f,1.0f, 0.0f); XMVECTOR camRight = XMVectorSet(1.0f,0.0f,0.0f, 0.0f); XMMATRIX camRotationMatrix; XMMATRIX groundWorld; float moveLeftRight = 0.0f; float moveBackForward = 0.0f; float camYaw = 0.0f; float camPitch = 0.0f; XMMATRIX Rotation; XMMATRIX Scale; XMMATRIX Translation; float rot = 0.01f; double countsPerSecond = 0.0; __int64 CounterStart = 0; int frameCount = 0; int fps = 0; __int64 frameTimeOld = 0; double frameTime; //Function Prototypes// bool InitializeDirect3d11App(HINSTANCE hInstance); void CleanUp(); bool InitScene(); void DrawScene(); bool InitD2D_D3D101_DWrite(IDXGIAdapter1 *Adapter); void InitD2DScreenTexture(); void UpdateScene(double time); void UpdateCamera(); void RenderText(std::wstring text, int inInt); void StartTimer(); double GetTime(); double GetFrameTime(); bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed); int messageloop(); bool InitDirectInput(HINSTANCE hInstance); void DetectInput(double time); /************************************New Stuff****************************************************/ int NumFaces = 0; int NumVertices = 0; struct HeightMapInfo{ // Heightmap structure int terrainWidth; // Width of heightmap int terrainHeight; // Height (Length) of heightmap XMFLOAT3 *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); //Create effects constant buffer's structure// struct cbPerObject { XMMATRIX WVP; XMMATRIX World; }; cbPerObject cbPerObj; struct Light { Light() { ZeroMemory(this, sizeof(Light)); } XMFLOAT3 dir; float pad; XMFLOAT4 ambient; XMFLOAT4 diffuse; }; Light light; struct cbPerFrame { Light light; }; cbPerFrame constbuffPerFrame; struct Vertex //Overloaded Vertex Structure { Vertex(){} Vertex(float x, float y, float z, float u, float v, float nx, float ny, float nz) : pos(x,y,z), texCoord(u, v), normal(nx, ny, nz){} XMFLOAT3 pos; XMFLOAT2 texCoord; XMFLOAT3 normal; }; D3D11_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0} }; UINT numElements = ARRAYSIZE(layout); 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(!InitializeDirect3d11App(hInstance)) //Initialize Direct3D { MessageBox(0, L"Direct3D Initialization - Failed", L"Error", MB_OK); return 0; } if(!InitScene()) //Initialize our scene { MessageBox(0, L"Scene Initialization - Failed", L"Error", MB_OK); return 0; } if(!InitDirectInput(hInstance)) { MessageBox(0, L"Direct Input Initialization - Failed", L"Error", MB_OK); return 0; } messageloop(); CleanUp(); return 0; } 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 + 1); 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"Lesson 4 - Begin Drawing", 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 InitializeDirect3d11App(HINSTANCE hInstance) { //Describe our SwapChain Buffer DXGI_MODE_DESC bufferDesc; ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC)); bufferDesc.Width = Width; bufferDesc.Height = Height; bufferDesc.RefreshRate.Numerator = 60; bufferDesc.RefreshRate.Denominator = 1; bufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; //Describe our SwapChain DXGI_SWAP_CHAIN_DESC swapChainDesc; ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC)); swapChainDesc.BufferDesc = bufferDesc; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.BufferCount = 1; swapChainDesc.OutputWindow = hwnd; ///////////////**************new**************//////////////////// swapChainDesc.Windowed = true; ///////////////**************new**************//////////////////// swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; // Create DXGI factory to enumerate adapters/////////////////////////////////////////////////////////////////////////// IDXGIFactory1 *DXGIFactory; HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&DXGIFactory); // Use the first adapter IDXGIAdapter1 *Adapter; hr = DXGIFactory->EnumAdapters1(0, &Adapter); DXGIFactory->Release(); //Create our Direct3D 11 Device and SwapChain////////////////////////////////////////////////////////////////////////// hr = D3D11CreateDeviceAndSwapChain(Adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, D3D11_CREATE_DEVICE_BGRA_SUPPORT, NULL, NULL, D3D11_SDK_VERSION, &swapChainDesc, &SwapChain, &d3d11Device, NULL, &d3d11DevCon); //Initialize Direct2D, Direct3D 10.1, DirectWrite InitD2D_D3D101_DWrite(Adapter); //Release the Adapter interface Adapter->Release(); //Create our BackBuffer and Render Target hr = SwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), (void**)&BackBuffer11 ); hr = d3d11Device->CreateRenderTargetView( BackBuffer11, NULL, &renderTargetView ); //Describe our Depth/Stencil Buffer D3D11_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 = D3D11_USAGE_DEFAULT; depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; depthStencilDesc.CPUAccessFlags = 0; depthStencilDesc.MiscFlags = 0; //Create the Depth/Stencil View d3d11Device->CreateTexture2D(&depthStencilDesc, NULL, &depthStencilBuffer); d3d11Device->CreateDepthStencilView(depthStencilBuffer, NULL, &depthStencilView); return true; } bool InitD2D_D3D101_DWrite(IDXGIAdapter1 *Adapter) { //Create our Direc3D 10.1 Device/////////////////////////////////////////////////////////////////////////////////////// hr = D3D10CreateDevice1(Adapter, D3D10_DRIVER_TYPE_HARDWARE, NULL,D3D10_CREATE_DEVICE_BGRA_SUPPORT, D3D10_FEATURE_LEVEL_9_3, D3D10_1_SDK_VERSION, &d3d101Device ); //Create Shared Texture that Direct3D 10.1 will render on////////////////////////////////////////////////////////////// D3D11_TEXTURE2D_DESC sharedTexDesc; ZeroMemory(&sharedTexDesc, sizeof(sharedTexDesc)); sharedTexDesc.Width = Width; sharedTexDesc.Height = Height; sharedTexDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; sharedTexDesc.MipLevels = 1; sharedTexDesc.ArraySize = 1; sharedTexDesc.SampleDesc.Count = 1; sharedTexDesc.Usage = D3D11_USAGE_DEFAULT; sharedTexDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; sharedTexDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; hr = d3d11Device->CreateTexture2D(&sharedTexDesc, NULL, &sharedTex11); // Get the keyed mutex for the shared texture (for D3D11)/////////////////////////////////////////////////////////////// hr = sharedTex11->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)&keyedMutex11); // Get the shared handle needed to open the shared texture in D3D10.1/////////////////////////////////////////////////// IDXGIResource *sharedResource10; HANDLE sharedHandle10; hr = sharedTex11->QueryInterface(__uuidof(IDXGIResource), (void**)&sharedResource10); hr = sharedResource10->GetSharedHandle(&sharedHandle10); sharedResource10->Release(); // Open the surface for the shared texture in D3D10.1/////////////////////////////////////////////////////////////////// IDXGISurface1 *sharedSurface10; hr = d3d101Device->OpenSharedResource(sharedHandle10, __uuidof(IDXGISurface1), (void**)(&sharedSurface10)); hr = sharedSurface10->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)&keyedMutex10); // Create D2D factory/////////////////////////////////////////////////////////////////////////////////////////////////// ID2D1Factory *D2DFactory; hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), (void**)&D2DFactory); D2D1_RENDER_TARGET_PROPERTIES renderTargetProperties; ZeroMemory(&renderTargetProperties, sizeof(renderTargetProperties)); renderTargetProperties.type = D2D1_RENDER_TARGET_TYPE_HARDWARE; renderTargetProperties.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED); hr = D2DFactory->CreateDxgiSurfaceRenderTarget(sharedSurface10, &renderTargetProperties, &D2DRenderTarget); sharedSurface10->Release(); D2DFactory->Release(); // Create a solid color brush to draw something with hr = D2DRenderTarget->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), &Brush); //DirectWrite/////////////////////////////////////////////////////////////////////////////////////////////////////////// hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&DWriteFactory)); hr = DWriteFactory->CreateTextFormat( L"Script", NULL, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 24.0f, L"en-us", &TextFormat ); hr = TextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); hr = TextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR); d3d101Device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_POINTLIST); return true; } /************************************New 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 XMFLOAT3[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 InitDirectInput(HINSTANCE hInstance) { hr = DirectInput8Create(hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&DirectInput, NULL); hr = DirectInput->CreateDevice(GUID_SysKeyboard, &DIKeyboard, NULL); hr = DirectInput->CreateDevice(GUID_SysMouse, &DIMouse, NULL); hr = DIKeyboard->SetDataFormat(&c_dfDIKeyboard); hr = DIKeyboard->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); hr = DIMouse->SetDataFormat(&c_dfDIMouse); hr = DIMouse->SetCooperativeLevel(hwnd, DISCL_EXCLUSIVE | DISCL_NOWINKEY | DISCL_FOREGROUND); return true; } void UpdateCamera() { camRotationMatrix = XMMatrixRotationRollPitchYaw(camPitch, camYaw, 0); camTarget = XMVector3TransformCoord(DefaultForward, camRotationMatrix ); camTarget = XMVector3Normalize(camTarget); /* // First-Person Camera XMMATRIX RotateYTempMatrix; RotateYTempMatrix = XMMatrixRotationY(camYaw); camRight = XMVector3TransformCoord(DefaultRight, RotateYTempMatrix); camUp = XMVector3TransformCoord(camUp, RotateYTempMatrix); camForward = XMVector3TransformCoord(DefaultForward, RotateYTempMatrix);*/ // Free-Look Camera camRight = XMVector3TransformCoord(DefaultRight, camRotationMatrix); camForward = XMVector3TransformCoord(DefaultForward, camRotationMatrix); camUp = XMVector3Cross(camForward, camRight); camPosition += moveLeftRight*camRight; camPosition += moveBackForward*camForward; moveLeftRight = 0.0f; moveBackForward = 0.0f; camTarget = camPosition + camTarget; camView = XMMatrixLookAtLH( camPosition, camTarget, camUp ); } void DetectInput(double time) { 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); float speed = 15.0f * time; 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)) { camYaw += mouseLastState.lX * 0.001f; camPitch += mouseCurrState.lY * 0.001f; mouseLastState = mouseCurrState; } UpdateCamera(); return; } void CleanUp() { SwapChain->SetFullscreenState(false, NULL); PostMessage(hwnd, WM_DESTROY, 0, 0); //Release the COM Objects we created SwapChain->Release(); d3d11Device->Release(); d3d11DevCon->Release(); renderTargetView->Release(); squareVertBuffer->Release(); squareIndexBuffer->Release(); VS->Release(); PS->Release(); VS_Buffer->Release(); PS_Buffer->Release(); vertLayout->Release(); depthStencilView->Release(); depthStencilBuffer->Release(); cbPerObjectBuffer->Release(); Transparency->Release(); CCWcullMode->Release(); CWcullMode->Release(); d3d101Device->Release(); keyedMutex11->Release(); keyedMutex10->Release(); D2DRenderTarget->Release(); Brush->Release(); BackBuffer11->Release(); sharedTex11->Release(); DWriteFactory->Release(); TextFormat->Release(); d2dTexture->Release(); cbPerFrameBuffer->Release(); DIKeyboard->Unacquire(); DIMouse->Unacquire(); DirectInput->Release(); } void InitD2DScreenTexture() { //Create the vertex buffer Vertex v[] = { // Front Face Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f,-1.0f, -1.0f, -1.0f), Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 0.0f,-1.0f, 1.0f, -1.0f), Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f), Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f), }; DWORD indices[] = { // Front Face 0, 1, 2, 0, 2, 3, }; D3D11_BUFFER_DESC indexBufferDesc; ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) ); indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; indexBufferDesc.ByteWidth = sizeof(DWORD) * 2 * 3; indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDesc.CPUAccessFlags = 0; indexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA iinitData; iinitData.pSysMem = indices; d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &d2dIndexBuffer); D3D11_BUFFER_DESC vertexBufferDesc; ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) ); vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; vertexBufferDesc.ByteWidth = sizeof( Vertex ) * 4; vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertexBufferDesc.CPUAccessFlags = 0; vertexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA vertexBufferData; ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) ); vertexBufferData.pSysMem = v; hr = d3d11Device->CreateBuffer( &vertexBufferDesc, &vertexBufferData, &d2dVertBuffer); //Create A shader resource view from the texture D2D will render to, //So we can use it to texture a square which overlays our scene d3d11Device->CreateShaderResourceView(sharedTex11, NULL, &d2dTexture); } bool InitScene() { InitD2DScreenTexture(); //Compile Shaders from shader file hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "VS", "vs_4_0", 0, 0, 0, &VS_Buffer, 0, 0); hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "PS", "ps_4_0", 0, 0, 0, &PS_Buffer, 0, 0); hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "D2D_PS", "ps_4_0", 0, 0, 0, &D2D_PS_Buffer, 0, 0); //Create the Shader Objects hr = d3d11Device->CreateVertexShader(VS_Buffer->GetBufferPointer(), VS_Buffer->GetBufferSize(), NULL, &VS); hr = d3d11Device->CreatePixelShader(PS_Buffer->GetBufferPointer(), PS_Buffer->GetBufferSize(), NULL, &PS); hr = d3d11Device->CreatePixelShader(D2D_PS_Buffer->GetBufferPointer(), D2D_PS_Buffer->GetBufferSize(), NULL, &D2D_PS); //Set Vertex and Pixel Shaders d3d11DevCon->VSSetShader(VS, 0, 0); d3d11DevCon->PSSetShader(PS, 0, 0); light.dir = XMFLOAT3(0.0f, 1.0f, 0.0f); light.ambient = XMFLOAT4(0.2f, 0.2f, 0.2f, 1.0f); light.diffuse = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f); /************************************New Stuff****************************************************/ HeightMapInfo hmInfo; HeightMapLoad("heightmap.bmp", hmInfo); // Load the heightmap and store it into hmInfo int cols = hmInfo.terrainWidth; int rows = hmInfo.terrainHeight; //Create the grid NumVertices = rows * cols; NumFaces = (rows-1)*(cols-1)*2; std::vector<Vertex> v(NumVertices); for(DWORD i = 0; i < rows; ++i) { for(DWORD j = 0; j < cols; ++j) { v[i*cols+j].pos = hmInfo.heightMap[i*cols+j]; v[i*cols+j].normal = XMFLOAT3(0.0f, 1.0f, 0.0f); } } std::vector<DWORD> indices(NumFaces * 3); int k = 0; int texUIndex = 0; int texVIndex = 0; for(DWORD i = 0; i < rows-1; i++) { for(DWORD j = 0; j < cols-1; j++) { indices[k] = i*cols+j; // Bottom left of quad v[i*cols+j].texCoord = XMFLOAT2(texUIndex + 0.0f, texVIndex + 1.0f); indices[k+1] = i*cols+j+1; // Bottom right of quad v[i*cols+j+1].texCoord = XMFLOAT2(texUIndex + 1.0f, texVIndex + 1.0f); indices[k+2] = (i+1)*cols+j; // Top left of quad v[(i+1)*cols+j].texCoord = XMFLOAT2(texUIndex + 0.0f, texVIndex + 0.0f); indices[k+3] = (i+1)*cols+j; // Top left of quad v[(i+1)*cols+j].texCoord = XMFLOAT2(texUIndex + 0.0f, texVIndex + 0.0f); indices[k+4] = i*cols+j+1; // Bottom right of quad v[i*cols+j+1].texCoord = XMFLOAT2(texUIndex + 1.0f, texVIndex + 1.0f); indices[k+5] = (i+1)*cols+j+1; // Top right of quad v[(i+1)*cols+j+1].texCoord = XMFLOAT2(texUIndex + 1.0f, texVIndex + 0.0f); k += 6; // next quad texUIndex++; } texUIndex = 0; texVIndex++; } //////////////////////Compute Normals/////////////////////////// //Now we will compute the normals for each vertex using normal averaging std::vector<XMFLOAT3> tempNormal; //normalized and unnormalized normals XMFLOAT3 unnormalized = XMFLOAT3(0.0f, 0.0f, 0.0f); //Used to get vectors (sides) from the position of the verts float vecX, vecY, vecZ; //Two edges of our triangle XMVECTOR edge1 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); XMVECTOR edge2 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); //Compute face normals for(int i = 0; i < NumFaces; ++i) { //Get the vector describing one edge of our triangle (edge 0,2) vecX = v[indices[(i*3)]].pos.x - v[indices[(i*3)+2]].pos.x; vecY = v[indices[(i*3)]].pos.y - v[indices[(i*3)+2]].pos.y; vecZ = v[indices[(i*3)]].pos.z - v[indices[(i*3)+2]].pos.z; edge1 = XMVectorSet(vecX, vecY, vecZ, 0.0f); //Create our first edge //Get the vector describing another edge of our triangle (edge 2,1) vecX = v[indices[(i*3)+2]].pos.x - v[indices[(i*3)+1]].pos.x; vecY = v[indices[(i*3)+2]].pos.y - v[indices[(i*3)+1]].pos.y; vecZ = v[indices[(i*3)+2]].pos.z - v[indices[(i*3)+1]].pos.z; edge2 = XMVectorSet(vecX, vecY, vecZ, 0.0f); //Create our second edge //Cross multiply the two edge vectors to get the un-normalized face normal XMStoreFloat3(&unnormalized, XMVector3Cross(edge1, edge2)); tempNormal.push_back(unnormalized); //Save unormalized normal (for normal averaging) } //Compute vertex normals (normal Averaging) XMVECTOR normalSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); int facesUsing = 0; float tX; float tY; float tZ; //Go through each vertex for(int i = 0; i < NumVertices; ++i) { //Check which triangles use this vertex for(int j = 0; j < NumFaces; ++j) { if(indices[j*3] == i || indices[(j*3)+1] == i || indices[(j*3)+2] == i) { tX = XMVectorGetX(normalSum) + tempNormal[j].x; tY = XMVectorGetY(normalSum) + tempNormal[j].y; tZ = XMVectorGetZ(normalSum) + tempNormal[j].z; normalSum = XMVectorSet(tX, tY, tZ, 0.0f); //If a face is using the vertex, add the unormalized face normal to the normalSum facesUsing++; } } //Get the actual normal by dividing the normalSum by the number of faces sharing the vertex normalSum = normalSum / facesUsing; //Normalize the normalSum vector normalSum = XMVector3Normalize(normalSum); //Store the normal in our current vertex v[i].normal.x = XMVectorGetX(normalSum); v[i].normal.y = XMVectorGetY(normalSum); v[i].normal.z = XMVectorGetZ(normalSum); //Clear normalSum and facesUsing for next vertex normalSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); facesUsing = 0; } /*************************************************************************************************/ D3D11_BUFFER_DESC indexBufferDesc; ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) ); indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; /************************************New Stuff****************************************************/ indexBufferDesc.ByteWidth = sizeof(DWORD) * NumFaces * 3; /*************************************************************************************************/ indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDesc.CPUAccessFlags = 0; indexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA iinitData; /************************************New Stuff****************************************************/ iinitData.pSysMem = &indices[0]; /*************************************************************************************************/ d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &squareIndexBuffer); D3D11_BUFFER_DESC vertexBufferDesc; ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) ); vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; /************************************New Stuff****************************************************/ vertexBufferDesc.ByteWidth = sizeof( Vertex ) * NumVertices; /*************************************************************************************************/ vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertexBufferDesc.CPUAccessFlags = 0; vertexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA vertexBufferData; ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) ); /************************************New Stuff****************************************************/ vertexBufferData.pSysMem = &v[0]; /*************************************************************************************************/ hr = d3d11Device->CreateBuffer( &vertexBufferDesc, &vertexBufferData, &squareVertBuffer); //Create the Input Layout hr = d3d11Device->CreateInputLayout( layout, numElements, VS_Buffer->GetBufferPointer(), VS_Buffer->GetBufferSize(), &vertLayout ); //Set the Input Layout d3d11DevCon->IASetInputLayout( vertLayout ); //Set Primitive Topology d3d11DevCon->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); //Create the Viewport D3D11_VIEWPORT viewport; ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT)); viewport.TopLeftX = 0; viewport.TopLeftY = 0; viewport.Width = Width; viewport.Height = Height; viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; //Set the Viewport d3d11DevCon->RSSetViewports(1, &viewport); //Create the buffer to send to the cbuffer in effect file D3D11_BUFFER_DESC cbbd; ZeroMemory(&cbbd, sizeof(D3D11_BUFFER_DESC)); cbbd.Usage = D3D11_USAGE_DEFAULT; cbbd.ByteWidth = sizeof(cbPerObject); cbbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; cbbd.CPUAccessFlags = 0; cbbd.MiscFlags = 0; hr = d3d11Device->CreateBuffer(&cbbd, NULL, &cbPerObjectBuffer); //Create the buffer to send to the cbuffer per frame in effect file ZeroMemory(&cbbd, sizeof(D3D11_BUFFER_DESC)); cbbd.Usage = D3D11_USAGE_DEFAULT; cbbd.ByteWidth = sizeof(cbPerFrame); cbbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; cbbd.CPUAccessFlags = 0; cbbd.MiscFlags = 0; hr = d3d11Device->CreateBuffer(&cbbd, NULL, &cbPerFrameBuffer); //Camera information camPosition = XMVectorSet( 0.0f, 5.0f, -8.0f, 0.0f ); camTarget = XMVectorSet( 0.0f, 0.0f, 0.0f, 0.0f ); camUp = XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f ); //Set the View matrix camView = XMMatrixLookAtLH( camPosition, camTarget, camUp ); //Set the Projection matrix camProjection = XMMatrixPerspectiveFovLH( 0.4f*3.14f, (float)Width/Height, 1.0f, 1000.0f); D3D11_BLEND_DESC blendDesc; ZeroMemory( &blendDesc, sizeof(blendDesc) ); D3D11_RENDER_TARGET_BLEND_DESC rtbd; ZeroMemory( &rtbd, sizeof(rtbd) ); rtbd.BlendEnable = true; rtbd.SrcBlend = D3D11_BLEND_SRC_COLOR; rtbd.DestBlend = D3D11_BLEND_INV_SRC_ALPHA; rtbd.BlendOp = D3D11_BLEND_OP_ADD; rtbd.SrcBlendAlpha = D3D11_BLEND_ONE; rtbd.DestBlendAlpha = D3D11_BLEND_ZERO; rtbd.BlendOpAlpha = D3D11_BLEND_OP_ADD; rtbd.RenderTargetWriteMask = D3D10_COLOR_WRITE_ENABLE_ALL; blendDesc.AlphaToCoverageEnable = false; blendDesc.RenderTarget[0] = rtbd; hr = D3DX11CreateShaderResourceViewFromFile( d3d11Device, L"grass.jpg", NULL, NULL, &CubesTexture, NULL ); // Describe the Sample State D3D11_SAMPLER_DESC sampDesc; ZeroMemory( &sampDesc, sizeof(sampDesc) ); sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; sampDesc.MinLOD = 0; sampDesc.MaxLOD = D3D11_FLOAT32_MAX; //Create the Sample State hr = d3d11Device->CreateSamplerState( &sampDesc, &CubesTexSamplerState ); d3d11Device->CreateBlendState(&blendDesc, &Transparency); D3D11_RASTERIZER_DESC cmdesc; ZeroMemory(&cmdesc, sizeof(D3D11_RASTERIZER_DESC)); cmdesc.FillMode = D3D11_FILL_SOLID; cmdesc.CullMode = D3D11_CULL_BACK; cmdesc.FrontCounterClockwise = true; hr = d3d11Device->CreateRasterizerState(&cmdesc, &CCWcullMode); cmdesc.FrontCounterClockwise = false; hr = d3d11Device->CreateRasterizerState(&cmdesc, &CWcullMode); return true; } void StartTimer() { LARGE_INTEGER frequencyCount; QueryPerformanceFrequency(&frequencyCount); countsPerSecond = double(frequencyCount.QuadPart); QueryPerformanceCounter(&frequencyCount); CounterStart = frequencyCount.QuadPart; } double GetTime() { LARGE_INTEGER currentTime; QueryPerformanceCounter(&currentTime); return double(currentTime.QuadPart-CounterStart)/countsPerSecond; } double GetFrameTime() { LARGE_INTEGER currentTime; __int64 tickCount; QueryPerformanceCounter(&currentTime); tickCount = currentTime.QuadPart-frameTimeOld; frameTimeOld = currentTime.QuadPart; if(tickCount < 0.0f) tickCount = 0.0f; return float(tickCount)/countsPerSecond; } void UpdateScene(double time) { //Reset groundWorld groundWorld = XMMatrixIdentity(); /************************************New Stuff****************************************************/ //Define terrains's world space matrix Scale = XMMatrixScaling( 10.0f, 10.0f, 10.0f ); Translation = XMMatrixTranslation( -100.0f, -100.0f, -100.0f ); /************************************New Stuff****************************************************/ //Set terrains's world space using the transformations groundWorld = Scale * Translation; } void RenderText(std::wstring text, int inInt) { d3d11DevCon->PSSetShader(D2D_PS, 0, 0); //Release the D3D 11 Device keyedMutex11->ReleaseSync(0); //Use D3D10.1 device keyedMutex10->AcquireSync(0, 5); //Draw D2D content D2DRenderTarget->BeginDraw(); //Clear D2D Background D2DRenderTarget->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f)); //Create our string std::wostringstream printString; printString << text << inInt; printText = printString.str(); //Set the Font Color D2D1_COLOR_F FontColor = D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f); //Set the brush color D2D will use to draw with Brush->SetColor(FontColor); //Create the D2D Render Area D2D1_RECT_F layoutRect = D2D1::RectF(0, 0, Width, Height); //Draw the Text D2DRenderTarget->DrawText( printText.c_str(), wcslen(printText.c_str()), TextFormat, layoutRect, Brush ); D2DRenderTarget->EndDraw(); //Release the D3D10.1 Device keyedMutex10->ReleaseSync(1); //Use the D3D11 Device keyedMutex11->AcquireSync(1, 5); //Use the shader resource representing the direct2d render target //to texture a square which is rendered in screen space so it //overlays on top of our entire scene. We use alpha blending so //that the entire background of the D2D render target is "invisible", //And only the stuff we draw with D2D will be visible (the text) //Set the blend state for D2D render target texture objects d3d11DevCon->OMSetBlendState(Transparency, NULL, 0xffffffff); //Set the d2d Index buffer d3d11DevCon->IASetIndexBuffer( d2dIndexBuffer, DXGI_FORMAT_R32_UINT, 0); //Set the d2d vertex buffer UINT stride = sizeof( Vertex ); UINT offset = 0; d3d11DevCon->IASetVertexBuffers( 0, 1, &d2dVertBuffer, &stride, &offset ); WVP = XMMatrixIdentity(); cbPerObj.WVP = XMMatrixTranspose(WVP); d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 ); d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer ); d3d11DevCon->PSSetShaderResources( 0, 1, &d2dTexture ); d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState ); d3d11DevCon->RSSetState(CWcullMode); //Draw the second cube d3d11DevCon->DrawIndexed( 6, 0, 0 ); } void DrawScene() { //Clear our render target and depth/stencil view float bgColor[4] = { 0.1f, 0.1f, 0.1f, 1.0f }; d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor); d3d11DevCon->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0); constbuffPerFrame.light = light; d3d11DevCon->UpdateSubresource( cbPerFrameBuffer, 0, NULL, &constbuffPerFrame, 0, 0 ); d3d11DevCon->PSSetConstantBuffers(0, 1, &cbPerFrameBuffer); //Set our Render Target d3d11DevCon->OMSetRenderTargets( 1, &renderTargetView, depthStencilView ); //Set the default blend state (no blending) for opaque objects d3d11DevCon->OMSetBlendState(0, 0, 0xffffffff); //Set Vertex and Pixel Shaders d3d11DevCon->VSSetShader(VS, 0, 0); d3d11DevCon->PSSetShader(PS, 0, 0); //Set the cubes index buffer d3d11DevCon->IASetIndexBuffer( squareIndexBuffer, DXGI_FORMAT_R32_UINT, 0); //Set the cubes vertex buffer UINT stride = sizeof( Vertex ); UINT offset = 0; d3d11DevCon->IASetVertexBuffers( 0, 1, &squareVertBuffer, &stride, &offset ); //Set the WVP matrix and send it to the constant buffer in effect file WVP = groundWorld * camView * camProjection; cbPerObj.WVP = XMMatrixTranspose(WVP); cbPerObj.World = XMMatrixTranspose(groundWorld); d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 ); d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer ); d3d11DevCon->PSSetShaderResources( 0, 1, &CubesTexture ); d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState ); d3d11DevCon->RSSetState(CCWcullMode); /************************************New Stuff****************************************************/ d3d11DevCon->DrawIndexed( NumFaces * 3, 0, 0 ); /*************************************************************************************************/ RenderText(L"FPS: ", fps); //Present the backbuffer to the screen 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 frameCount++; if(GetTime() > 1.0f) { fps = frameCount; frameCount = 0; StartTimer(); } frameTime = GetFrameTime(); DetectInput(frameTime); UpdateScene(frameTime); DrawScene(); } } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) { case WM_KEYDOWN: if( wParam == VK_ESCAPE ){ DestroyWindow(hwnd); } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); } Effects.fx struct Light { float3 dir; float4 ambient; float4 diffuse; }; cbuffer cbPerFrame { Light light; }; cbuffer cbPerObject { float4x4 WVP; float4x4 World; }; Texture2D ObjTexture; SamplerState ObjSamplerState; struct VS_OUTPUT { float4 Pos : SV_POSITION; float2 TexCoord : TEXCOORD; float3 normal : NORMAL; }; VS_OUTPUT VS(float4 inPos : POSITION, float2 inTexCoord : TEXCOORD, float3 normal : NORMAL) { VS_OUTPUT output; output.Pos = mul(inPos, WVP); output.normal = mul(normal, World); output.TexCoord = inTexCoord; return output; } float4 PS(VS_OUTPUT input) : SV_TARGET { input.normal = normalize(input.normal); float4 diffuse = ObjTexture.Sample( ObjSamplerState, input.TexCoord ); float3 finalColor; finalColor = diffuse * light.ambient; finalColor += saturate(dot(light.dir, input.normal) * light.diffuse * diffuse); return float4(finalColor, diffuse.a); } float4 D2D_PS(VS_OUTPUT input) : SV_TARGET { float4 diffuse = ObjTexture.Sample( ObjSamplerState, input.TexCoord ); return diffuse; }