This lesson builds directly off the last lesson, Bounding Sphere Collision Detection. In this lesson, we will learn how to impliment a very accurate collision detection model, Triangle to Triangle. Although the idea is pretty simple, the math can get a little complex, so you might want to brush up on your math skills. I will try to explain as best as I can though. The only new things in this lesson are two new functions. One function for detecting a triangle to triangle collision (which calls the second function to complete the detection) and another function to test whether a point is inside a triangle. I did this so you could easily put these function into your own project, and/or use the point in triangle function to test for accurate picking, when picking a 3d model.
CD == Collision Detection Unlike Bounding volume CD, which is a somewhat poor CD approximation, Triangle to Triangle CD is a method to find very accurate collisions between two 3D models, down to the triangle. A lot of times, bounding volume CD is all you might really need, and since triangle to triangle CD is very expensive, you should use it as seldom as possible. Now, as I mentioned in the last lesson, a scene might contain thousands of 3d models, where even testing bounding volume collisions can be slow, and on top of that, each object might contain thousands or even more triangles, so testing every triangle with every other triangle in the scene for a collision is not usually an option, as a single triangle might have to test against a million other triangle, and each of those have to test against each other. So to save processing time, you can follow a certain sequence or order when detecting for collisions. First, you might want to divide your scene into squares (using a "grid"). Then when testing for collisions with a certain object, you only test the objects in the surrounding squares of the square that certain object occupies. After you have found the objects you want to test for a collision against, you would want to first test their bounding volumes to see if they are even close enough for an actual collision. Each model might have a bunch of different subsets, or sections (eg. arm, head, leg, torso), and each of those sections might have a bounding volume you would want to test against. And finally, if the bounding volume CD has passed, and if you must, you can use a more accurate CD model, such as what we will be learning in this lesson, Triangle to Triangle. To check for a collision between two triangles, a couple steps must be taken. Of course, there are many different ways to do the same thing, but these are the steps we will take in this lesson: -Find the plane equation in which the first triangle lies in (Ax + By + Cz + D = 0) -Make a line from the second triangle using two of its vertices -Find where on the line intersects with triangle one's plane -Find where on the first triangle's plane the line intersects -Check if the intersecting point on the first triangles plane is inside the triangle Now I will cover these steps in more detail. First, how do we find the plane which the first triangle lies in? Well first we get the face normal of the triangle. A lot of times you won't need to do this in code if the model you are testing already has face normals. However, We can compute the face normal using the triangles three vertices, V1, V2, V3, and put them into an equation like this: face normal = (V2 - V1) x (V3 - V1) where "x" is a cross multiplication operator. We do this in two steps in the code using the variables U and V, where U is (V2 - V1) and V is (V3 - V1), then we cross multiply U and V and store it into a variable called faceNormal. Now we have our A, B, and C values for our face equation "Ax + By + Cz + D = 0", but we still need x, y, and z of the equation (D is computed last using some simple algebra). x, y, and z are just a point in the triangle. The easiest way to find x, y, and z is to just find the point in the center of the triangle. We can find this by taking the average of all three vertices: triPoint = (V1 + V2 + V3) / 3 So now our plane equation looks like this: (faceNormal.x * triPoint.x) + (faceNormal.y * triPoint.y) + (faceNormal.z * triPoint.z) + D = 0 To find D is very easy, all we have to do is subtract D from both sides. But then the equation would equal -D (negative D). To fix this, we just need to multiply both sides by -1, So here is the equation to find D: -((faceNormal.x * triPoint.x) - (faceNormal.y * triPoint.y) - (faceNormal.z * triPoint.z)) = D Then we store A, B, C, and D so we can use them in a bit to find if triangle two intersects with triangle one's plane. To do this, we will make a line from one of the second triangles edges, created with two of the triangles vertices, and find where the line intersects with the first triangles plane. We take the two vertices which make up the line, V1(x1, y1, z1) and V2(x2, y2, z2), and put them into the equation V1(x1, y1, z1) * t == V2(x2, y2, z2) * (1 - t) Where "t" is a value between 0 and 1 representing where the line crosses the plane. If t = 0, the line crosses at V2, and if t = 1, the line crosses at V1. We know the line crosses between the two points V1 and V2 if "t" is between 0 and 1. +[http://www.braynzarsoft.net/image/100165][triangle collision detection] We can find "t" by plugging these into the plane equation: A( (x1*t) + (x2*(1-t)) ) + B( (y1*t) + (y2*(1-t)) ) + C( (z1*t) + (z2*(1-t)) ) + D = 0 I know, it can look awfully confusing, but we'll live. Now to find "t": t = -(A*x2 + B*y2 + C*z2 + D) / (A*(x1-x2) + B*(y1-y2) + C*(z1-z2)) Now we know "t". If "t" is not between 0 and 1, we check the remaining two edges. If none of the edges intersect with the plane (t is not between 0 and 1), we know there was no collision. Otherwise we can move on to find if the edges intersection with the plane was inside the first triangle. Before we do this though, we need to find the actual point of intersection with the first triangles plane. If the line we created used V1 and V2 of the second triangle, we can find the x, y, and z values of the point in the plane like this: planeIntersectX = (V1.x * t) + (V2.x * (1 - t)) planeIntersectY = (V1.y * t) + (V2.y * (1 - t)) planeIntersectZ = (V1.z * t) + (V2.z * (1 - t)) Then the point in the plane would be (planeIntersectX, planeIntersectY, planeIntersectZ). Now we check if this point is inside the triangle. One way to do this, is to create three new triangles using the point of intersection, and the first triangles three vertices. We can then add the areas of the three new triangles together, and if the combined areas of the three triangles is equal to the first triangles area, we know the point is inside the triangle. So all thats left to do now is find the areas. +[http://www.braynzarsoft.net/image/100166][triangle collision detection] To find the area of a triangle, we will first find the lenth of its three sides. To find the lenth of one of its sides, we find the distance between two of its vertices, say V1 and V2. We can do it like this: distX = V1.x - V2.x distY = V1.y - V2.y distZ = V1.z - V2.z edgeLength1 = sqrt(distX*distX + distY*distY + distZ*distZ); This will find the length of an edge. We do this for the three edges of a triangle. After finding the length of each edge, we calculate the factor "s" which we will use in the equation to find the area: s = (edgeLength1 + edgeLength2 + edgeLength3)/2.0f Now put it all together to find the area of the triangle: triArea = sqrt(s*(s-edgeLength1)*(s-edgeLength2)*(s-edgeLength3)) Like I said, we find the area of the first triangle, Then find the areas of the three triangles created with the point in the plane and the first triangles three vertices. We then add those three triangles areas together and compare it to the first triangles area, if they are equal, we know the point is inside the triangle! Another thing I should mention, is it might happen where the tip of the triangle you are testing AGAINST (the triangle whose plane you need to find) intersects with the triangle you are testing WITH (the triangle you find the lines and then find t), but it doesn't get detected because the lines of the triangle you are testing WITH do not intersect inside the triangle you are testing AGAINST. Look below if that seems confusing. +[http://www.braynzarsoft.net/image/100167][triangle collision detection] This is usually not much of a problem though, because most of the objects are completely closed, and if one triangle did not detect it, another probably will detect a collision. Not only that, but when it comes the other objects turn to test for collisions, it will find it. BUT, if you do need to make sure this does not go undetected, you can detect it by swapping the two triangles "part" in the collision detecting method. So the triangle you are testing AGAINST becomes the triangle you are testing WITH, and the triangle you are testing WITH becomes the triangle you are testing AGAINST. This is TERRIBLY inefficient, as it will now take TWICE as long to detect collisions, but it would make your collision detecting that much more accurate. You shouldn't have to do that, just thought I would mention it if the thought came across your mind. Anyway, now to the code... Here we declare our two functions used in our accurate triangle to triangle collision testing. Like I said earlier, The only new code in this lesson are these two new functions. One will test for the triangle to triangle collision, calling the second one to complete the function, and the second one will find out if a point in a plane is inside a triangle. The PointInTriangle could be used for picking. bool PointInTriangle(D3DXVECTOR3 triV1, D3DXVECTOR3 triV2, D3DXVECTOR3 triV3, D3DXVECTOR3 point ); bool TriTriCollisionDetect(ID3DX10Mesh* mesh1, D3DXMATRIX mesh1ObjSpace, ID3DX10Mesh* mesh2, D3DXMATRIX mesh2ObjSpace); Ok, here is our first function, the TriTriCollisionDetect() function. It takes in four arguments, one for the mesh to test against, and its local space, and one for the mesh to test with, and its local space. bool TriTriCollisionDetect(ID3DX10Mesh* mesh1, D3DXMATRIX mesh1ObjSpace, ID3DX10Mesh* mesh2, D3DXMATRIX mesh2ObjSpace) { Before we do any testing, we will need to gather the information about each of the meshes. We take the number of faces in the mesh, used for the loop to make sure we iterate through each and every triangle, or until a collision was detected. Then we can get the vertices and indices of the mesh. We can do this by calling GetVertexBuffer and GetIndexBuffer, and storing them into an ID3DX10MeshBuffer interface. We can then take out the actual data from the mesh buffer and give it to the right variable or array by calling the Map function. After that, we need to unmap the buffers then release them. Now we have the vertices and index list of the meshes. int numMesh1Faces = mesh1->GetFaceCount(); ID3DX10MeshBuffer* mesh1VertexBuffer; mesh1->GetVertexBuffer(0, &mesh1VertexBuffer); Vertex* mesh1Vertices = 0; mesh1VertexBuffer->Map((void**)&mesh1Vertices, 0); ID3DX10MeshBuffer* mesh1IndexBuffer; mesh1->GetIndexBuffer(&mesh1IndexBuffer); DWORD* mesh1Indices = 0; mesh1IndexBuffer->Map((void**)&mesh1Indices, 0); mesh1VertexBuffer->Unmap(); mesh1VertexBuffer->Release(); mesh1IndexBuffer->Unmap(); mesh1IndexBuffer->Release(); int numMesh2Faces = mesh2->GetFaceCount(); ID3DX10MeshBuffer* mesh2VertexBuffer; mesh2->GetVertexBuffer(0, &mesh2VertexBuffer); Vertex* mesh2Vertices = 0; mesh2VertexBuffer->Map((void**)&mesh2Vertices,0); ID3DX10MeshBuffer* mesh2IndexBuffer; mesh2->GetIndexBuffer(&mesh2IndexBuffer); DWORD* mesh2Indices = 0; mesh2IndexBuffer->Map((void**)&mesh2Indices, 0); mesh2VertexBuffer->Unmap(); mesh2VertexBuffer->Release(); mesh2IndexBuffer->Unmap(); mesh2IndexBuffer->Release(); Now we enter the loop for the first meshes triangles. The first thing we do is initialize three vertices which will hold the three vertices of the current meshes triangle we are testing. Then we set them to the appropriate vertex in the current triangle. Now the vertices the mesh has provided us are in the meshes model space, we need to transform these vertices into the meshes world space. for(int i = 0; i < numMesh1Faces; i++) { D3DXVECTOR3 tri1V1 = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVECTOR3 tri1V2 = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVECTOR3 tri1V3 = D3DXVECTOR3(0.0f, 0.0f, 0.0f); tri1V1 = mesh1Vertices[mesh1Indices[(i*3)+0]].pos; tri1V2 = mesh1Vertices[mesh1Indices[(i*3)+1]].pos; tri1V3 = mesh1Vertices[mesh1Indices[(i*3)+2]].pos; D3DXVec3TransformCoord(&tri1V1, &tri1V1, &mesh1ObjSpace); D3DXVec3TransformCoord(&tri1V2, &tri1V2, &mesh1ObjSpace); D3DXVec3TransformCoord(&tri1V3, &tri1V3, &mesh1ObjSpace); Here we create two vectors, U and V, which will be used to calculate the face normal, which will be stored in faceNormal. We then set U and V, then cross multiply them, storing the result in faceNormal. Then we normalize the faceNormal vector. D3DXVECTOR3 U = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVECTOR3 V = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVECTOR3 faceNormal = D3DXVECTOR3(0.0f, 0.0f, 0.0f); U = tri1V2 - tri1V1; V = tri1V3 - tri1V1; D3DXVec3Cross(&faceNormal, &U, &V); D3DXVec3Normalize(&faceNormal, &faceNormal); Now we need to find a point inside the triangle to use for the plane equation. We will just find the average of the three vertices which make up the triangle. Then we set the A, B, C, and D values of the plane equation "Ax + By + Cz + D = 0". D3DXVECTOR3 triPoint = (tri1V1 + tri1V2 + tri1V3)/3; float tri1A = faceNormal.x; float tri1B = faceNormal.y; float tri1C = faceNormal.z; float tri1D = (-tri1A*triPoint.x - tri1B*triPoint.y - tri1C*triPoint.z); Next we loop through each of the second meshes triangles. We start by doing the same thing we did for the first meshes triangle. We get the triangles vertices, then transform them into world space. for(int j = 0; j < numMesh2Faces; j++) { //Triangle's vertices V1, V2, V3 D3DXVECTOR3 tri2V1 = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVECTOR3 tri2V2 = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVECTOR3 tri2V3 = D3DXVECTOR3(0.0f, 0.0f, 0.0f); //Get mesh2's triangle tri2V1 = mesh2Vertices[mesh2Indices[(j*3)+0]].pos; tri2V2 = mesh2Vertices[mesh2Indices[(j*3)+1]].pos; tri2V3 = mesh2Vertices[mesh2Indices[(j*3)+2]].pos; //Transform triangles vertice's to second objects world space D3DXVec3TransformCoord(&tri2V1, &tri2V1, &mesh2ObjSpace); D3DXVec3TransformCoord(&tri2V2, &tri2V2, &mesh2ObjSpace); D3DXVec3TransformCoord(&tri2V3, &tri2V3, &mesh2ObjSpace); Next we will find the "t" value. We initialize six floats, ep1, ep2, which are the two parts of the equation we will use to find "t", then the three values, x, y, and z which will define the lines point of intersection with the plane. You can see we have an array of 4 vertices. We can use this array so we can easily loop through the 3 sides of the triangle, keeping the code more condensed. the fourth value of our array is just the first triangles vertex. We do that because when we are using the incremental "k" value to find the vertex we want, we use "k + 1". So when "k = 2", that will be the third vertex of the triangle, so when we call "k + 1" again, we will just be using the first vertex again. It's actually a much more simple idea than I probably just now made it seem. Then we loop through each edge of the triangle. float ep1, ep2, t = 0.0f; float planeIntersectX, planeIntersectY, planeIntersectZ = 0.0f; D3DXVECTOR3 pointInPlane = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVECTOR3 t2V[4]; t2V[0] = tri2V1; t2V[1] = tri2V2; t2V[2] = tri2V3; t2V[3] = tri2V1; for(int k = 0; k < 3; k++) { Now, for each edge in the triangle, or until a collision was detected, we need to find where the line that is created from the edge intersects with the first triangles plane. ep1 and ep2 are just the two parts of the equation we use to find "t". We then set t = -1, so in case there might be a divide by zero in the next step, we don't accidentally move on to checking if the point was inside the triangle, as that would be a waste of computation time, and also a false positive if it happen to come out as true, which it wouldn't. Then we check to make sure ep1 is not equal to ep2 to avoid a divide by zero error, then we set "t". ep1 = (tri1A * t2V[k].x) + (tri1B * t2V[k].y) + (tri1C * t2V[k].z); ep2 = (tri1A * t2V[k+1].x) + (tri1B * t2V[k+1].y) + (tri1C * t2V[k+1].z); t = -1; if(ep1 != ep2) t = -(ep2 + tri1D)/(ep1 - ep2); If "t" is between 0 and 1, we know the point of intersection is between the two vertices that make up that line, which also means the triangle itself is intersecting with the plane, so we move on to find the actual point of intersection on the plane, and determine if the point of intersection is within the first triangle by calling the PointInTriangle function. If that function returns true, our two triangles are colliding. if it returns false, we move on to the next two edges, then the next triangles. If none of the triangle intersect, we will return false. if(t >= 0.0f && t <= 1.0f) { planeIntersectX = (t2V[k].x * t) + (t2V[k+1].x * (1 - t)); planeIntersectY = (t2V[k].y * t) + (t2V[k+1].y * (1 - t)); planeIntersectZ = (t2V[k].z * t) + (t2V[k+1].z * (1 - t)); pointInPlane = D3DXVECTOR3(planeIntersectX, planeIntersectY, planeIntersectZ); if(PointInTriangle(tri1V1, tri1V2, tri1V3, pointInPlane)) { return true; } } } } } return false; } Here is our PointInTriangle() function. It takes four arguments, the first three are the vertices which make up the triangle, and the fourth is the point of intersection with the triangles plane. bool PointInTriangle(D3DXVECTOR3 triV1, D3DXVECTOR3 triV2, D3DXVECTOR3 triV3, D3DXVECTOR3 point ) { To find if the point is within the triangle, we will find the area of the triangle. We can do that by find the length of each edge, then using the lengths of the edge to find the area of the triangle. float distX = triV1.x - triV2.x; float distY = triV1.y - triV2.y; float distZ = triV1.z - triV2.z; float edgeLength1 = sqrt(distX*distX + distY*distY + distZ*distZ); distX = triV1.x - triV3.x; distY = triV1.y - triV3.y; distZ = triV1.z - triV3.z; float edgeLength2 = sqrt(distX*distX + distY*distY + distZ*distZ); distX = triV2.x - triV3.x; distY = triV2.y - triV3.y; distZ = triV2.z - triV3.z; float edgeLength3 = sqrt(distX*distX + distY*distY + distZ*distZ); float s = (edgeLength1 + edgeLength2 + edgeLength3)/2.0f; float mainTriArea = sqrt(s*(s-edgeLength1)*(s-edgeLength2)*(s-edgeLength3)); We initialize another array, which will hold the three float values representing the areas of the three triangles created with the point of intersection, and the three vertices of the triangle. float smallTriArea[3] = {0.0f, 0.0f, 0.0f}; D3DXVECTOR3 triVert[4]; triVert[0] = triV1; triVert[1] = triV2; triVert[2] = triV3; triVert[3] = triV1; //When i=2, i+1 will be triV1 //Find 3 triangle areas using the plane intersecting point for(int i = 0; i < 3; i++) { distX = point.x - triVert[i].x; distY = point.y - triVert[i].y; distZ = point.z - triVert[i].z; edgeLength1 = sqrt(distX*distX + distY*distY + distZ*distZ); distX = point.x - triVert[i+1].x; distY = point.y - triVert[i+1].y; distZ = point.z - triVert[i+1].z; edgeLength2 = sqrt(distX*distX + distY*distY + distZ*distZ); distX = triVert[i].x - triVert[i+1].x; distY = triVert[i].y - triVert[i+1].y; distZ = triVert[i].z - triVert[i+1].z; edgeLength3 = sqrt(distX*distX + distY*distY + distZ*distZ); s = (edgeLength1 + edgeLength2 + edgeLength3)/2.0f; smallTriArea[i] = sqrt(s*(s-edgeLength1)*(s-edgeLength2)*(s-edgeLength3)); } Then we add the three areas together, and compare them with the triangles area. If the areas are equal (or smaller which is not actually possible without our small modification), the point is inside the triangle, and we return true, otherwise we retrun false. The modification I was talking about is subtracting a small float value from totalSmallTriArea. I do this because of float innacuracy. Sometimes if the point is inside the triangle, the value of the three smaller triangles added up might be a tiny bit larger than what they should because float values are only so accurate. Since the three triangle areas added up cannot POSSIBLY be any smaller than the main triangles area (meaning the three triangle areas LOWEST possible value would be the same as the main triangle), we take away a small value to make up for the innacuracy. If you don't do this, you might notice that your collision detection doesn't work the entire time, and sometimes your objects will be half into each other before a collision was detected. float totalSmallTriArea = smallTriArea[0] + smallTriArea[1] + smallTriArea[2]; //Compare the three smaller triangle areas with the main triangles area //Subtract a small value from totalSmallTriArea to make up for inacuracy if(mainTriArea >= (totalSmallTriArea - 0.001f)) { return true; } return false; } Now to impliment our highly accurate collision detection functions, we go to our original CollisionDetect() function and, after we have found that the bounding spheres of two objects are colliding, we check to see if the two objects triangles are colliding by calling our new function. If you want to test the difference in frame rate between using a bounding sphere then checking for triangle to triangle collisions, and just checking for the triangle to triangle collisions without checking the bounding spheres, you can do that here. comment out the if(distToObject <= (spheresBoundingRadius + spheresBoundingRadius)), (and the two brackets associated with it). You will notice how far your frame rate will drop. Mine dropped from about 2000 to 1. That is why you want to do these accurate collision tests as least as you can. if(distToObject <= (spheresBoundingRadius + spheresBoundingRadius)) { if(TriTriCollisionDetect(meshes[1], objSpace, meshes[1], SphereLocalSpace[i])) { 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; } } Now we can know how to use very accurate collision detection! Let me know if you find any problems. 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 = 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 = 81; int spheres[81]; D3DXMATRIX SphereLocalSpace[81]; ID3DX10Mesh* sphereBoundingBox; ID3D10ShaderResourceView* BoundingBoxTexture; bool showBoundingVolumes = true; bool spacePress = false; int bbShots; void CreateSpheresBoundingBox(); bool DetectCollision(D3DXMATRIX objSpace); float spheresBoundingRadius; int pickedObj = -1; /////////////////////////new///////////////////////////////////////////////// bool PointInTriangle(D3DXVECTOR3 triV1, D3DXVECTOR3 triV2, D3DXVECTOR3 triV3, D3DXVECTOR3 point ); bool TriTriCollisionDetect(ID3DX10Mesh* mesh1, D3DXMATRIX mesh1ObjSpace, ID3DX10Mesh* mesh2, D3DXMATRIX mesh2ObjSpace); /////////////////////////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 TriTriCollisionDetect(ID3DX10Mesh* mesh1, D3DXMATRIX mesh1ObjSpace, ID3DX10Mesh* mesh2, D3DXMATRIX mesh2ObjSpace) { //Get the first mesh's information// int numMesh1Faces = mesh1->GetFaceCount(); ID3DX10MeshBuffer* mesh1VertexBuffer; mesh1->GetVertexBuffer(0, &mesh1VertexBuffer); Vertex* mesh1Vertices = 0; mesh1VertexBuffer->Map((void**)&mesh1Vertices, 0); ID3DX10MeshBuffer* mesh1IndexBuffer; mesh1->GetIndexBuffer(&mesh1IndexBuffer); DWORD* mesh1Indices = 0; mesh1IndexBuffer->Map((void**)&mesh1Indices, 0); mesh1VertexBuffer->Unmap(); mesh1VertexBuffer->Release(); mesh1IndexBuffer->Unmap(); mesh1IndexBuffer->Release(); //Get the second mesh's information// int numMesh2Faces = mesh2->GetFaceCount(); ID3DX10MeshBuffer* mesh2VertexBuffer; mesh2->GetVertexBuffer(0, &mesh2VertexBuffer); Vertex* mesh2Vertices = 0; mesh2VertexBuffer->Map((void**)&mesh2Vertices,0); ID3DX10MeshBuffer* mesh2IndexBuffer; mesh2->GetIndexBuffer(&mesh2IndexBuffer); DWORD* mesh2Indices = 0; mesh2IndexBuffer->Map((void**)&mesh2Indices, 0); mesh2VertexBuffer->Unmap(); mesh2VertexBuffer->Release(); mesh2IndexBuffer->Unmap(); mesh2IndexBuffer->Release(); //Detect Collision for(int i = 0; i < numMesh1Faces; i++) { //Triangle's vertices V1, V2, V3 D3DXVECTOR3 tri1V1 = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVECTOR3 tri1V2 = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVECTOR3 tri1V3 = D3DXVECTOR3(0.0f, 0.0f, 0.0f); //Get mesh1's triangle tri1V1 = mesh1Vertices[mesh1Indices[(i*3)+0]].pos; tri1V2 = mesh1Vertices[mesh1Indices[(i*3)+1]].pos; tri1V3 = mesh1Vertices[mesh1Indices[(i*3)+2]].pos; //Transform vertice's local space to objects world space D3DXVec3TransformCoord(&tri1V1, &tri1V1, &mesh1ObjSpace); D3DXVec3TransformCoord(&tri1V2, &tri1V2, &mesh1ObjSpace); D3DXVec3TransformCoord(&tri1V3, &tri1V3, &mesh1ObjSpace); //Find the normal using U, V coordinates D3DXVECTOR3 U = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVECTOR3 V = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVECTOR3 faceNormal = D3DXVECTOR3(0.0f, 0.0f, 0.0f); U = tri1V2 - tri1V1; V = tri1V3 - tri1V1; //Comput face normal using U, V D3DXVec3Cross(&faceNormal, &U, &V); D3DXVec3Normalize(&faceNormal, &faceNormal); //Calculate a point on the triangle D3DXVECTOR3 triPoint = (tri1V1 + tri1V2 + tri1V3)/3; //Get plane equation "Ax + By + Cz + D = 0" Variables float tri1A = faceNormal.x; float tri1B = faceNormal.y; float tri1C = faceNormal.z; float tri1D = (-tri1A*triPoint.x - tri1B*triPoint.y - tri1C*triPoint.z); //Now loop through second mesh's faces to check for collision for(int j = 0; j < numMesh2Faces; j++) { //Triangle's vertices V1, V2, V3 D3DXVECTOR3 tri2V1 = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVECTOR3 tri2V2 = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVECTOR3 tri2V3 = D3DXVECTOR3(0.0f, 0.0f, 0.0f); //Get mesh2's triangle tri2V1 = mesh2Vertices[mesh2Indices[(j*3)+0]].pos; tri2V2 = mesh2Vertices[mesh2Indices[(j*3)+1]].pos; tri2V3 = mesh2Vertices[mesh2Indices[(j*3)+2]].pos; //Transform triangles vertice's to second objects world space D3DXVec3TransformCoord(&tri2V1, &tri2V1, &mesh2ObjSpace); D3DXVec3TransformCoord(&tri2V2, &tri2V2, &mesh2ObjSpace); D3DXVec3TransformCoord(&tri2V3, &tri2V3, &mesh2ObjSpace); float ep1, ep2, t = 0.0f; float planeIntersectX, planeIntersectY, planeIntersectZ = 0.0f; D3DXVECTOR3 pointInPlane = D3DXVECTOR3(0.0f, 0.0f, 0.0f); D3DXVECTOR3 t2V[4]; //Triangle 2 vertices, array used for loop t2V[0] = tri2V1; t2V[1] = tri2V2; t2V[2] = tri2V3; t2V[3] = tri2V1; for(int k = 0; k < 3; k++) { //Calculate where on the line, created from this trangles //edge (eg. tri2V1, tri2V2), intersects with the plane ep1 = (tri1A * t2V[k].x) + (tri1B * t2V[k].y) + (tri1C * t2V[k].z); ep2 = (tri1A * t2V[k+1].x) + (tri1B * t2V[k+1].y) + (tri1C * t2V[k+1].z); //Set t to -1 in case there is a divide-by-zero t = -1; //Make sure there are no divide-by-zeros if(ep1 != ep2) t = -(ep2 + tri1D)/(ep1 - ep2); //If the lines intersection with the triangles plane was between the //two vertices (tri2V1, tri2V2), calculate the point of intersection if(t >= 0.0f && t <= 1.0f) { planeIntersectX = (t2V[k].x * t) + (t2V[k+1].x * (1 - t)); planeIntersectY = (t2V[k].y * t) + (t2V[k+1].y * (1 - t)); planeIntersectZ = (t2V[k].z * t) + (t2V[k+1].z * (1 - t)); pointInPlane = D3DXVECTOR3(planeIntersectX, planeIntersectY, planeIntersectZ); //Call function to check if point is in the triangle if(PointInTriangle(tri1V1, tri1V2, tri1V3, pointInPlane)) { return true; //There was an intersection } } } } } return false; //There was no intersection } bool PointInTriangle(D3DXVECTOR3 triV1, D3DXVECTOR3 triV2, D3DXVECTOR3 triV3, D3DXVECTOR3 point ) { //To find out if the point is inside the triangle, we will first find the area //of the entire triangle. After we find the area of the entire triangle, we will //use the point as a vertex, and create 3 more triangles using the three vertices //which make up the first triangle. We will find the area of the three triangles we //make using the point, then add the three triangle areas together. If the area //of the three triangles added up is the same as the first triangles area, the point //is inside the triangle. If the area of the three triangles added up is greater than //the area of the first triangle, the point is outside the triangle. //Find area of first triangle float distX = triV1.x - triV2.x; float distY = triV1.y - triV2.y; float distZ = triV1.z - triV2.z; float edgeLength1 = sqrt(distX*distX + distY*distY + distZ*distZ); distX = triV1.x - triV3.x; distY = triV1.y - triV3.y; distZ = triV1.z - triV3.z; float edgeLength2 = sqrt(distX*distX + distY*distY + distZ*distZ); distX = triV2.x - triV3.x; distY = triV2.y - triV3.y; distZ = triV2.z - triV3.z; float edgeLength3 = sqrt(distX*distX + distY*distY + distZ*distZ); float s = (edgeLength1 + edgeLength2 + edgeLength3)/2.0f; float mainTriArea = sqrt(s*(s-edgeLength1)*(s-edgeLength2)*(s-edgeLength3)); //Find areas of the three triangles created with the point float smallTriArea[3] = {0.0f, 0.0f, 0.0f}; D3DXVECTOR3 triVert[4]; triVert[0] = triV1; triVert[1] = triV2; triVert[2] = triV3; triVert[3] = triV1; //When i=2, i+1 will be triV1 //Find 3 triangle areas using the plane intersecting point for(int i = 0; i < 3; i++) { distX = point.x - triVert[i].x; distY = point.y - triVert[i].y; distZ = point.z - triVert[i].z; edgeLength1 = sqrt(distX*distX + distY*distY + distZ*distZ); distX = point.x - triVert[i+1].x; distY = point.y - triVert[i+1].y; distZ = point.z - triVert[i+1].z; edgeLength2 = sqrt(distX*distX + distY*distY + distZ*distZ); distX = triVert[i].x - triVert[i+1].x; distY = triVert[i].y - triVert[i+1].y; distZ = triVert[i].z - triVert[i+1].z; edgeLength3 = sqrt(distX*distX + distY*distY + distZ*distZ); s = (edgeLength1 + edgeLength2 + edgeLength3)/2.0f; smallTriArea[i] = sqrt(s*(s-edgeLength1)*(s-edgeLength2)*(s-edgeLength3)); } float totalSmallTriArea = smallTriArea[0] + smallTriArea[1] + smallTriArea[2]; //Compare the three smaller triangle areas with the main triangles area //Subtract a small value from totalSmallTriArea to make up for inacuracy if(mainTriArea >= (totalSmallTriArea - 0.001f)) { return true; } return false; } /////////////////////////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); /////////////////////////new///////////////////////////////////////////////// if(distToObject <= (spheresBoundingRadius + spheresBoundingRadius)) { if(TriTriCollisionDetect(meshes[1], objSpace, meshes[1], SphereLocalSpace[i])) { 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; } } /////////////////////////new///////////////////////////////////////////////// } } return false; } 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; } 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); } 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(¤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; } 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); } 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; 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(); float sphereXPos = 0.0f; float sphereZPos = 0.0f; float sxadd = 0.0f; float szadd = 0.0f; for(int i = 0; i < numSpheres; i++) { D3DXMatrixScaling( &Scale, 2.0f, 2.0f, 2.0f ); D3DXMatrixTranslation( &Translation, sphereXPos + sxadd*10.0f, 1.0f, sphereZPos + szadd*10.0f ); SphereLocalSpace[i] = Scale * Translation; } 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); } }