This tutorial is part of a Collection: 02. DirectX 10 - Braynzar Soft Tutorials
rate up
0
rate down
1269
views
bookmark
22. Bounding Volumes (Box)

As games can have quite complex and detailed models, containing thousands of triangles, checking every triangle in the entire scene for an intersection EVERY frame would slow the game down considerably. In this lesson we will learn a how to impliment a technique to cut down the amount of triangles we test every frame for an intersection by creating a bounding box for each of our models. That way, we only test the few triangles in each models bounding box for an intersection, and if there was an intersection, we can test the actual model for an intersection. This will be a huge gain in performance later when you have thousands of models to test for intersections.

There are no files for this tutorial
As we said before, scenes becoming more complex usually means more triangles to test against when checking for collision or picking. So, we will learn here how to minimize the amount of triangles that need to be tested each frame to keep our fps higher. The method we will learn here is something called "Bounding Volumes". A bounding volume some sort of volume, usually a cube or sphere, which tightly surrounds a mesh. Then when we check for collisions or picking, we first check the bounding volumes of the objects, and if the bounding volume has been intersected, then we can move on to check the actual object the bounding volume is bound to. Here we will be implimenting a bounding box, although a bounding sphere may be a tad more efficient, I chose a bounding box so we can actually see it. To compute a bounding sphere is not that difficult, in fact, all it really is, is a radius with a position, usually the center of the object. To find it, we would iterate through all the vertices in the object, and set the radius (which is our bounding sphere) to the distance of the vertex furthest from the center of the object. Here, like I said, we will be creating a bounding box. To find the bounding box, we will need to create two points, the minimum and the maximum, which we will use to find the size of our bounding box, to make sure it tightly surrounds our object (sphere in this case). To find the minimum and maximum points, we will iterate through each vertex in the object. We will find the maximum and minimum +X, -X, +Y, -Y, +Z, and -Z points, and set the distance to the most positive x y and z points to the maximum point, and the negative x y and z points to the minimum point. To better picture it, lets say we are making a bounding square (2D to make it simpler), and pretend we have four points: P1(-1, 0) P2(+1, 0) P3(0, -1) P4(0, +1) We will iterate through each point, and set the maximum and minimum points to the maximum and minimum distances, to make sure we surroun the all points. First we test P1(-1, 0) - Since this is the first one, it will set the Min point to (-1,0) and the Max point to (-1, 0) Next is P2(+1, 0) - Since The X value of Min is smaller, Min will stay (-1, 0), but since the X value of Max is smaller, it will set Max to (+1, 0) Next, P3(0, -1) - The X value of the min and max are both higher and lower, so it will not set the X value of either, but will set the Y value of Min to -1, making Min (-1, -1), and Max (+1, 0) Finally, P4(0, +1) - Min will stay the same, since both values of Min are already smaller than the values of P4, but Max will change to (+1, +1) So the final result is: Min(-1, -1) and Max(+1, +1) Now we need to use the Min and Max points to create a square that surrounds our points. We can create each vertex like this: V1(Min.x, Min.y) V2(Min.x, Max.y) V3(Max.x, Max.y) V4(Max.x, Min.y) Here is a visual representation: +[http://www.braynzarsoft.net/image/100162][bounding box example] We initialize a couple new things we will be using in this lesson. The first is a ID3DX10Mesh object we will store our bounding cube into. The next is the texture we will give our bounding cube (generally bounding volumes do not get drawn to the screen, so you would not normally need a texture for one, in fact, all you would really need are the positions of the vertices. Next is a boolean variable we will use to draw or not to draw our bounding volumes when we press space. The next one is another boolean variable we will use to make sure we do not keep turning on and off the bounding boxes when we hold down the spacebar. Next is an integer to hold the amount of times we have clicked on a bounding box, and the last one is our new function, which will create a bounding box for our sphere. ID3DX10Mesh* sphereBoundingBox; ID3D10ShaderResourceView* BoundingBoxTexture; bool showBoundingVolumes = true; bool spacePress = false; int bbShots; void CreateSpheresBoundingBox(); This is where we test objects to see if we have "picked" them. First we will test our bounding box, then if that was picked, we increase our bounding box picks, which will be displayed on the screen, and then test our actual mesh if it has been picked. When using bounding volumes, you must make sure it uses the same "space" as the model it is bound to. That means the same translation, scale, and rotation. If you rotate the mesh, you must rotate the bounding volume bound to it, and same for translating and scaling. When using a bounding sphere, you don't have to worry about rotation, but you do neet to make sure it is in the same position, and also when the model scales, you need to scale the "radius" of the sphere by the highest scale value when scaling the object. Here we use the same "space" when checking the bounding box and the object for intersections with the picking 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); } This is simple enough, we check for the spacebar being pressed, if it is, and spacePress is false, then we change the showBoundingVolumes to the oposite of what it was, then set spacePress to true. When the spacebar is not being pressed, we are setting spacePress to false. if(keyboardState[DIK_SPACE] & 0x80) { if(spacePress == false) { showBoundingVolumes = !showBoundingVolumes; spacePress = true; } } else //If space is not currently being pressed { spacePress = false; } Here we create our bounding box for the sphere function. First we create a temporary vertex buffer to store the vertex buffer from the mesh[1] (our sphere object) by calling ID3DX10Mesh::GetVertexBuffer(). After that we create a Vertex array, then a SIZE_T variable which we will use to store the size of the vertices in the vertex buffer. Then we call the Map method of our Vertex buffer to get all the vertices and store them into the vertices variable. Now we can iterate through them to find the two min and max points which we will use to create our box. Before that, we need to set our min and max points to FLT_MAX and -FLT_MAX to make sure that no vertex is left out when checking them, in case the model is centered around a very different point. Then we get the amount of vertices in our mesh by calling ID3DX10Mesh::GetVertexCount(), and storing it into a UINT variable. Then we iterate through each vertex, to find the furthest out x, y, z, -x, -y, and -z distances. After that, we can unmap our vertex buffer, then release it, as it is no longer needed. void CreateBoundingBox() { 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); } VertexBuffer->Unmap(); VertexBuffer->Release(); Next we create a new mesh, and store it into the sphereBoundingBox object we initialized at the beginning. We already know what all this does. Then we need to load the texture for our bounding box. Remember, you wouldn't "normally" have to load a texture for a bounding volume, or anything other than vertex positions and an index list, since you woudn't "normally" be drawing a bounding volume onto the screen. We know how to load a texture, so I won't explain that. Then we need to create a vertex array to hold the vertices of our bounding box. We only need 8 vertices, one for each corner of the cube. Now, look at how we set the positions of each vertex. We are using the x, y and z values of the minVertex and maxVertex variables. You can probably see what we are doing. After that we store the vertex array into our mesh object by calling the SetVertexData method of the mesh interface. After that we create our index list, then store that into our mesh too. Then we commit our mesh to the device so we can use it. That sums up creating our bounding box. 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(); } Now we call the function from our initScene function. CreateBoundingBox(); Don't forget to release interface objects. if( sphereBoundingBox ) sphereBoundingBox->Release(); if( BoundingBoxTexture ) BoundingBoxTexture->Release(); And finally we come to the part to draw our bounding box. Of course we wouldn't normally do this, but just to make things more interesting we will. You can probably tell whats going on here. 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); } } } After all that, We're done with the lesson! Now you can use these bounding boxes to test for collisions and picking! We'll have another lesson on collision detection soon, so stay tooned! 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 = 20; int spheres[20]; D3DXMATRIX SphereLocalSpace[20]; /////////////////////////new///////////////////////////////////////////////// ID3DX10Mesh* sphereBoundingBox; ID3D10ShaderResourceView* BoundingBoxTexture; bool showBoundingVolumes = true; bool spacePress = false; int bbShots; void CreateSpheresBoundingBox(); /////////////////////////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, 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; /////////////////////////new///////////////////////////////////////////////// //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); } /////////////////////////new///////////////////////////////////////////////// //Find nearest object hit, if any if(numHits > 0) { if(hitDistance < closestObjectDist) { closestObject = i; closestObjectDist = hitDistance; } } } } if(closestObject >= 0) { spheres[closestObject] = 0; totalScore++; } } 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); } /////////////////////////new///////////////////////////////////////////////// if(keyboardState[DIK_SPACE] & 0x80) { if(spacePress == false) { showBoundingVolumes = !showBoundingVolumes; spacePress = true; } } else //If space is not currently being pressed { spacePress = false; } /////////////////////////new///////////////////////////////////////////////// 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); } 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; } /////////////////////////new///////////////////////////////////////////////// void CreateBoundingBox() { 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); } 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(); } /////////////////////////new///////////////////////////////////////////////// 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); /////////////////////////new///////////////////////////////////////////////// CreateBoundingBox(); /////////////////////////new///////////////////////////////////////////////// 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(); 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///////////////////////////////////////////////// if( sphereBoundingBox ) sphereBoundingBox->Release(); if( BoundingBoxTexture ) BoundingBoxTexture->Release(); /////////////////////////new///////////////////////////////////////////////// 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++) { 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 /////////////////////////new///////////////////////////////////////////////// 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); } } } /////////////////////////new///////////////////////////////////////////////// } 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; 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); } }