This tutorial is part of a Collection: 02. DirectX 10 - Braynzar Soft Tutorials
rate up
0
rate down
3987
views
bookmark
23. Bounding Sphere Collision Detection

Sphere-Sphere Collision Detection: Most games wouldn't be much without collision detection. In this lesson, which builds off the last lesson, we will learn how to create a binding sphere from the binding box we created in the last one. Then we will use this binding sphere to test if the objects on the screen are colliding. If they are, we will move the object that is being collided with, depending on the angle at which they are colliding at. We will only learn how to test binding spheres for collisions in this lesson, but my next lesson will be aimed at Triangle-Triangle collision detection, which of course would be much much much more accurate for the average model than using a binding sphere.

There are no files for this tutorial
Collision detection is not usually a very simple task. Not only that, it can be very expensive. Of course, the more accurate your collision detection system is, the more expensive it is. There are many ways to save processing time of collision detection. The way we will be learning in this lesson, is called bounding spheres. Not only do bounding spheres save time in collision detection, they are also very easy to impliment. All a bounding sphere is, is a single float value. That float value represents the radius of the bounding sphere. So when checking for collisions, All we need are four values. Two values representing the positions of the two objects we are checking, and Two values representing the bounding spheres of the two objects. We will be using the bounding box from the last lesson to find the radius of the bounding sphere. Remember the bounding box found two values, a min and a max value. We can add those two values together, then divide by 2, to get the center of the object. Then we can iterate through the objects vertices and find the furthest from the center vertex. To check for a collision between two bounding spheres is very easy. We first find the distance between the two objects, and if that distance is greater than the two bounding spheres added up, they are not colliding. Of course, a bounding sphere can be quite inaccurate, especially for longer objects, such as a spaceship. Using the bounding box we learned about in the last lesson might be more accurate, and also not very difficult to impliment a collision detection system with. So anyway, a lot of games don't actually need a very accurate collision detection system, which is why bounding volumes might be all the game usually uses for a collision detection. But if a game did want to get more accurate, you would first check the bounding volumes collision detection. If the bounding volumes were colliding, then you would need to go into a more accurate collision detection, triangles to triangles. This is not a very easy model to impliment, and a little knowledge in calculus would be needed to understand exactly how to do it. I am planning on making a triangle to triangle collision detection system lesson soon. So, now we have a little idea of how we might go about checking if two objects are colliding, but theres still a problem. A scene might contain thousands of objects to test against. How are we supposed to test every object in the scene against every other object in the scene, and still keep a decent frame rate??? Well, like we said a bit ago, bounding volumes help keep collision detection from taking too long, but it might not be sufficient enough to keep the frame rate high when there are thousands of objects to test. This is where Scene Management comes in. There are many ways to manage your scene, and stop collision detection from taking too long. One way is to use grids. We will not learn how to impliment grids in this lesson, but hopefully I will make one soon. A grid separates your scene into squares, like a chessboard or something. Then when testing for objects, you might start in the top left corner square, Test the objects in that square, the square below it, the square on the bottom right of it, and the objects in the square on the right of it. After you have checked that square, you move to the next one on the right, and do the same thing. You will not need to check the objects in the squares behind it, only in front, bottom, and bottom right of it, since it was already checked by the squares behind it. A problem with this is there might be giant objects, and tiny tiny objects, so either you can have bigger squares, and all the small object in the big squares will be tested against each other multiple times, or you can have smaller squares, where the big object is tested by all the objects more than once. To solve this problem, you would use a multi resolution grid, where each object was in the grid resolution that fit it the best. This way, you would start with the highest resolution (the smaller squares), and work up to the lower resolution, (the bigger squares). As you moved to the next lower resolution, you would not need to test the higher resolutions below it (the smaller squares), since they have already checked all the lower resolution grids. There are other ways to manage your scene of course, but i wanted to give you an idea to work with if you needed. First we declare a new function which will detect a collision, if any, with the spheres object space which we will provide. Then we make a new float variable which will hold our spheres bounding radius. Then a new integer which is the object we are currently picking. bool DetectCollision(D3DXMATRIX objSpace); float spheresBoundingRadius; int pickedObj = -1; To keep less code, I have deleted the update camera function since we will not be updating the camera in this lesson. This is our new function, DetectCollision(). It has one parameter, which is the object space of the sphere we will be testing. Of course this make the function very inflexible, but it would be very easy to add another parameter for the objects radius, and a list of objects that are to be tested against. We first make a loop, which will loop through all the spheres local spaces in the array we made at the beginning. Then we make sure that the sphere does not test against itself, since it's own local space is among the array of spheres local spaces. bool DetectCollision(D3DXMATRIX objSpace) { for(int i = 0; i < numSpheres; i++) { if(objSpace != SphereLocalSpace[i]) { Now we make a new float value for the distance between the objects, and set it to 0. We also initialize two vectors, one for the object we are testing agains, and one for the current object that is doing the testing. Then we get the locationg of the object we are testing agains from the array of sphere local spaces, and get the object which we are holding onto's location, which is the location we passed in when calling the function. float distToObject = 0.0f; D3DXVECTOR3 checkObjPos = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVECTOR3 thisObjPos = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVec3TransformCoord(&checkObjPos, &checkObjPos, &SphereLocalSpace[i]); D3DXVec3TransformCoord(&thisObjPos, &thisObjPos, &objSpace); We need to find the distance between the two positions now. We can do this by subtracting the both the positions x values, then y values, and then z values. Then we do the equation to find the distance. float distX = thisObjPos.x - checkObjPos.x; float distY = thisObjPos.y - checkObjPos.y; float distZ = thisObjPos.z - checkObjPos.z; distToObject = sqrt(distX*distX + distY*distY + distZ*distZ); Now we check to see if they have collided. We can do this by adding the two spheres bounding radii together, and seeing if thats bigger than the distance between the objects. If it is, then the objects have collided. if(distToObject <= (spheresBoundingRadius + spheresBoundingRadius)) { This is where we will do something if the objects have collided. What we do here is move the object we have collided with, then check to see if the object we just moved has collided with another object, by calling the DetectCollision() function again. First we get the objects position that we want to move. The distX, distY, and distZ together are basically the vector that explains the angle the two objects have collided at. We can use this vector to "push back" the object we are colliding with, in the right direction, which is what we do, we add the negative distX and distZ, times the "speed" which we want to move at, to the objects x and z position values. Then we transform the local space matrix of the object, call DetectCollision() to see if it has collided with another object, then finally return true, and as you can see below that, if we did not collide with anything, we return false. D3DXVECTOR3 objPos = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVec3TransformCoord(&objPos, &objPos, &SphereLocalSpace[i]); objPos.x += -distX * 0.075f; objPos.z += -distZ * 0.075f; D3DXMatrixScaling( &Scale, 2.0f, 2.0f, 2.0f ); D3DXMatrixTranslation( &Translation, objPos.x, 1.0f, objPos.z ); SphereLocalSpace[i] = Scale * Translation; DetectCollision(SphereLocalSpace[i]); return true; } } } return false; } I'm sure if you look at this, you can see whats going on, but i'll give it a quick go-over. Here we check to see if the mouse has moved, if it has (we still have the values to update our camera, which we don't need in this lesson, but I see i've left them by mistake anyway, oh well), we check to see if an object has been picked by checking if pickedObj is not -1. If we have an object picked, we are going to move the object based on how the mouse moves, then detect if there was a collision by calling DetectCollision(). Below that, we check to see if the mouse left mouse button was pressed. If it is (again more camera stuff), we check to first see if an object was picked. If an object was picked, we set the picked object (pickedObj was set in the pick function), then we check if pickedObj is set. If it is, we make our cursor dissapear (so it looks like we have better control over the object, since the cursor does not float over the object), and set the position of our cursor to the center of our client area. We do that so when we let go of the left mouse button, its not hiding in the corner of the screen or something. HOWEVER, you may notice that the mouse cursor sometimes takes forever to reappear. I'm sure this is because after you have touched so many object together, it takes a while to process all the collisions, and the mouse will appear after the processing is done. After that we check to make sure the left mouse button is not down. If it is now down, we show the cursor again, and set pickedObj to -1, so the program knows we are not currently picking an object. if((mouseCurrState.lX != mouseLastState.lX) || (mouseCurrState.lY != mouseLastState.lY)) { yaw += mouseLastState.lX * 0.001f; pitch += mouseCurrState.lY * 0.001f; mouseLastState = mouseCurrState; if(pickedObj != -1) { D3DXVECTOR3 objPos = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVec3TransformCoord(&objPos, &objPos, &SphereLocalSpace[pickedObj]); objPos.x += mouseLastState.lX * 0.075f; objPos.z += -mouseLastState.lY * 0.075f; D3DXMatrixScaling( &Scale, 2.0f, 2.0f, 2.0f ); D3DXMatrixTranslation( &Translation, objPos.x, 1.0f, objPos.z ); SphereLocalSpace[pickedObj] = Scale * Translation; DetectCollision(SphereLocalSpace[pickedObj]); } } 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(pickedObj != -1) { ShowCursor(false); SetCursor(NULL); SetCursorPos(ClientWidth/2.0f, ClientHeight/2.0f); } } if(!mouseCurrState.rgbButtons[0]) { isShoot = false; pickedObj = -1; ShowCursor(true); } Now when we create our binding box, we are going to create a binding sphere, which we will use the minVertex and maxVertex positions to do so. First we find the distance between them, and then cut that distance in half to find the radius of our bounding sphere. We set that to spheresBoundingRadius (since all spheres in our scene are the same size). Remember though, whenever you change the size of an object, you must change the size of the radius by the LARGEST x, y or z number you have changed your object by. float distX = maxVertex.x - minVertex.x; float distY = maxVertex.y - minVertex.y; float distZ = maxVertex.z - minVertex.z; spheresBoundingRadius = (sqrt(distX*distX + distY*distY + distZ*distZ))/2.0f; This is at the bottom of our initScene function. We have moved it from the draw scene function since we only need to set the spheres in the scene in order once, because the spheres positions might change when we collide with them or move them around. float sphereXPos = -40.0f; float sphereZPos = 40.0f; float sxadd = 0.0f; float szadd = 0.0f; for(int i = 0; i < numSpheres; i++) { if(sxadd == 9) { 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; sxadd++; } Like I said, bounding sphere collisions are very easy to impliment. I hope you have found this lesson helpfull, like I always say ;) Also, I hope to finish a triangle to triangle collision detection system lesson soon. Please let me know what you think of the lessons! 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> #include <sstream> #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 = 800; const int Height = 600; 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 = 81; int spheres[81]; D3DXMATRIX SphereLocalSpace[81]; ID3DX10Mesh* sphereBoundingBox; ID3D10ShaderResourceView* BoundingBoxTexture; bool showBoundingVolumes = true; bool spacePress = false; int bbShots; void CreateSpheresBoundingBox(); /////////////////////////new///////////////////////////////////////////////// bool DetectCollision(D3DXMATRIX objSpace); float spheresBoundingRadius; int pickedObj = -1; /////////////////////////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; 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, 70.0f, -60.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 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; //Test the bounding box for an intersection with the ray sphereBoundingBox->Intersect(&pickRayLocalSpacePos, &pickRayLocalSpaceDir, &numHits, 0, 0, 0, &hitDistance, 0); if(numHits > 0) { bbShots++; //Keep track of how many times we shot the bounding box //If the ray hit the bounding box, text actual mesh for an intersection 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) { pickedObj = closestObject; totalScore++; } } /////////////////////////new///////////////////////////////////////////////// bool DetectCollision(D3DXMATRIX objSpace) { for(int i = 0; i < numSpheres; i++) { if(objSpace != SphereLocalSpace[i]) { float distToObject = 0.0f; D3DXVECTOR3 checkObjPos = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVECTOR3 thisObjPos = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVec3TransformCoord(&checkObjPos, &checkObjPos, &SphereLocalSpace[i]); D3DXVec3TransformCoord(&thisObjPos, &thisObjPos, &objSpace); float distX = thisObjPos.x - checkObjPos.x; float distY = thisObjPos.y - checkObjPos.y; float distZ = thisObjPos.z - checkObjPos.z; distToObject = sqrt(distX*distX + distY*distY + distZ*distZ); if(distToObject <= (spheresBoundingRadius + spheresBoundingRadius)) { D3DXVECTOR3 objPos = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVec3TransformCoord(&objPos, &objPos, &SphereLocalSpace[i]); objPos.x += -distX * 0.075f; objPos.z += -distZ * 0.075f; D3DXMatrixScaling( &Scale, 2.0f, 2.0f, 2.0f ); D3DXMatrixTranslation( &Translation, objPos.x, 1.0f, objPos.z ); SphereLocalSpace[i] = Scale * Translation; DetectCollision(SphereLocalSpace[i]); return true; } } } return false; } /////////////////////////new///////////////////////////////////////////////// void DetectInput() { float speed = 10.0f * frameTime; 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_SPACE] & 0x80) { if(spacePress == false) { showBoundingVolumes = !showBoundingVolumes; spacePress = true; } } else //If space is not currently being pressed { spacePress = false; } 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; } /////////////////////////new///////////////////////////////////////////////// if((mouseCurrState.lX != mouseLastState.lX) || (mouseCurrState.lY != mouseLastState.lY)) { yaw += mouseLastState.lX * 0.001f; pitch += mouseCurrState.lY * 0.001f; mouseLastState = mouseCurrState; if(pickedObj != -1) { D3DXVECTOR3 objPos = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVec3TransformCoord(&objPos, &objPos, &SphereLocalSpace[pickedObj]); objPos.x += mouseLastState.lX * 0.075f; objPos.z += -mouseLastState.lY * 0.075f; D3DXMatrixScaling( &Scale, 2.0f, 2.0f, 2.0f ); D3DXMatrixTranslation( &Translation, objPos.x, 1.0f, objPos.z ); SphereLocalSpace[pickedObj] = Scale * Translation; DetectCollision(SphereLocalSpace[pickedObj]); } } 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(pickedObj != -1) { ShowCursor(false); SetCursor(NULL); SetCursorPos(ClientWidth/2.0f, ClientHeight/2.0f); } } if(!mouseCurrState.rgbButtons[0]) { isShoot = false; pickedObj = -1; ShowCursor(true); } /////////////////////////new///////////////////////////////////////////////// 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); } 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 CreateSpheresBoundingBox() { ID3DX10MeshBuffer* VertexBuffer; meshes[1]->GetVertexBuffer(0, &VertexBuffer); Vertex* vertices = 0; SIZE_T size; VertexBuffer->Map((void**)&vertices, &size); D3DXVECTOR3 minVertex = D3DXVECTOR3(FLT_MAX, FLT_MAX, FLT_MAX); D3DXVECTOR3 maxVertex = D3DXVECTOR3(-FLT_MAX, -FLT_MAX, -FLT_MAX); UINT meshVertexCount = meshes[1]->GetVertexCount(); for(UINT i = 0; i < meshVertexCount; i++) { D3DXVec3Minimize(&minVertex, &minVertex, &vertices[i].pos); D3DXVec3Maximize(&maxVertex, &maxVertex, &vertices[i].pos); } /////////////////////////new///////////////////////////////////////////////// float distX = maxVertex.x - minVertex.x; float distY = maxVertex.y - minVertex.y; float distZ = maxVertex.z - minVertex.z; spheresBoundingRadius = (sqrt(distX*distX + distY*distY + distZ*distZ))/2.0f; /////////////////////////new///////////////////////////////////////////////// VertexBuffer->Unmap(); VertexBuffer->Release(); D3DX10CreateMesh(d3dDevice, layout, 3, layout[0].SemanticName, 8, 12, D3DX10_MESH_32_BIT, &sphereBoundingBox); D3DX10CreateShaderResourceViewFromFile(d3dDevice, L"Wire_Fence.png", 0, 0, &BoundingBoxTexture, 0 ); Vertex* v = new Vertex[8]; // Front Vertices v[0] = Vertex(minVertex.x, minVertex.y, minVertex.z, 0.0f, 1.0f,-1.0f, -1.0f, -1.0f); v[1] = Vertex(minVertex.x, maxVertex.y, minVertex.z, 0.0f, 0.0f,-1.0f, 1.0f, -1.0f); v[2] = Vertex(maxVertex.x, maxVertex.y, minVertex.z, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f); v[3] = Vertex(maxVertex.x, minVertex.y, minVertex.z, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f); // Back Vertices v[4] = Vertex(minVertex.x, minVertex.y, maxVertex.z, 1.0f, 1.0f,-1.0f, -1.0f, 1.0f); v[5] = Vertex(maxVertex.x, minVertex.y, maxVertex.z, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f); v[6] = Vertex(maxVertex.x, maxVertex.y, maxVertex.z, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f); v[7] = Vertex(minVertex.x, maxVertex.y, maxVertex.z, 1.0f, 0.0f,-1.0f, 1.0f, 1.0f); sphereBoundingBox->SetVertexData(0, v); DWORD* i = new DWORD[36]; // Front Face i[0] = 0; i[1] = 1; i[2] = 2; i[3] = 0; i[4] = 2; i[5] = 3; // Back Face i[6] = 4; i[7] = 5; i[8] = 6; i[9] = 4; i[10] = 6; i[11] = 7; // Top Face i[12] = 1; i[13] = 7; i[14] = 6; i[15] = 1; i[16] = 6; i[17] = 2; // Bottom Face i[18] = 0; i[19] = 4; i[20] = 5; i[21] = 0; i[22] = 5; i[23] = 3; // Left Face i[24] = 4; i[25] = 7; i[26] = 1; i[27] = 4; i[28] = 1; i[29] = 0; // Right Face i[30] = 3; i[31] = 2; i[32] = 6; i[33] = 3; i[34] = 6; i[35] = 5; sphereBoundingBox->SetIndexData(i, 36); sphereBoundingBox->CommitToDevice(); } bool InitScene() { 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); HRESULT hr = 0; ID3D10Blob* compilationErrors = 0; LoadMesh(L"plane.dat"); //meshes[0] LoadMesh(L"sphere.dat"); //meshes[1] CreateSphere(10, 10); CreateSpheresBoundingBox(); 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; } StartTimer(); /////////////////////////new///////////////////////////////////////////////// float sphereXPos = -40.0f; float sphereZPos = 40.0f; float sxadd = 0.0f; float szadd = 0.0f; for(int i = 0; i < numSpheres; i++) { if(sxadd == 9) { 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; sxadd++; } /////////////////////////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(); if( sphereBoundingBox ) sphereBoundingBox->Release(); if( BoundingBoxTexture ) BoundingBoxTexture->Release(); Font->Release(); return true; } void DrawScene() { //Draw Scene Here D3DXCOLOR bgColor( 0.0f, 0.0f, 0.0f, 1.0f); d3dDevice->ClearRenderTargetView( RenderTargetView, bgColor ); d3dDevice->ClearDepthStencilView(DepthStencilView, D3D10_CLEAR_DEPTH|D3D10_CLEAR_STENCIL, 1.0f, 0); D3DXMatrixPerspectiveFovLH(&Projection, 0.4f*3.14f, ClientWidth/ClientHeight, 1.0f, 1000.0f); d3dDevice->OMSetDepthStencilState(0, 0); d3dDevice->IASetInputLayout( VertexLayout ); d3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); 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++) { 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); } } if(showBoundingVolumes) { for( UINT p = 0; p < techDesc.Passes; ++p ) { fxDiffuseMapVar->SetResource(BoundingBoxTexture); Technique->GetPassByIndex( p )->Apply( 0 ); sphereBoundingBox->DrawSubset(0); } } } } 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); } } wostringstream printString; printString << L"Score: " << totalScore << L"\n" << L"FPS: " << fps << L"\n" << L"Shots through Bounding Box: " << bbShots << L"\n" << pickedObj << L"\n"; 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); 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(); 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 ); clip (diffuse.a - .25); 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); } }