This tutorial is part of a Collection: 02. DirectX 10 - Braynzar Soft Tutorials
1593
views
21. High Resolution Timer
We will learn how to create a high resolution timer, which we will use to find and display the frame rate (frames per second). We will also use this timer to make sure the speed of our camera does not fluxuate with the frames per second, keeping it's speed nice and smooth. You can also use this timer to keep animations smooth.
Here we will learn how to use the QueryPerformanceCounter() and QueryPerformanceTimer() functions to impliment a high resolution performance game timer which we will use to find the Frames per Second, and display them on the screen using the ID3DX10Font interface we learned about in one of the earlier lessons. We will also use this timer to find the time it takes for each frame, and update our camera depending on the time it takes per frame. This will keep the camera moving smoothly and at the same speed, whatever the frames per second are. To make things slightly more fun we'll also include a score with the FPS, where you get one point for every sphere you shoot. First we have a new include. This is included so we can create a wide output string stream, which we will use to first put into a wide string variable then display it on the screen. #include <sstream> Here are our globals. First we create a ID3DX10Font object called Font, then we create a wide string to store the string we want to display onto the screen. After that we have an integer to store our score. Then a double and 64 bit integer, to hold our counts per second and CounterStart variables, which we will talk about soon. Then two more integers, one for the frame count which we add one to every scene and reset after a second, and the fps, which we store the frames per second into every second. Then three new functions, one to start the timer, one to return the number of seconds since the timer started, and one to return the time in seconds for each frame. The last two variables are used for storing and finding the time for each frame. We will use the last one, frameTime, to update our camera. ID3DX10Font* Font; wstring printScoreFPS = L""; int totalScore = 0; double countsPerSecond = 0.0; __int64 CounterStart = 0; int frameCount = 0; int fps = 0; void StartTimer(); double GetTimer(); double GetFrameTime(); __int64 frameTimeOld = 0; double frameTime; In our pick function, at the bottom where we set a sphere to 0 if it was picked, we need to update our score if an object was picked. We can do this here. if(closestObject >= 0) { spheres[closestObject] = 0; /////////////////////////new///////////////////////////////////////////////// totalScore++; /////////////////////////new///////////////////////////////////////////////// } Go to the detect input function where we updated this line to include the frameTime variable, which will make sure our camera is moving at a constant speed. float speed = 10.0f * frameTime; Now on to the heart of this lesson. The timer functions. To get the time, we will use two win32 functions, QueryPerformanceFrequency() and QueryPerformanceCounter(). QueryPerformanceFrequency() returns a 64 bit integer value, representing the frequency, or number of counts per second. Then we use the QueryPerformanceCounter() function to get the current time in counts, which is also stored in a 64 bit integer variable. So the StartTimer() function stores the frequency (counts per second) in a variable we called countsPerSecond, and stores the current time in counts in a variable called CounterStart. The GetTime() function gets the current time in counts using the QueryPerformanceCounter() and stores it into the 64 bit integer we called currentTime. Then we subtract the CounterStart value from the currentTime value, giving us the amount of counts since we last called StartTimer(), Then we divide that number by the countsPerSecond variable, to get the time in seconds since the StartTimer() function was last called, which is what the GetTime() function returns. The GetFrameTime() function basically does what the other two functions do, except instead of finding the time since we called startTimer, we find the time since GetFrameTime was last called, which we will call once every frame. This will return the time in seconds each frame takes to process, which we can then use to make sure our camera and animations are updated smoothly. void StartTimer() { LARGE_INTEGER frequencyCount; QueryPerformanceFrequency(&frequencyCount); countsPerSecond = double(frequencyCount.QuadPart); QueryPerformanceCounter(&frequencyCount); CounterStart = frequencyCount.QuadPart; } double GetTime() { LARGE_INTEGER currentTime; QueryPerformanceCounter(¤tTime); return double(currentTime.QuadPart-CounterStart)/countsPerSecond; } double GetFrameTime() { LARGE_INTEGER currentTime; __int64 tickCount; QueryPerformanceCounter(¤tTime); tickCount = currentTime.QuadPart-frameTimeOld; frameTimeOld = currentTime.QuadPart; if(tickCount < 0.0f) tickCount = 0.0f; return float(tickCount)/countsPerSecond; } Here in our InitScene() function, we create our Font. We have already covered all this in that one lesson on simple fonts. D3DX10_FONT_DESC fd; fd.Height = 25; fd.Width = 0; fd.Weight = 0; fd.MipLevels = 1; fd.Italic = false; fd.CharSet = OUT_DEFAULT_PRECIS; fd.Quality = DEFAULT_QUALITY; fd.PitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; wcscpy_s(fd.FaceName, L"Arial"); D3DX10CreateFontIndirect(d3dDevice, &fd, &Font); At the bottom of our InitScene() function, we call StartTimer(), which stores the current time in counts and the frequency (counts per second). StartTimer(); Don't forget to release the font object in the release objects function. Font->Release(); Go to the drawScene function. You will notice we have re-set our depth/stencil state, input layout, and topology. We need to do this because a call to the ID3DX10Font::DrawText() function changes all that. Not only does it change that, it changes the geometry shader in the effect file, so we need to remember to reset the geometry shader too, which we will do soon. d3dDevice->OMSetDepthStencilState(0, 0); d3dDevice->IASetInputLayout( VertexLayout ); d3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); First we create a wide output string stream called printString. Then we input what we want to display onto the screen into that string, which includes our score and frames per second. Then we set the printScoreFPS to printString.str(). After that we do our font stuff, which creating the font color, giving it a rectangle size and position to print on (the size of the rectangle does not matter, as we set DT_NOCLIP), then calle the DrawText function to draw our stuff to the scene. wostringstream printString; printString << L"Score: " << totalScore << L"\n" << L"FPS: " << fps; printScoreFPS = printString.str(); D3DXCOLOR FontColor(1.0f, 1.0f, 1.0f, 1.0f); RECT R = {10, 10, 0, 0}; Font->DrawText(0, printScoreFPS.c_str(), -1, &R, DT_NOCLIP, FontColor); Lets move down to our messageLoop function, where we impliment our timer functions. First, we increase our frameCount, then we call the GetTime function to see if its been more than one second since the starttimer function was last called. If it has been, we set the fps to the framecount, set the framecount back to zero, and reset the start timer by calling StartTimer(). Then we set our frameTime value to whatever GetFrameTime() returns, which will be the time in seconds since it was last called. Then we can use this variable to make sure our camera moves the same speed whatever the frames per second are. You will eventually want to impliment a pause function, which would calculate the total time the game was "paused", then subtract that value from the value returned from the GetTimer() function. While the game was paused, you would also want to skip the update scene function and go straight onto the draw scene function. I have put both functions into one, but separating them would be no difficult matter. frameCount++; if(GetTimer() > 1.0f) { fps = frameCount; frameCount = 0; StartTimer(); } frameTime = GetFrameTime(); Finally, look at the techniques in our effects file. We have added in a function called SetGeometryShader(), which does that, it sets our geometry shader, like we set the vertex and pixel shaders. However, we do not have a geometry shader yet, which is why we set it to NULL. We need to do this because a call to the DrawText function will change the geometry shader, along with the states, vertex layout, and the topology. technique10 Tech { pass P0 { SetVertexShader( CompileShader( vs_4_0, VS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader( ps_4_0, PS() ) ); } } technique10 SkyMapTech { pass P0 { SetVertexShader( CompileShader( vs_4_0, SKYMAP_VS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader( ps_4_0, SKYMAP_PS() ) ); SetRasterizerState(NoCulling); SetDepthStencilState(LessEqualDSS, 0); } } Thats all we have for the timer, its not a very complicated matter. However, if you are having problems with displaying text AND seeing your scene, most likely you have forgotten to reset your states and geometry shader. Let me know if you liked this! 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> /////////////////////////new///////////////////////////////////////////////// #include <sstream> /////////////////////////new///////////////////////////////////////////////// #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 = 1920; const int Height = 1200; 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; ID3D10EffectShaderResourceVariable* fxDiffuseMapVar; ID3D10EffectMatrixVariable* fxWVPVar; D3DXMATRIX WVP; D3DXMATRIX World; D3DXMATRIX View; D3DXMATRIX Projection; ID3D10EffectVariable* fxLightVar; IDirectInputDevice8* DIKeyboard; IDirectInputDevice8* DIMouse; DIMOUSESTATE mouseLastState; LPDIRECTINPUT8 DirectInput; vector<ID3DX10Mesh*> meshes; int meshCount; bool LoadMesh(wstring filename); int meshTextures = 0; vector<ID3D10ShaderResourceView*> TextureResourceViews; vector<UINT> meshSubsets; DWORD NumVertices; DWORD NumFaces; ID3D10EffectShaderResourceVariable* fxSkyMapVar; ID3D10ShaderResourceView* smrv = 0; ID3D10EffectTechnique* SkyMapTechnique; void CreateSphere(int LatLines, int LongLines); bool isShoot = false; void pick(float mouseX, float mouseY); int numSpheres = 20; int spheres[20]; D3DXMATRIX SphereLocalSpace[20]; /////////////////////////new///////////////////////////////////////////////// ID3DX10Font* Font; wstring printScoreFPS = L""; int totalScore = 0; double countsPerSecond = 0.0; __int64 CounterStart = 0; int frameCount = 0; int fps = 0; void StartTimer(); double GetTime(); double GetFrameTime(); __int64 frameTimeOld = 0; double frameTime; /////////////////////////new///////////////////////////////////////////////// int ClientWidth = 0; int ClientHeight = 0; float moveLeftRight = 0.0f; float moveBackForward = 0.0f; D3DXVECTOR3 Position = D3DXVECTOR3( 0.0f, 1.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(); D3DXMATRIX Scale; D3DXMATRIX Translation; D3DXMATRIX Transformations; bool InitializeDirect3dApp(HINSTANCE hInstance); bool InitDirectInput(HINSTANCE hInstance); void DetectInput(); bool InitScene(); void DrawScene(); bool ReleaseObjects(); int messageloop(); LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); struct Vertex { 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){} D3DXVECTOR3 pos; D3DXVECTOR2 texCoord; D3DXVECTOR3 normal; }; D3D10_INPUT_ELEMENT_DESC layout[] = { {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0}, {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D10_INPUT_PER_VERTEX_DATA, 0} }; struct Light { Light() { ZeroMemory(this, sizeof(Light)); } D3DXVECTOR3 dir; float pad; D3DXCOLOR ambient; D3DXCOLOR diffuse; }; Light light; int WINAPI WinMain(HINSTANCE hInstance, //Main windows function HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { if(!InitializeWindow(hInstance, nShowCmd, Width, Height, false)) { 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; } 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 = false; 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 ); Position = D3DXVECTOR3( 0.0f, 4.0f, -10.0f ); Target = D3DXVECTOR3( 0.0f, 0.0f, 0.0f ); Up = D3DXVECTOR3( 0.0f, 1.0f, 0.0f ); 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_NONEXCLUSIVE | 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); D3DXVec3TransformNormal(&Right, &DefaultRight, &RotateYTempMatrix); D3DXVec3TransformNormal(&Up, &Up, &RotateYTempMatrix); D3DXVec3TransformNormal(&Forward, &DefaultForward, &RotateYTempMatrix); Position += moveLeftRight*Right; Position += moveBackForward*Forward; moveLeftRight = 0.0f; moveBackForward = 0.0f; Target = Position + Target; D3DXMatrixLookAtLH( &View, &Position, &Target, &Up ); } void pick(float mouseX, float mouseY) { //Transform 2D pick position on screen space to 3D ray in View space D3DXVECTOR3 pickRayViewSpace; pickRayViewSpace.x = ((( 2.0f * mouseX) / ClientWidth ) - 1 ) / Projection(0,0); pickRayViewSpace.y = -((( 2.0f * mouseY) / ClientHeight) - 1 ) / Projection(1,1); pickRayViewSpace.z = 1.0f; D3DXVECTOR3 pickRayViewSpacePos(0.0f, 0.0f, 0.0f); D3DXVECTOR3 pickRayViewSpaceDir(pickRayViewSpace.x, pickRayViewSpace.y, pickRayViewSpace.z); // Transform 3D Ray from View space to 3D ray in World space D3DXMATRIX pickRayWorldSpace; D3DXMatrixInverse(&pickRayWorldSpace, 0, &View); D3DXVec3TransformCoord(&pickRayViewSpacePos, &pickRayViewSpacePos, &pickRayWorldSpace); D3DXVec3TransformNormal(&pickRayViewSpaceDir, &pickRayViewSpaceDir, &pickRayWorldSpace); // Transform 3D Ray from World space to each objects/models own local space int closestObject = -1; float closestObjectDist = FLT_MAX; for(int i = 0; i < numSpheres; i++) { if(spheres[i] == 1) { D3DXMATRIX pickRayLocalSpace; D3DXMatrixInverse(&pickRayLocalSpace,NULL,&SphereLocalSpace[i]); D3DXVECTOR3 pickRayLocalSpacePos,pickRayLocalSpaceDir; D3DXVec3TransformCoord(&pickRayLocalSpacePos,&pickRayViewSpacePos,&pickRayLocalSpace); D3DXVec3TransformNormal(&pickRayLocalSpaceDir,&pickRayViewSpaceDir,&pickRayLocalSpace); D3DXVec3Normalize(&pickRayLocalSpaceDir,&pickRayLocalSpaceDir); UINT numHits = 0; float hitDistance; meshes[1]->Intersect(&pickRayLocalSpacePos, &pickRayLocalSpaceDir, &numHits, 0, 0, 0, &hitDistance, 0); //Find nearest object hit, if any if(numHits > 0) { if(hitDistance < closestObjectDist) { closestObject = i; closestObjectDist = hitDistance; } } } } if(closestObject >= 0) { spheres[closestObject] = 0; /////////////////////////new///////////////////////////////////////////////// totalScore++; /////////////////////////new///////////////////////////////////////////////// } } void DetectInput() { /////////////////////////new///////////////////////////////////////////////// float speed = 10.0f * frameTime; /////////////////////////new///////////////////////////////////////////////// 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) { SwapChain->SetFullscreenState(false, NULL); 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; } if(mouseCurrState.rgbButtons[0]) { if(isShoot == false) { POINT mousePos; GetCursorPos(&mousePos); ScreenToClient(hwnd, &mousePos); int mousex = mousePos.x; int mousey = mousePos.y; pick(mousex, mousey); isShoot = true; } } if(!mouseCurrState.rgbButtons[0]) { isShoot = false; } UpdateCamera(); return; } bool LoadMesh(wstring filename) { HRESULT hr = 0; ID3DX10Mesh* tempMesh; UINT tempMeshSubsets; wifstream fileIn (filename.c_str()); wstring skipString; UINT meshVertices = 0; UINT meshTriangles = 0; if (fileIn) { fileIn >> skipString; // #Subsets fileIn >> tempMeshSubsets; fileIn >> skipString; // #Vertices fileIn >> meshVertices; fileIn >> skipString; // #Faces (Triangles) fileIn >> meshTriangles; meshSubsets.push_back(tempMeshSubsets); hr = D3DX10CreateMesh(d3dDevice, layout, 3, layout[0].SemanticName, meshVertices, meshTriangles, D3DX10_MESH_32_BIT, &tempMesh); if(FAILED(hr)) { MessageBox(0, L"Mesh Creation - Failed", L"Error", MB_OK); return false; } fileIn >> skipString; //#Subset_info for(UINT i = 0; i < tempMeshSubsets; ++i) { std::wstring diffuseMapFilename; fileIn >> diffuseMapFilename; ID3D10ShaderResourceView* DiffuseMapResourceView; D3DX10CreateShaderResourceViewFromFile(d3dDevice, diffuseMapFilename.c_str(), 0, 0, &DiffuseMapResourceView, 0 ); TextureResourceViews.push_back(DiffuseMapResourceView); meshTextures++; } Vertex* verts = new Vertex[meshVertices]; fileIn >> skipString; //#Vertex_info for(UINT i = 0; i < meshVertices; ++i) { fileIn >> skipString; //Vertex Position fileIn >> verts[i].pos.x; fileIn >> verts[i].pos.y; fileIn >> verts[i].pos.z; fileIn >> skipString; //Vertex Normal fileIn >> verts[i].normal.x; fileIn >> verts[i].normal.y; fileIn >> verts[i].normal.z; fileIn >> skipString; //Vertex Texture Coordinates fileIn >> verts[i].texCoord.x; fileIn >> verts[i].texCoord.y; } tempMesh->SetVertexData(0, verts); delete[] verts; DWORD* indices = new DWORD[meshTriangles*3]; UINT* attributeIndex = new UINT[meshTriangles]; fileIn >> skipString; //#Face_Index for(UINT i = 0; i < meshTriangles; ++i) { fileIn >> indices[i*3+0]; fileIn >> indices[i*3+1]; fileIn >> indices[i*3+2]; fileIn >> attributeIndex[i]; //Current Subset } tempMesh->SetIndexData(indices, meshTriangles*3); tempMesh->SetAttributeData(attributeIndex); delete[] indices; delete[] attributeIndex; tempMesh->GenerateAdjacencyAndPointReps(0.001f); tempMesh->Optimize(D3DX10_MESHOPT_ATTR_SORT|D3DX10_MESHOPT_VERTEX_CACHE,0,0); tempMesh->CommitToDevice(); meshCount++; meshes.push_back(tempMesh); } else { MessageBox(0, L"Load Mesh File - Failed", L"Error", MB_OK); return false; } return true; } void CreateSphere(int LatLines, int LongLines) { NumVertices = LatLines * LongLines; NumFaces = (LatLines-1)*(LongLines-1)*2; float sphereYaw = 0.0f; float spherePitch = 0.0f; std::vector<Vertex> vertices(NumVertices); D3DXVECTOR3 currVertPos = D3DXVECTOR3(0.0f, 1.0f, 0.0f); for(DWORD i = 0; i < LatLines; ++i) { sphereYaw = i * (3.14/LatLines); for(DWORD j = 0; j < LongLines; ++j) { spherePitch = j * (3.14/LongLines); D3DXMatrixRotationYawPitchRoll( &rotationMatrix, sphereYaw, spherePitch, 0 ); D3DXVec3TransformCoord( &currVertPos, &DefaultForward, &rotationMatrix ); D3DXVec3Normalize( &currVertPos, &currVertPos ); vertices[i*LongLines+j].pos = currVertPos; } } 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 < LatLines-1; ++i) { for(DWORD j = 0; j < LongLines-1; ++j) { indices[k] = i*LongLines+j; indices[k+1] = i*LongLines+j+1; indices[k+2] = (i+1)*LongLines+j; indices[k+3] = (i+1)*LongLines+j; indices[k+4] = i*LongLines+j+1; indices[k+5] = (i+1)*LongLines+j+1; k += 6; } } 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); } /////////////////////////new///////////////////////////////////////////////// void StartTimer() { LARGE_INTEGER frequencyCount; QueryPerformanceFrequency(&frequencyCount); countsPerSecond = double(frequencyCount.QuadPart); QueryPerformanceCounter(&frequencyCount); CounterStart = frequencyCount.QuadPart; } double GetTime() { LARGE_INTEGER currentTime; QueryPerformanceCounter(¤tTime); return double(currentTime.QuadPart-CounterStart)/countsPerSecond; } double GetFrameTime() { LARGE_INTEGER currentTime; __int64 tickCount; QueryPerformanceCounter(¤tTime); tickCount = currentTime.QuadPart-frameTimeOld; frameTimeOld = currentTime.QuadPart; if(tickCount < 0.0f) tickCount = 0.0f; return float(tickCount)/countsPerSecond; } /////////////////////////new///////////////////////////////////////////////// bool InitScene() { /////////////////////////new///////////////////////////////////////////////// D3DX10_FONT_DESC fd; fd.Height = 25; fd.Width = 0; fd.Weight = 0; fd.MipLevels = 1; fd.Italic = false; fd.CharSet = OUT_DEFAULT_PRECIS; fd.Quality = DEFAULT_QUALITY; fd.PitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; wcscpy_s(fd.FaceName, L"Arial"); D3DX10CreateFontIndirect(d3dDevice, &fd, &Font); /////////////////////////new///////////////////////////////////////////////// HRESULT hr = 0; ID3D10Blob* compilationErrors = 0; LoadMesh(L"plane.dat"); //meshes[0] LoadMesh(L"sphere.dat"); //meshes[1] CreateSphere(10, 10); D3DX10_IMAGE_LOAD_INFO loadSMInfo; loadSMInfo.MiscFlags = D3D10_RESOURCE_MISC_TEXTURECUBE; ID3D10Texture2D* SMTexture = 0; hr = D3DX10CreateTextureFromFile(d3dDevice, L"skymap.dds", &loadSMInfo, 0, (ID3D10Resource**)&SMTexture, 0); if(FAILED(hr)) { MessageBox(0, L"Load cube texture - Failed", L"Error", MB_OK); return false; } D3D10_TEXTURE2D_DESC SMTextureDesc; SMTexture->GetDesc(&SMTextureDesc); D3D10_SHADER_RESOURCE_VIEW_DESC SMViewDesc; SMViewDesc.Format = SMTextureDesc.Format; SMViewDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURECUBE; SMViewDesc.TextureCube.MipLevels = SMTextureDesc.MipLevels; SMViewDesc.TextureCube.MostDetailedMip = 0; hr = d3dDevice->CreateShaderResourceView(SMTexture, &SMViewDesc, &smrv); if(FAILED(hr)) { MessageBox(0, L"Create Cube Texture RV - Failed", L"Error", MB_OK); return false; } SMTexture->Release(); light.dir = D3DXVECTOR3(0.25f, 0.5f, -1.0f); light.ambient = D3DXCOLOR(0.2f, 0.2f, 0.2f, 1.0f); light.diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); 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; } Technique = FX->GetTechniqueByName( "Tech" ); SkyMapTechnique = FX->GetTechniqueByName("SkyMapTech"); fxWVPVar = FX->GetVariableByName("WVP")->AsMatrix(); fxDiffuseMapVar = FX->GetVariableByName("DiffuseMap")->AsShaderResource(); fxLightVar = FX->GetVariableByName("light"); fxSkyMapVar = FX->GetVariableByName("SkyMap")->AsShaderResource(); D3D10_PASS_DESC PassDesc; Technique->GetPassByIndex( 0 )->GetDesc( &PassDesc ); d3dDevice->CreateInputLayout( layout, 3, PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &VertexLayout ); d3dDevice->IASetInputLayout( VertexLayout ); d3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); for( int i = 0; i < numSpheres; i++) { spheres[i] = 1; } /////////////////////////new///////////////////////////////////////////////// StartTimer(); /////////////////////////new///////////////////////////////////////////////// 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(); DIKeyboard->Unacquire(); DIMouse->Unacquire(); if( DirectInput ) DirectInput->Release(); for(int i = 0; i < meshCount; i++) if( meshes[i] ) meshes[i]->Release(); /////////////////////////new///////////////////////////////////////////////// Font->Release(); /////////////////////////new///////////////////////////////////////////////// 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, ClientWidth/ClientHeight, 1.0f, 1000.0f); /////////////////////////new///////////////////////////////////////////////// d3dDevice->OMSetDepthStencilState(0, 0); d3dDevice->IASetInputLayout( VertexLayout ); d3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); /////////////////////////new///////////////////////////////////////////////// fxLightVar->SetRawValue(&light, 0, sizeof(Light)); D3D10_TECHNIQUE_DESC skymaptechDesc; SkyMapTechnique->GetDesc( &skymaptechDesc ); D3D10_TECHNIQUE_DESC techDesc; Technique->GetDesc( &techDesc ); D3DXMatrixScaling( &Scale, 1.0f, 1.0f, 1.0f ); D3DXMatrixTranslation( &Translation, 0.0f, -3.0f, 0.0f ); Transformations = Scale * Translation; WVP = Transformations * View * Projection; fxWVPVar->SetMatrix((float*)&WVP); //draw plane for( UINT p = 0; p < techDesc.Passes; ++p ) { for(UINT subsetID = 0; subsetID < meshSubsets[0]; ++subsetID) { fxDiffuseMapVar->SetResource(TextureResourceViews[subsetID]); Technique->GetPassByIndex( p )->Apply( 0 ); meshes[0]->DrawSubset(subsetID); } } float sphereXPos = -30.0f; float sphereZPos = 30.0f; float sxadd = 0.0f; float szadd = 0.0f; for(int i = 0; i < numSpheres; i++) { sxadd++; if(sxadd == 10) { szadd -= 1.0f; sxadd = 0; } D3DXMatrixScaling( &Scale, 2.0f, 2.0f, 2.0f ); D3DXMatrixTranslation( &Translation, sphereXPos + sxadd*10.0f, 1.0f, sphereZPos + szadd*10.0f ); SphereLocalSpace[i] = Scale * Translation; WVP = SphereLocalSpace[i] * View * Projection; fxWVPVar->SetMatrix((float*)&WVP); //draw sphere if(spheres[i] == 1) { for( UINT p = 0; p < techDesc.Passes; ++p ) { for(UINT subsetID = 0; subsetID < meshSubsets[1]; ++subsetID) { fxDiffuseMapVar->SetResource(TextureResourceViews[subsetID + meshSubsets[0]]); Technique->GetPassByIndex( p )->Apply( 0 ); meshes[1]->DrawSubset(subsetID); } } } } D3DXMatrixScaling( &Scale, 100.0f, 100.0f,100.0f ); D3DXMatrixTranslation( &Translation, Position.x, Position.y, Position.z ); Transformations = Scale * Translation; WVP = Transformations * View * Projection; fxWVPVar->SetMatrix((float*)&WVP); //draw skymap based off loaded mesh sphere fxSkyMapVar->SetResource(smrv); for( UINT p = 0; p < skymaptechDesc.Passes; ++p ) { for(UINT subsetID = 0; subsetID < meshSubsets[1]; ++subsetID) { fxDiffuseMapVar->SetResource(TextureResourceViews[subsetID + meshSubsets[0]]); SkyMapTechnique->GetPassByIndex( p )->Apply( 0 ); meshes[1]->DrawSubset(subsetID); } } /////////////////////////new///////////////////////////////////////////////// wostringstream printString; printString << L"Score: " << totalScore << L"\n" << L"FPS: " << fps; printScoreFPS = printString.str(); D3DXCOLOR FontColor(1.0f, 1.0f, 1.0f, 1.0f); RECT R = {10, 10, 0, 0}; Font->DrawText(0, printScoreFPS.c_str(), -1, &R, DT_NOCLIP, FontColor); /////////////////////////new///////////////////////////////////////////////// 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 /////////////////////////new///////////////////////////////////////////////// frameCount++; if(GetTime() > 1.0f) { fps = frameCount; frameCount = 0; StartTimer(); } frameTime = GetFrameTime(); /////////////////////////new///////////////////////////////////////////////// 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; case WM_SIZE: ClientWidth = LOWORD(lParam); ClientHeight = HIWORD(lParam); 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() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader( ps_4_0, PS() ) ); } } technique10 SkyMapTech { pass P0 { SetVertexShader( CompileShader( vs_4_0, SKYMAP_VS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader( ps_4_0, SKYMAP_PS() ) ); SetRasterizerState(NoCulling); SetDepthStencilState(LessEqualDSS, 0); } }
Sign in to comment