This tutorial is part of a Collection: 03. DirectX 11 - Braynzar Soft Tutorials
rate up
0
rate down
5298
views
bookmark
24. Picking

This lesson builds directly off the last lesson, normal mapping. Here we will learn how to turn the 2d screen position (in pixels) that the mouse cursor is at, into a 3d ray in world space. We can then check to see if that ray intersects with any of the objects on the screen. (The objects in this case are "supposed" to be bottles, but i've been recently told they look like urns...). If the ray intersects with any of the bottles in this lesson, we will display the distance from the camera to that bottle, increase the score, and remove that bottle from being displayed any longer and from being checked if the ray picks it again.

1316 downloads
##Introduction## This lesson build directly off the last lesson, Normal Mapping. We will learn how to pick objects in Direct3D 11. Picking in D3D11 takes an extra step than picking in D3D10. This is because the mesh interface is not available in D3D11 anymore, which before had a method to test if a ray intersected with an object. We will now do this ourselves. Picking a 3D Object To pick a 3D object, we will need to follow a series of steps: Get a 2D vector (x, y) of the Position of the mouse cursor in screen space (the client window) (in pixels). Transform the 2D vector representing the mouses position in screen space to a 3D vector (x, y, z) representing a ray in view space. Transform the view space ray into a world space ray. Transform the model, or objects vertex positions from model (or local) space to world space. Loop through each of the triangles in the model to find out which (if any) triangle the ray intersects with. (This is the step we were able to skip in D3D10 by using a method provided by the mesh interface.) **1. Get the Cursor Position in Screen Space** This is easy. To get the cursor position in screen space (the client window), we call two functions, GetCursorPos(), and ScreenToClient(). These will store the x and y positions of the cursor in a variable of type POINT. This variable will be the position of our cursor measured in units of pixels, where the top left of the client window is (0, 0), and the bottom right is the (clientWidth, clientHeight). We will then pass the x and y coordinates of the cursor to a function which will transform it into a 3D vector representing a ray in view space, then transform that 3D ray in view space to a 3D ray in world space. **2. Transform 2D Screen Space Ray to 3D View Space Ray** This is where we start running into a little math. First we Transform the Picked point's x- and y- axis (in screen space) to View space coordinates. View space ranges from -1 to 1 on the x and y axis, and 0 to 1 on the z axis. To do this is not very difficult, We multiply the picked x and y positions by two, divide that by the width and height of the client window, then subtract 1. This is probably more clear by example. Say our window is 800 by 600. we click at the point (100, 100). We will start by finding the x axis in view space. First we multiply 100 by 2. This gives us 200. We then divide 200 by the width of the window (800), giving us 0.25. We can then subtract 1 from this answer, which gives us -0.75. Now this point's x axis has been converted to view space, where the value is between -1 and 1. You can imagine the same for the Y axis. We now know the equation to get the x and y axis in screen space converted to view space: ViewSpaceX = (2 * PickedPointX) / ClientWidth ViewSpaceY = (2 * PickedPointY) / ClientHeight That was easy right? Well now for the more difficult math problem to kick in. We need to solve for ViewSpaceZ. We can do this in one of two ways. Either set Z to equal 1, and modify the ViewSpaceX and ViewSpaceY equations, or actually solve for Z. Z is actually the distance from the cameras origin, to the projection window. We can find Z, or the distance to the projection window with the equation Z = cotangent(angle/2). Angle is the vertical field of view in radians, which we set before in our code when we set camProjection using the function XMMatrixPerspectiveFovLH(). If you look for this line in the lessons code, you can see that we set the vertical field of view angle to 0.4f * 3.14. To find the cotangent we can use 1/tangent, so to find Z: ViewSpaceZ = 1 / tan((0.4 * 3.14) / 2) Now instead of actually solving for Z, We can easily modify our two equations to find view spaces x and y values by using the projection matrix represented by camProjection in this lessons code. For ViewSpaceX, all we need to do is divide by the variable stored in camProjection(0,0). This variable is "1 / (ratio * tangent(angle / 2))". For ViewSpaceY, we divide by camProjection(1,1), which is "1 / tangent(angle / 2)". So Our final code will look like this: ViewSpaceX = ((2 * PickedPointX) / ClientWidth) / camProjection(0,0) ViewSpaceY = ((2 * PickedPointY) / ClientHeight) / camProjection(1,1) ViewSpaceZ = 1 *First Person Shooter Picking* If we want to use the center of the screen (like in a first person shooter) to be the point we are picking with, That is very very very easy. Since View Space ranges from -1 to 1 on the x and y axis, and 0 to 1 on the z axis, we can use (0, 0, 1) as our View Space point. The reason this works is because We are going straight into view space from the origin of the camera, which is at point (0, 0, 0) in view space. In fact, we could actually set the z value to anything greater than 0 for this to work, since we essencially creating a ray using the two points (origin and direction). So our code for a first person shooter would look like this: ViewSpaceX = 0 ViewSpaceY = 0 ViewSpaceZ = 1 **3. View Space to World Space** Remember to go from local to world to view to screen space, we just multiply them in order? Well now we need to go backwards from View space to World space. How do we do this? easy, we find the inverse of our view space matrix, which is actually our world space matrix! We can find the inverse of the matrix by using the function XMMatrixInverse(), where the first parameter we will not use, and the second parameter is our cameras view space matrix. The funny thing about this function, is we cannot set the first parameter to null, even though we don't need it. We need to supply it a vector, which it will then send the "inverse determinant" vector to whatever we supply for the first parameter. Anyway, After we find our world matrix by inversing our view matrix, we transform our rays position and direction in view space using the world space matrix, which results in our world space ray! **4. Transform Vertices to World Space** We're not done transforming things yet! But this is our last transform. We need to take the vertices positions (in the models space) that make up the model, and transform them to world space, using the models world space matrix. The problem with this step that you might find, is that we need to actually get our vertex positions from the model. In D3D10, this was not a problem, all we had to do was use the models world space matrix and a function provided by the mesh interface which would detect if a ray intersected with the model. However, in D3D 11, the mesh interface is no long an option. Because of this, we need to get the vertex positions ourselves, and check for intersection ourselves. Getting the Vertex positions is not actually difficult, but we will not be taking them from our vertex buffer, since the CPU can only read from a staging buffer, and a buffer that the cpu can read from cannot be directly binded to the pipeline for rendering. So what we need to do now, is when we create our vertices and incices to put into our vertex and index buffer for the model, we ALSO need to create two arrays or vectors to hold the indices and just the positions of our vertices. We will then use these positions in the vertex array to get the positions of the triangles in our model, and the indices to find which vertices make up each triangle. After we do all that, we can then transform the vertices into world space using the models world space matrix. One thing I should also mention, is up until this point, Everything we have been doing, only needs to be done once per click, because all objects we check if the pick ray intersects with will use that same picking ray. But from this part on (step 4), we need to start doing these steps for every model or model's subsets we want to check. **5. Check for an Intersection Between the Pick Ray and Object (Model)** Here's where the real math starts. This step can be broken down into smaller steps: First fill in the plane equation the triangle lies on ("Ax + By + Cz + D = 0"). Find where (on the ray) the ray intersects with the triangles plane. Find the point on the triangles plane in which the ray intersects. Find if the point the ray intersects with the triangles plane is inside the triangle. *1. Fill in the plane equation the triangle lies on ("Ax + By + Cz + D = 0")* To find the plane the triangle lies on (which is described by the triangles normal), we first get two edges of the triangle, U and V. We will then use these two edges to get the normal of the triangle, which describes the plane, and the A, B, and C components of our equation. We can use the following equation to find the face normal: faceNormal = (V2 - V1) x (V3 - V1) Where "x" is a cross multiplication of vectors. We can do this in code like this: //Gets the two edges U = TriangleVertex2 - TriangleVertex1; V = TriangleVertex3 - TriangleVertex1; //Gets the triangles normal faceNormal = XMVector3Cross(U, V); //Normalize the normal faceNormal = XMVector3Normalize(faceNormal); Now we have the A, B, and C components of our plane equation, which is just the x, y, and z parts of the planes normal. Now we need to find the x, y, and z components of our equation. These are just another point on the plane, and since all three vertices make up the plane, any of them will work, so in our code, we will just use the first vertex that makes up the triangle. Now our equations looks like this: (faceNormal.x * TriangleVertex1.x) + (faceNormal.y * TriangleVertex1.y) + (faceNormal.z * TriangleVertex1.z) + D = 0 The last thing we need to do in this step is to find "D". This only takes a little algebra, so it isn't very difficult. Remember learning in algebra whatever you do to one side of the equals sign, you have to do to the other? Well taking that into account, we will first subtract D from both sides, making the equation look like this: Ax + By + Cz = -D Now we have found negative D (-D), but we need just plain "D". How do we do this? Easy, just multiply both sides by -1! -1 * (Ax + By + Cz) = D //Turns into: -Ax - By - Cz = D //In Code: D = -(faceNormal.x * TriangleVertex1.x) - (faceNormal.y * TriangleVertex1.y) - (faceNormal.z * TriangleVertex1.z) We have now found our plane equation and can move on to find where on the ray intersects with the plane. *2. Find where (on the ray) the ray intersects with the triangles plane.* Now we find a value we will name "t". "t" is the distance from the rays origin (position of the camera) that the ray intersects with the triangles plane. If "t" is positive, we know the intersection happened in front of the camera, and if "t" is negative, the intersection happened behind the camera, which we will then skip the rest (unless you want to pick objects behind the camera). We have to watch out for a divide by zero here, in the off chance that the picked ray runs perfectly parallel to the plane. To find "t" we can use this parametric equation: t = -(A*x2 + B*y2 + C*z2 + D) / (A*(x1-x2) + B*(y1-y2) + C*(z1-z2)) Where x1, y1, and z1 are the origin of the ray, and x2, y2, and z2 are the direction of the ray. To make this easier to read in code, we have first created two parts to our equation, "ep1" and "ep2". These parts are found by plugging the position and orgin of our ray into the plane equation, like this: ep1 = (pickRayInWorldSpacePos.x * (Ax)) + (pickRayInWorldSpacePos.y * (By)) + (pickRayInWorldSpacePos.z * (Cz)); ep2 = (pickRayInWorldSpaceDir.x * (Ax)) + (pickRayInWorldSpaceDir.y * (By)) + (pickRayInWorldSpaceDir.z * (Cz)); After we have these two parts, we can solve for "t" much easier: t = -(ep1 + D)/(ep2) Remember the divide by zero I mentioned? this is where it could happen. So to prevent a vortex which swallows up the entire world (a divide by zero), we check to make sure "ep2" does not equal zero before we run this equation. *3. Find the Point on the Triangles Plane in Which the Picked Ray Intersects.* This is pretty easy actually. We can find the x, y, and z values of this point with the following equations: planeIntersectX = pickRayInWorldSpacePos.x + pickRayInWorldSpaceDir.x * t; planeIntersectY = pickRayInWorldSpacePos.y + pickRayInWorldSpaceDir.y * t; planeIntersectZ = pickRayInWorldSpacePos.z + pickRayInWorldSpaceDir.z * t; *4. Find if the point the ray intersects with the triangles plane is inside the triangle.* This is also not terribly difficult, and there are many ways to do this. One possibility is to find the area of the triangle, create three smaller triangles using the intersection point and the three vertices that make up the first triangle (the triangle you are checking for an intersection with). Add up the areas of the three smaller triangles, and if they equal the area of the first triangle, the point is inside. Another way to do this is use barycentric coordinates, which is a tad bit more confusing, but the most efficient way of doing this. The way we will do it, is check which side of each of the three edges that make up the triangle that the intersection point is located. If the intersection point is on the "correct" side of each of the edges, then it is inside the triangle, otherwise if it is on the wrong side of any of the triangles edges, it is not inside the triangle and we can exit early. I want to direct you to here if you are interested in learning how to use barycentric coordinates to do this job, or for a more complete explanation on the way I will show you how to do it. To test a side of the triangle to find if the point is on the "correct" side, we will need another point that IS on the correct side of the line. We will use the third, or unused corner of the triangle (i say unused, but i mean unused in the edge we are checking against). We create two cross products, the first using the point and the edges two vertices, and the second using the third vertex, and the edges two vertices. We will get the dot product of these two cross products, and if the answer is greater than or equal to 0, the point is on the "correct" side. Otherwise, the point is outside the triangle and we can exit. Here is the code: XMVECTOR cp1 = XMVector3Cross((triV3 - triV2), (point - triV2)); XMVECTOR cp2 = XMVector3Cross((triV3 - triV2), (triV1 - triV2)); if(XMVectorGetX(XMVector3Dot(cp1, cp2)) >= 0) { cp1 = XMVector3Cross((triV3 - triV1), (point - triV1)); cp2 = XMVector3Cross((triV3 - triV1), (triV2 - triV1)); if(XMVectorGetX(XMVector3Dot(cp1, cp2)) >= 0) { cp1 = XMVector3Cross((triV2 - triV1), (point - triV1)); cp2 = XMVector3Cross((triV2 - triV1), (triV3 - triV1)); if(XMVectorGetX(XMVector3Dot(cp1, cp2)) >= 0) { return true; } else return false; } else return false; } return false; I want to mention one last thing. Don't make fun of my bottle ##Model's Vertex Position and Index Arrays## Now that our program needs to read the models vertex positions and vertex indices, we need to create a vector to hold them, since we are not able to read the D3D buffers they are held in directly with the CPU. So here is the two new ones for our ground model. std::vector<XMFLOAT3> groundVertPosArray; std::vector<DWORD> groundVertIndexArray; ##Bottle Model Members## These are all the variables and stuff that our bottle model's need. Notice how we have a an array of the bottles world matrix and an array of integers named "bottleHit", also the "numBottles" variable. These are because we will be displaying more than a single bottle in our screen, specifically here, 20. ID3D11Buffer* bottleVertBuff; ID3D11Buffer* bottleIndexBuff; std::vector<XMFLOAT3> bottleVertPosArray; std::vector<DWORD> bottleVertIndexArray; int bottleSubsets = 0; std::vector<int> bottleSubsetIndexStart; std::vector<int> bottleSubsetTexture; XMMATRIX bottleWorld[20]; int* bottleHit = new int[20]; int numBottles = 20; ##New Global Variables## We have a couple new global variables. The first being "isShoot". This variable will be true while the first mouse button is being pressed, and false while it is not. We do this so we can't just hold the mouse button down and glide the cursor over the objects to be picked. We only want to be able to pick one time per click. The next two are integers which will hold the width and height of our client window in pixels. As you will see later, these are updated as the screen is resized. Luckily, the "WM_SIZE" windows message is automatically called when we start our program, so these variables will be filled as our program starts. Otherwise we would have to fill them manually at first, which might not be 100% accurate if we are in windowed mode (since the border and the top of the applications window take up a little space). Next is a global variable that will keep track of our score (the number of bottles picked), and the one after that is the distance from the camera to the last picked object (only updated when the object was picked). bool isShoot = false; int ClientWidth = 0; int ClientHeight = 0; int score = 0; float pickedDist = 0.0f; ##New Function Prototypes## We have three new functions. The first will calculate the world space pick ray from the 2d coordinates of our mouse cursor. The next calculates whether the object was picked or not, calling the last function. The last function is called from the pick function, and it says if a point is inside a triangle (the point has to be on the triangles plane to know for sure). void pickRayVector(float mouseX, float mouseY, XMVECTOR& pickRayInWorldSpacePos, XMVECTOR& pickRayInWorldSpaceDir); float pick(XMVECTOR pickRayInWorldSpacePos, XMVECTOR pickRayInWorldSpaceDir, std::vector<XMFLOAT3>& vertPosArray, std::vector<DWORD>& indexPosArray, XMMATRIX& worldSpace); bool PointInTriangle(XMVECTOR& triV1, XMVECTOR& triV2, XMVECTOR& triV3, XMVECTOR& point ); ##New LoadObjModel() Function Prototype## We have added two new arguments to this function. They are so we can get the models vertex positions and indices and do operations like check for a ray intersection with the model. We need to do this because we cannot read from the D3D buffers that hold the vertex and index information we bind to the pipeline. bool LoadObjModel(std::wstring filename, //.obj filename ID3D11Buffer** vertBuff, //mesh vertex buffer ID3D11Buffer** indexBuff, //mesh index buffer std::vector<int>& subsetIndexStart, //start index of each subset std::vector<int>& subsetMaterialArray, //index value of material for each subset std::vector<SurfaceMaterial>& material, //vector of material structures int& subsetCount, //Number of subsets in mesh bool isRHCoordSys, //true if model was created in right hand coord system bool computeNormals, //true to compute the normals, false to use the files normals ///////////////**************new**************//////////////////// std::vector<XMFLOAT3>& vertPosArray, //Used for CPU to do calculations on the Geometry std::vector<DWORD>& vertIndexArray); //Also used for CPU caculations on geometry ///////////////**************new**************//////////////////// ##Initializing Direct Input## We have to modify the function that initializes direct input so we can see the mouse when running our application, because until now (and since we implemented direct input), we have had the cooperation level for the mouse set to exclusive, and we could not see the cursor, but now we will set it to non-exclusive, so we can see the cursor. bool InitDirectInput(HINSTANCE hInstance) { hr = DirectInput8Create(hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&DirectInput, NULL); hr = DirectInput->CreateDevice(GUID_SysKeyboard, &DIKeyboard, NULL); hr = DirectInput->CreateDevice(GUID_SysMouse, &DIMouse, NULL); hr = DIKeyboard->SetDataFormat(&c_dfDIKeyboard); hr = DIKeyboard->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); hr = DIMouse->SetDataFormat(&c_dfDIMouse); ///////////////**************new**************//////////////////// hr = DIMouse->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_NOWINKEY | DISCL_FOREGROUND); ///////////////**************new**************//////////////////// return true; } ##Checking for Input## Now we will check for the left mouse button to be pressed. The left mouse button is represented by mouseCurrState.rgbButtons[0]. Remember that global variable, isShoot? Well now we get to use it. Remember when checking for input, if the button is down, the code will keep running, same with if its up. That means, the code to run when a button is pressed will not be only ran once per click, but it will be ran as many times possible while the button is currently down. We need to check to make sure the code has not already been ran since the button was last up. If you look, you can see, when the mouse button is pressed, it checks isShoot, if its false, it runs the code then sets isShoot to true. That way, the next time around, if the mouse button is still being held down, we don't check for picking again, since we only want to check for picking once per click. When the button is released, isShoot is set to false again. We create a variable of type POINT which will store our mouses position. We then get our mouses position, and translate that position to the client windows position (in case we are in windowed mode, the client window might not align exactly with the monitors screen). We create a couple variables. The first, tempDist will store the picked distance to the current object being checked. If the current object being check for picking is not actually picked, this value will be FLT_MAX, which is the largest float value possible. The next variable is the distance to the closest object that was picked. Next we have hitIndex, which will store the index value of the closest picked bottle. We need these last two variables so we only pick the closest bottle, and not bottles behind. We then create two vectors to hold the world space picking ray position and direction, and call our function which will transform our mouse cursor's position on our monitor to two 3d vectors describing the world space picking rays position and direction. The next part, the loop, is where we will check every pickable object for an intersection with our picking ray. The first thing we do in this loop is call the function to check if the current object is picked or not, and store its picked distance in tempDist. Next, if tempDist is smaller than closestDist, we know that the current picked object is closer to the camera than the last one (if there even was a last one), and set the new closestDist and hitIndex variables. After we check each model, we make sure that an object was even picked by making sure closestDist is smaller than FLT_MAX. If an object was picked, this is where we would do whatever it is we want to do when an object is picked, and in the case of this lesson, we will make sure the picked bottle is no longer displayed or checked for picking by setting its bottleHit value to 1, then increase our score, and set pickedDist to the distance to this bottle, which we will then later display on the screen. void DetectInput(double time) { DIMOUSESTATE mouseCurrState; BYTE keyboardState[256]; DIKeyboard->Acquire(); DIMouse->Acquire(); DIMouse->GetDeviceState(sizeof(DIMOUSESTATE), &mouseCurrState); DIKeyboard->GetDeviceState(sizeof(keyboardState),(LPVOID)&keyboardState); if(keyboardState[DIK_ESCAPE] & 0x80) PostMessage(hwnd, WM_DESTROY, 0, 0); float speed = 10.0f * time; if(keyboardState[DIK_A] & 0x80) { moveLeftRight -= speed; } if(keyboardState[DIK_D] & 0x80) { moveLeftRight += speed; } if(keyboardState[DIK_W] & 0x80) { moveBackForward += speed; } if(keyboardState[DIK_S] & 0x80) { moveBackForward -= speed; } ///////////////**************new**************//////////////////// if(mouseCurrState.rgbButtons[0]) { if(isShoot == false) { POINT mousePos; GetCursorPos(&mousePos); ScreenToClient(hwnd, &mousePos); int mousex = mousePos.x; int mousey = mousePos.y; float tempDist; float closestDist = FLT_MAX; int hitIndex; XMVECTOR prwsPos, prwsDir; pickRayVector(mousex, mousey, prwsPos, prwsDir); for(int i = 0; i < numBottles; i++) { if(bottleHit[i] == 0) //No need to check bottles already hit { tempDist = pick(prwsPos, prwsDir, bottleVertPosArray, bottleVertIndexArray, bottleWorld[i]); if(tempDist < closestDist) { closestDist = tempDist; hitIndex = i; } } } if(closestDist < FLT_MAX) { bottleHit[hitIndex] = 1; pickedDist = closestDist; score++; } isShoot = true; } } if(!mouseCurrState.rgbButtons[0]) { isShoot = false; } ///////////////**************new**************//////////////////// if((mouseCurrState.lX != mouseLastState.lX) || (mouseCurrState.lY != mouseLastState.lY)) { camYaw += mouseLastState.lX * 0.001f; camPitch += mouseCurrState.lY * 0.001f; mouseLastState = mouseCurrState; } UpdateCamera(); return; } ##Clean Up## Don't forget to release our bottles vertex and index buffers. void CleanUp() { SwapChain->SetFullscreenState(false, NULL); PostMessage(hwnd, WM_DESTROY, 0, 0); //Release the COM Objects we created SwapChain->Release(); d3d11Device->Release(); d3d11DevCon->Release(); renderTargetView->Release(); VS->Release(); PS->Release(); VS_Buffer->Release(); PS_Buffer->Release(); vertLayout->Release(); depthStencilView->Release(); depthStencilBuffer->Release(); cbPerObjectBuffer->Release(); Transparency->Release(); CCWcullMode->Release(); CWcullMode->Release(); d3d101Device->Release(); keyedMutex11->Release(); keyedMutex10->Release(); D2DRenderTarget->Release(); Brush->Release(); BackBuffer11->Release(); sharedTex11->Release(); DWriteFactory->Release(); TextFormat->Release(); d2dTexture->Release(); cbPerFrameBuffer->Release(); DIKeyboard->Unacquire(); DIMouse->Unacquire(); DirectInput->Release(); sphereIndexBuffer->Release(); sphereVertBuffer->Release(); SKYMAP_VS->Release(); SKYMAP_PS->Release(); SKYMAP_VS_Buffer->Release(); SKYMAP_PS_Buffer->Release(); smrv->Release(); DSLessEqual->Release(); RSCullNone->Release(); meshVertBuff->Release(); meshIndexBuff->Release(); ///////////////**************new**************//////////////////// bottleVertBuff->Release(); bottleIndexBuff->Release(); ///////////////**************new**************//////////////////// } ##The pickRayVector() Function## This is the function that will transform our cursor position to a 3d ray in world space. This was explained at the top, so I shouldn't really have to say much about it. void pickRayVector(float mouseX, float mouseY, XMVECTOR& pickRayInWorldSpacePos, XMVECTOR& pickRayInWorldSpaceDir) { XMVECTOR pickRayInViewSpaceDir = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); XMVECTOR pickRayInViewSpacePos = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); float PRVecX, PRVecY, PRVecZ; //Transform 2D pick position on screen space to 3D ray in View space PRVecX = ((( 2.0f * mouseX) / ClientWidth ) - 1 ) / camProjection(0,0); PRVecY = -((( 2.0f * mouseY) / ClientHeight) - 1 ) / camProjection(1,1); PRVecZ = 1.0f; //View space's Z direction ranges from 0 to 1, so we set 1 since the ray goes "into" the screen pickRayInViewSpaceDir = XMVectorSet(PRVecX, PRVecY, PRVecZ, 0.0f); //Uncomment this line if you want to use the center of the screen (client area) //to be the point that creates the picking ray (eg. first person shooter) //pickRayInViewSpaceDir = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f); // Transform 3D Ray from View space to 3D ray in World space XMMATRIX pickRayToWorldSpaceMatrix; XMVECTOR matInvDeter; //We don't use this, but the xna matrix inverse function requires the first parameter to not be null pickRayToWorldSpaceMatrix = XMMatrixInverse(&matInvDeter, camView); //Inverse of View Space matrix is World space matrix pickRayInWorldSpacePos = XMVector3TransformCoord(pickRayInViewSpacePos, pickRayToWorldSpaceMatrix); pickRayInWorldSpaceDir = XMVector3TransformNormal(pickRayInViewSpaceDir, pickRayToWorldSpaceMatrix); } ##The Pick() Function## This is the funciton that will check our object to see if our pick ray intersects with it. This funciton returns a float, which is the distance to the picked object. If the object was not picked, this function returns FLT_MAX. float pick(XMVECTOR pickRayInWorldSpacePos, XMVECTOR pickRayInWorldSpaceDir, std::vector<XMFLOAT3>& vertPosArray, std::vector<DWORD>& indexPosArray, XMMATRIX& worldSpace) { //Loop through each triangle in the object for(int i = 0; i < indexPosArray.size()/3; i++) { //Triangle's vertices V1, V2, V3 XMVECTOR tri1V1 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); XMVECTOR tri1V2 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); XMVECTOR tri1V3 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); //Temporary 3d floats for each vertex XMFLOAT3 tV1, tV2, tV3; //Get triangle tV1 = vertPosArray[indexPosArray[(i*3)+0]]; tV2 = vertPosArray[indexPosArray[(i*3)+1]]; tV3 = vertPosArray[indexPosArray[(i*3)+2]]; tri1V1 = XMVectorSet(tV1.x, tV1.y, tV1.z, 0.0f); tri1V2 = XMVectorSet(tV2.x, tV2.y, tV2.z, 0.0f); tri1V3 = XMVectorSet(tV3.x, tV3.y, tV3.z, 0.0f); //Transform the vertices to world space tri1V1 = XMVector3TransformCoord(tri1V1, worldSpace); tri1V2 = XMVector3TransformCoord(tri1V2, worldSpace); tri1V3 = XMVector3TransformCoord(tri1V3, worldSpace); //Find the normal using U, V coordinates (two edges) XMVECTOR U = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); XMVECTOR V = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); XMVECTOR faceNormal = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); U = tri1V2 - tri1V1; V = tri1V3 - tri1V1; //Compute face normal by crossing U, V faceNormal = XMVector3Cross(U, V); faceNormal = XMVector3Normalize(faceNormal); //Calculate a point on the triangle for the plane equation XMVECTOR triPoint = tri1V1; //Get plane equation ("Ax + By + Cz + D = 0") Variables float tri1A = XMVectorGetX(faceNormal); float tri1B = XMVectorGetY(faceNormal); float tri1C = XMVectorGetZ(faceNormal); float tri1D = (-tri1A*XMVectorGetX(triPoint) - tri1B*XMVectorGetY(triPoint) - tri1C*XMVectorGetZ(triPoint)); //Now we find where (on the ray) the ray intersects with the triangles plane float ep1, ep2, t = 0.0f; float planeIntersectX, planeIntersectY, planeIntersectZ = 0.0f; XMVECTOR pointInPlane = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); ep1 = (XMVectorGetX(pickRayInWorldSpacePos) * tri1A) + (XMVectorGetY(pickRayInWorldSpacePos) * tri1B) + (XMVectorGetZ(pickRayInWorldSpacePos) * tri1C); ep2 = (XMVectorGetX(pickRayInWorldSpaceDir) * tri1A) + (XMVectorGetY(pickRayInWorldSpaceDir) * tri1B) + (XMVectorGetZ(pickRayInWorldSpaceDir) * tri1C); //Make sure there are no divide-by-zeros if(ep2 != 0.0f) t = -(ep1 + tri1D)/(ep2); if(t > 0.0f) //Make sure you don't pick objects behind the camera { //Get the point on the plane planeIntersectX = XMVectorGetX(pickRayInWorldSpacePos) + XMVectorGetX(pickRayInWorldSpaceDir) * t; planeIntersectY = XMVectorGetY(pickRayInWorldSpacePos) + XMVectorGetY(pickRayInWorldSpaceDir) * t; planeIntersectZ = XMVectorGetZ(pickRayInWorldSpacePos) + XMVectorGetZ(pickRayInWorldSpaceDir) * t; pointInPlane = XMVectorSet(planeIntersectX, planeIntersectY, planeIntersectZ, 0.0f); //Call function to check if point is in the triangle if(PointInTriangle(tri1V1, tri1V2, tri1V3, pointInPlane)) { //Return the distance to the hit, so you can check all the other pickable objects in your scene //and choose whichever object is closest to the camera return t/2.0f; } } } //return the max float value (near infinity) if an object was not picked return FLT_MAX; } ##The PointInTriangle() Function## This function was also explained above. It will check if a point on a triangles plane is inside the triangle. If the point is inside the triangle, it returns true, otherwise it returns false. bool PointInTriangle(XMVECTOR& triV1, XMVECTOR& triV2, XMVECTOR& triV3, XMVECTOR& point ) { //To find out if the point is inside the triangle, we will check to see if the point //is on the correct side of each of the triangles edges. XMVECTOR cp1 = XMVector3Cross((triV3 - triV2), (point - triV2)); XMVECTOR cp2 = XMVector3Cross((triV3 - triV2), (triV1 - triV2)); if(XMVectorGetX(XMVector3Dot(cp1, cp2)) >= 0) { cp1 = XMVector3Cross((triV3 - triV1), (point - triV1)); cp2 = XMVector3Cross((triV3 - triV1), (triV2 - triV1)); if(XMVectorGetX(XMVector3Dot(cp1, cp2)) >= 0) { cp1 = XMVector3Cross((triV2 - triV1), (point - triV1)); cp2 = XMVector3Cross((triV2 - triV1), (triV3 - triV1)); if(XMVectorGetX(XMVector3Dot(cp1, cp2)) >= 0) { return true; } else return false; } else return false; } return false; } ##The Updated LoadObjModel() Function## Here is the function with the two new parameters which are vectors for the models vertex positions and index array. bool LoadObjModel(std::wstring filename, ID3D11Buffer** vertBuff, ID3D11Buffer** indexBuff, std::vector<int>& subsetIndexStart, std::vector<int>& subsetMaterialArray, std::vector<SurfaceMaterial>& material, int& subsetCount, bool isRHCoordSys, bool computeNormals, ///////////////**************new**************//////////////////// std::vector<XMFLOAT3>& vertPosArray, std::vector<DWORD>& vertIndexArray) ///////////////**************new**************//////////////////// { ##Storing the Models Vertex Position List and Index List## The only modification in this function is very easy. All we do is store the position of each vertex in our vertPosArray vector, then store the entire index list in vertIndexArray. //Create our vertices using the information we got //from the file and store them in a vector for(int j = 0 ; j < totalVerts; ++j) { tempVert.pos = vertPos[vertPosIndex[j]]; tempVert.normal = vertNorm[vertNormIndex[j]]; tempVert.texCoord = vertTexCoord[vertTCIndex[j]]; vertices.push_back(tempVert); ///////////////**************new**************//////////////////// //Copy just the vertex positions to the vector vertPosArray.push_back(tempVert.pos); ///////////////**************new**************//////////////////// } ///////////////**************new**************//////////////////// //Copy the index list to the array vertIndexArray = indices; ///////////////**************new**************//////////////////// ##Loading the Models## We have added two arguments when loading our models. They are at the end, and are the vertex position and index list vectors. if(!LoadObjModel(L"ground.obj", &meshVertBuff, &meshIndexBuff, meshSubsetIndexStart, meshSubsetTexture, material, meshSubsets, true, true, groundVertPosArray, groundVertIndexArray)) return false; if(!LoadObjModel(L"bottle.obj", &bottleVertBuff, &bottleIndexBuff, bottleSubsetIndexStart, bottleSubsetTexture, material, bottleSubsets, true, true, bottleVertPosArray, bottleVertIndexArray)) return false; ##Setting the Bottles World Space Matrices## At the bottom of our initScene function, we have created a loop to go through each of the bottle world space matrices and set them in order, so that the bottles are all lined up in the world. float bottleXPos = -30.0f; float bottleZPos = 30.0f; float bxadd = 0.0f; float bzadd = 0.0f; for(int i = 0; i < numBottles; i++) { bottleHit[i] = 0; //set the loaded bottles world space bottleWorld[i] = XMMatrixIdentity(); bxadd++; if(bxadd == 10) { bzadd -= 1.0f; bxadd = 0; } Rotation = XMMatrixRotationY(3.14f); Scale = XMMatrixScaling( 1.0f, 1.0f, 1.0f ); Translation = XMMatrixTranslation( bottleXPos + bxadd*10.0f, 4.0f, bottleZPos + bzadd*10.0f ); bottleWorld[i] = Rotation * Scale * Translation; } ##The RenderText() Function## We want to display the score and distance to the last picked object on the screen, so we modify the RenderText() function to do this. std::wostringstream printString; printString << text << inInt << L"\n" << L"Score: " << score << L"\n" << L"Picked Dist: " << pickedDist; ##The DrawScene() Function## We now need to display our bottles. First we loop through each bottle. If the bottle has not been hit yet, we will display it. Also, you might notice we do not display the transparent parts of the bottle in this lesson. That is because we know there are no transparent parts, so we will not waste space. //draw bottle's nontransparent subsets for(int j = 0; j < numBottles; j++) { if(bottleHit[j] == 0) { for(int i = 0; i < bottleSubsets; ++i) { //Set the grounds index buffer d3d11DevCon->IASetIndexBuffer( bottleIndexBuff, DXGI_FORMAT_R32_UINT, 0); //Set the grounds vertex buffer d3d11DevCon->IASetVertexBuffers( 0, 1, &bottleVertBuff, &stride, &offset ); //Set the WVP matrix and send it to the constant buffer in effect file WVP = bottleWorld[j] * camView * camProjection; cbPerObj.WVP = XMMatrixTranspose(WVP); cbPerObj.World = XMMatrixTranspose(bottleWorld[j]); cbPerObj.difColor = material[bottleSubsetTexture[i]].difColor; cbPerObj.hasTexture = material[bottleSubsetTexture[i]].hasTexture; cbPerObj.hasNormMap = material[bottleSubsetTexture[i]].hasNormMap; d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 ); d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer ); d3d11DevCon->PSSetConstantBuffers( 1, 1, &cbPerObjectBuffer ); if(material[bottleSubsetTexture[i]].hasTexture) d3d11DevCon->PSSetShaderResources( 0, 1, &meshSRV[material[bottleSubsetTexture[i]].texArrayIndex] ); if(material[bottleSubsetTexture[i]].hasNormMap) d3d11DevCon->PSSetShaderResources( 1, 1, &meshSRV[material[bottleSubsetTexture[i]].normMapTexArrayIndex] ); d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState ); d3d11DevCon->RSSetState(RSCullNone); int indexStart = bottleSubsetIndexStart[i]; int indexDrawAmount = bottleSubsetIndexStart[i+1] - bottleSubsetIndexStart[i]; if(!material[bottleSubsetTexture[i]].transparent) d3d11DevCon->DrawIndexed( indexDrawAmount, indexStart, 0 ); } } } ##The WndProc() Function## Now we go to our windows procedure function, which checks for windows messages. We need to check for a window resize message. This message will be sent when the program first starts, and sent every time we change the size of our window. We need to do this and store the windows new height and width in the global variables ClientWidth and ClientHeight. LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) { case WM_KEYDOWN: if( wParam == VK_ESCAPE ){ DestroyWindow(hwnd); } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; ///////////////**************new**************//////////////////// case WM_SIZE: ClientWidth = LOWORD(lParam); ClientHeight = HIWORD(lParam); return 0; } ///////////////**************new**************//////////////////// return DefWindowProc(hwnd, msg, wParam, lParam); } Now we know how to pick 3D models on the screen with Direct3D 11! Hope you got some use out of this! Don't forget to leave a comment if you find any mistakes or just found it usefull or anything! ##Exercise:## 1. Make a First Person Shooter style picking, where objects in the center of the screen are picked rather than under the cursor. 2. Make a First Person Shooter! 3. Leave a comment here! Here's the final code: bottle.obj # 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware # File Created: 30.08.2011 17:56:16 mtllib bottle.mtl # # object Line01 # v 0.0336 0.7127 -0.0641 v -0.0035 0.6952 -0.0000 v 0.0706 0.7127 0.0000 v 0.0573 0.6698 -0.1052 v 0.1181 0.6698 0.0000 v 0.0583 0.6366 -0.1070 v 0.1201 0.6366 0.0000 v 0.0529 0.5750 -0.0976 v 0.1093 0.5750 0.0000 v 0.0397 0.4945 -0.0748 v 0.0829 0.4945 0.0000 v 0.0325 0.4228 -0.0624 v 0.0685 0.4228 0.0000 v 0.0265 0.3392 -0.0520 v 0.0565 0.3392 0.0000 v 0.0232 0.2525 -0.0461 v 0.0498 0.2525 0.0000 v 0.0238 0.1712 -0.0473 v 0.0512 0.1712 0.0000 v 0.0300 0.1039 -0.0580 v 0.0635 0.1039 0.0000 v 0.0426 0.0552 -0.0798 v 0.0887 0.0552 0.0000 v 0.0600 0.0178 -0.1099 v 0.1234 0.0178 0.0000 v 0.0797 -0.0174 -0.1440 v 0.1628 -0.0174 -0.0000 v 0.0993 -0.0593 -0.1779 v 0.2020 -0.0593 -0.0000 v 0.1163 -0.1171 -0.2074 v 0.2360 -0.1171 -0.0000 v 0.1283 -0.1998 -0.2281 v 0.2600 -0.1998 -0.0000 v 0.1328 -0.3164 -0.2360 v 0.2691 -0.3164 -0.0000 v 0.1328 -1.6232 -0.2360 v 0.2691 -1.6232 -0.0000 v -0.0035 -1.6232 -0.0000 v -0.0405 0.7127 -0.0641 v -0.0642 0.6698 -0.1052 v -0.0652 0.6366 -0.1070 v -0.0598 0.5750 -0.0976 v -0.0467 0.4945 -0.0748 v -0.0395 0.4228 -0.0624 v -0.0335 0.3392 -0.0520 v -0.0301 0.2525 -0.0461 v -0.0308 0.1712 -0.0473 v -0.0370 0.1039 -0.0580 v -0.0496 0.0552 -0.0798 v -0.0669 0.0178 -0.1099 v -0.0866 -0.0174 -0.1440 v -0.1062 -0.0593 -0.1779 v -0.1232 -0.1171 -0.2074 v -0.1352 -0.1998 -0.2281 v -0.1397 -0.3164 -0.2360 v -0.1397 -1.6232 -0.2360 v -0.0035 -1.6232 0.0000 v -0.0775 0.7127 -0.0000 v -0.1250 0.6698 -0.0000 v -0.1270 0.6366 -0.0000 v -0.1162 0.5750 -0.0000 v -0.0899 0.4945 -0.0000 v -0.0755 0.4228 -0.0000 v -0.0635 0.3392 -0.0000 v -0.0567 0.2525 -0.0000 v -0.0581 0.1712 -0.0000 v -0.0704 0.1039 -0.0000 v -0.0956 0.0552 -0.0000 v -0.1304 0.0178 -0.0000 v -0.1698 -0.0174 -0.0000 v -0.2089 -0.0593 -0.0000 v -0.2429 -0.1171 0.0000 v -0.2669 -0.1998 0.0000 v -0.2760 -0.3164 0.0000 v -0.2760 -1.6232 0.0000 v -0.0405 0.7127 0.0641 v -0.0642 0.6698 0.1052 v -0.0652 0.6366 0.1070 v -0.0598 0.5750 0.0976 v -0.0467 0.4945 0.0748 v -0.0395 0.4228 0.0624 v -0.0335 0.3392 0.0520 v -0.0301 0.2525 0.0461 v -0.0308 0.1712 0.0473 v -0.0370 0.1039 0.0580 v -0.0496 0.0552 0.0798 v -0.0669 0.0178 0.1099 v -0.0866 -0.0174 0.1440 v -0.1062 -0.0593 0.1779 v -0.1232 -0.1171 0.2074 v -0.1352 -0.1998 0.2281 v -0.1397 -0.3164 0.2360 v -0.1397 -1.6232 0.2360 v 0.0336 0.7127 0.0641 v 0.0573 0.6698 0.1052 v 0.0583 0.6366 0.1070 v 0.0529 0.5750 0.0976 v 0.0397 0.4945 0.0748 v 0.0325 0.4228 0.0624 v 0.0265 0.3392 0.0520 v 0.0232 0.2525 0.0461 v 0.0238 0.1712 0.0473 v 0.0300 0.1039 0.0580 v 0.0426 0.0552 0.0798 v 0.0600 0.0178 0.1099 v 0.0797 -0.0174 0.1440 v 0.0993 -0.0593 0.1779 v 0.1163 -0.1171 0.2074 v 0.1283 -0.1998 0.2281 v 0.1328 -0.3164 0.2360 v 0.1328 -1.6232 0.2360 # 111 vertices vn -0.2282 0.9647 0.1317 vn 0.6249 0.6924 -0.3608 vn 0.8648 0.0521 -0.4993 vn 0.8563 -0.1498 -0.4944 vn 0.8332 -0.2726 -0.4811 vn 0.8532 -0.1712 -0.4926 vn 0.8594 -0.1235 -0.4962 vn 0.8641 -0.0673 -0.4989 vn 0.8659 0.0146 -0.4999 vn 0.8553 0.1571 -0.4938 vn 0.7904 0.4087 -0.4563 vn 0.6750 0.6265 -0.3897 vn 0.6216 0.6963 -0.3589 vn 0.6736 0.6285 -0.3889 vn 0.7716 0.4541 -0.4455 vn 0.8399 0.2437 -0.4849 vn 0.8641 0.0673 -0.4989 vn 0.8660 -0.0000 -0.5000 vn 0.8660 0.0000 -0.5000 vn 0.0000 -1.0000 -0.0000 vn 0.0000 1.0000 0.0000 vn -0.0000 0.9647 0.2635 vn 0.0000 0.6924 -0.7216 vn 0.0000 0.0521 -0.9986 vn 0.0000 -0.1498 -0.9887 vn 0.0000 -0.2726 -0.9621 vn 0.0000 -0.1712 -0.9852 vn 0.0000 -0.1235 -0.9923 vn 0.0000 -0.0673 -0.9977 vn 0.0000 0.0146 -0.9999 vn 0.0000 0.1571 -0.9876 vn 0.0000 0.4087 -0.9127 vn 0.0000 0.6265 -0.7794 vn 0.0000 0.6963 -0.7178 vn 0.0000 0.6285 -0.7778 vn 0.0000 0.4541 -0.8909 vn 0.0000 0.2437 -0.9698 vn -0.0000 0.2437 -0.9698 vn -0.0000 0.0673 -0.9977 vn -0.0000 -0.0000 -1.0000 vn 0.2282 0.9647 0.1317 vn -0.6249 0.6924 -0.3608 vn -0.8648 0.0521 -0.4993 vn -0.8563 -0.1498 -0.4944 vn -0.8332 -0.2726 -0.4811 vn -0.8532 -0.1712 -0.4926 vn -0.8594 -0.1235 -0.4962 vn -0.8641 -0.0673 -0.4989 vn -0.8659 0.0146 -0.4999 vn -0.8553 0.1571 -0.4938 vn -0.7904 0.4087 -0.4563 vn -0.6750 0.6265 -0.3897 vn -0.6216 0.6963 -0.3589 vn -0.6736 0.6285 -0.3889 vn -0.7716 0.4541 -0.4455 vn -0.8399 0.2437 -0.4849 vn -0.8641 0.0673 -0.4989 vn -0.8660 -0.0000 -0.5000 vn -0.8660 0.0000 -0.5000 vn 0.2282 0.9647 -0.1317 vn -0.6249 0.6924 0.3608 vn -0.8648 0.0521 0.4993 vn -0.8563 -0.1498 0.4944 vn -0.8332 -0.2726 0.4811 vn -0.8532 -0.1712 0.4926 vn -0.8594 -0.1235 0.4962 vn -0.8641 -0.0673 0.4989 vn -0.8659 0.0146 0.4999 vn -0.8553 0.1571 0.4938 vn -0.7904 0.4087 0.4563 vn -0.6750 0.6265 0.3897 vn -0.6216 0.6963 0.3589 vn -0.6736 0.6285 0.3889 vn -0.7716 0.4541 0.4455 vn -0.8399 0.2437 0.4849 vn -0.8641 0.0673 0.4989 vn -0.8660 -0.0000 0.5000 vn -0.8660 0.0000 0.5000 vn 0.0000 0.9647 -0.2635 vn -0.0000 0.6924 0.7216 vn -0.0000 0.0521 0.9986 vn -0.0000 -0.1498 0.9887 vn -0.0000 -0.2726 0.9621 vn -0.0000 -0.1712 0.9852 vn -0.0000 -0.1235 0.9923 vn -0.0000 -0.0673 0.9977 vn -0.0000 0.0146 0.9999 vn -0.0000 0.1571 0.9876 vn -0.0000 0.4087 0.9127 vn -0.0000 0.6265 0.7794 vn -0.0000 0.6963 0.7178 vn -0.0000 0.6285 0.7778 vn -0.0000 0.4541 0.8909 vn 0.0000 0.4541 0.8909 vn 0.0000 0.2437 0.9698 vn 0.0000 0.0673 0.9977 vn 0.0000 0.0000 1.0000 vn -0.2282 0.9647 -0.1317 vn 0.6249 0.6924 0.3608 vn 0.8648 0.0521 0.4993 vn 0.8563 -0.1498 0.4944 vn 0.8332 -0.2726 0.4811 vn 0.8532 -0.1712 0.4926 vn 0.8594 -0.1235 0.4962 vn 0.8641 -0.0673 0.4989 vn 0.8659 0.0146 0.4999 vn 0.8553 0.1571 0.4938 vn 0.7904 0.4087 0.4563 vn 0.6750 0.6265 0.3897 vn 0.6216 0.6963 0.3589 vn 0.6736 0.6285 0.3889 vn 0.7716 0.4541 0.4455 vn 0.8399 0.2437 0.4849 vn 0.8641 0.0673 0.4989 vn 0.8660 -0.0000 0.5000 vn 0.8660 0.0000 0.5000 # 116 vertex normals g Line01 usemtl 01___Default f 1//1 2//1 3//1 f 4//2 1//2 3//2 f 3//2 5//2 4//2 f 6//3 4//3 5//3 f 5//3 7//3 6//3 f 8//4 6//4 7//4 f 7//4 9//4 8//4 f 10//5 8//5 9//5 f 9//5 11//5 10//5 f 12//6 10//6 11//6 f 11//6 13//6 12//6 f 14//7 12//7 13//7 f 13//7 15//7 14//7 f 16//8 14//8 15//8 f 15//8 17//8 16//8 f 18//9 16//9 17//9 f 17//9 19//9 18//9 f 20//10 18//10 19//10 f 19//10 21//10 20//10 f 22//11 20//11 21//11 f 21//11 23//11 22//11 f 24//12 22//12 23//12 f 23//12 25//12 24//12 f 26//13 24//13 25//13 f 25//13 27//13 26//13 f 28//14 26//14 27//14 f 27//14 29//14 28//14 f 30//15 28//15 29//15 f 29//15 31//15 30//15 f 32//16 30//16 31//16 f 31//16 33//16 32//16 f 34//17 32//17 33//17 f 33//17 35//17 34//17 f 36//18 34//18 35//18 f 35//19 37//19 36//19 f 38//20 36//20 37//20 f 37//21 38//21 38//21 f 39//22 2//22 1//22 f 40//23 39//23 1//23 f 1//23 4//23 40//23 f 41//24 40//24 4//24 f 4//24 6//24 41//24 f 42//25 41//25 6//25 f 6//25 8//25 42//25 f 43//26 42//26 8//26 f 8//26 10//26 43//26 f 44//27 43//27 10//27 f 10//27 12//27 44//27 f 45//28 44//28 12//28 f 12//28 14//28 45//28 f 46//29 45//29 14//29 f 14//29 16//29 46//29 f 47//30 46//30 16//30 f 16//30 18//30 47//30 f 48//31 47//31 18//31 f 18//31 20//31 48//31 f 49//32 48//32 20//32 f 20//32 22//32 49//32 f 50//33 49//33 22//33 f 22//33 24//33 50//33 f 51//34 50//34 24//34 f 24//34 26//34 51//34 f 52//35 51//35 26//35 f 26//35 28//35 52//35 f 53//36 52//36 28//36 f 28//36 30//36 53//36 f 54//37 53//37 30//37 f 30//38 32//38 54//38 f 55//39 54//39 32//39 f 32//39 34//39 55//39 f 56//40 55//40 34//40 f 34//40 36//40 56//40 f 57//20 56//20 36//20 f 36//21 38//21 57//21 f 58//41 2//41 39//41 f 59//42 58//42 39//42 f 39//42 40//42 59//42 f 60//43 59//43 40//43 f 40//43 41//43 60//43 f 61//44 60//44 41//44 f 41//44 42//44 61//44 f 62//45 61//45 42//45 f 42//45 43//45 62//45 f 63//46 62//46 43//46 f 43//46 44//46 63//46 f 64//47 63//47 44//47 f 44//47 45//47 64//47 f 65//48 64//48 45//48 f 45//48 46//48 65//48 f 66//49 65//49 46//49 f 46//49 47//49 66//49 f 67//50 66//50 47//50 f 47//50 48//50 67//50 f 68//51 67//51 48//51 f 48//51 49//51 68//51 f 69//52 68//52 49//52 f 49//52 50//52 69//52 f 70//53 69//53 50//53 f 50//53 51//53 70//53 f 71//54 70//54 51//54 f 51//54 52//54 71//54 f 72//55 71//55 52//55 f 52//55 53//55 72//55 f 73//56 72//56 53//56 f 53//56 54//56 73//56 f 74//57 73//57 54//57 f 54//57 55//57 74//57 f 75//58 74//58 55//58 f 55//59 56//59 75//59 f 57//20 75//20 56//20 f 56//21 57//21 57//21 f 76//60 2//60 58//60 f 77//61 76//61 58//61 f 58//61 59//61 77//61 f 78//62 77//62 59//62 f 59//62 60//62 78//62 f 79//63 78//63 60//63 f 60//63 61//63 79//63 f 80//64 79//64 61//64 f 61//64 62//64 80//64 f 81//65 80//65 62//65 f 62//65 63//65 81//65 f 82//66 81//66 63//66 f 63//66 64//66 82//66 f 83//67 82//67 64//67 f 64//67 65//67 83//67 f 84//68 83//68 65//68 f 65//68 66//68 84//68 f 85//69 84//69 66//69 f 66//69 67//69 85//69 f 86//70 85//70 67//70 f 67//70 68//70 86//70 f 87//71 86//71 68//71 f 68//71 69//71 87//71 f 88//72 87//72 69//72 f 69//72 70//72 88//72 f 89//73 88//73 70//73 f 70//73 71//73 89//73 f 90//74 89//74 71//74 f 71//74 72//74 90//74 f 91//75 90//75 72//75 f 72//75 73//75 91//75 f 92//76 91//76 73//76 f 73//76 74//76 92//76 f 93//77 92//77 74//77 f 74//78 75//78 93//78 f 57//20 93//20 75//20 f 75//21 57//21 57//21 f 94//79 2//79 76//79 f 95//80 94//80 76//80 f 76//80 77//80 95//80 f 96//81 95//81 77//81 f 77//81 78//81 96//81 f 97//82 96//82 78//82 f 78//82 79//82 97//82 f 98//83 97//83 79//83 f 79//83 80//83 98//83 f 99//84 98//84 80//84 f 80//84 81//84 99//84 f 100//85 99//85 81//85 f 81//85 82//85 100//85 f 101//86 100//86 82//86 f 82//86 83//86 101//86 f 102//87 101//87 83//87 f 83//87 84//87 102//87 f 103//88 102//88 84//88 f 84//88 85//88 103//88 f 104//89 103//89 85//89 f 85//89 86//89 104//89 f 105//90 104//90 86//90 f 86//90 87//90 105//90 f 106//91 105//91 87//91 f 87//91 88//91 106//91 f 107//92 106//92 88//92 f 88//92 89//92 107//92 f 108//93 107//93 89//93 f 89//94 90//94 108//94 f 109//95 108//95 90//95 f 90//95 91//95 109//95 f 110//96 109//96 91//96 f 91//96 92//96 110//96 f 111//97 110//97 92//97 f 92//97 93//97 111//97 f 38//20 111//20 93//20 f 93//21 57//21 38//21 f 3//98 2//98 94//98 f 5//99 3//99 94//99 f 94//99 95//99 5//99 f 7//100 5//100 95//100 f 95//100 96//100 7//100 f 9//101 7//101 96//101 f 96//101 97//101 9//101 f 11//102 9//102 97//102 f 97//102 98//102 11//102 f 13//103 11//103 98//103 f 98//103 99//103 13//103 f 15//104 13//104 99//104 f 99//104 100//104 15//104 f 17//105 15//105 100//105 f 100//105 101//105 17//105 f 19//106 17//106 101//106 f 101//106 102//106 19//106 f 21//107 19//107 102//107 f 102//107 103//107 21//107 f 23//108 21//108 103//108 f 103//108 104//108 23//108 f 25//109 23//109 104//109 f 104//109 105//109 25//109 f 27//110 25//110 105//110 f 105//110 106//110 27//110 f 29//111 27//111 106//111 f 106//111 107//111 29//111 f 31//112 29//112 107//112 f 107//112 108//112 31//112 f 33//113 31//113 108//113 f 108//113 109//113 33//113 f 35//114 33//114 109//114 f 109//114 110//114 35//114 f 37//115 35//115 110//115 f 110//116 111//116 37//116 f 38//20 37//20 111//20 f 111//21 38//21 38//21 # 222 faces bottle.mtl # 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware # File Created: 30.08.2011 17:56:16 newmtl 01___Default Ns 10.0000 Ni 1.5000 d 1.0000 Tr 0.0000 Tf 1.0000 1.0000 1.0000 illum 2 Ka 0.6353 0.5882 1.0000 Kd 0.6353 0.5882 1.0000 Ks 0.0000 0.0000 0.0000 Ke 0.0000 0.0000 0.0000 main.cpp //Include and link appropriate libraries and headers// #pragma comment(lib, "d3d11.lib") #pragma comment(lib, "d3dx11.lib") #pragma comment(lib, "d3dx10.lib") #pragma comment (lib, "D3D10_1.lib") #pragma comment (lib, "DXGI.lib") #pragma comment (lib, "D2D1.lib") #pragma comment (lib, "dwrite.lib") #pragma comment (lib, "dinput8.lib") #pragma comment (lib, "dxguid.lib") #include <windows.h> #include <d3d11.h> #include <d3dx11.h> #include <d3dx10.h> #include <xnamath.h> #include <D3D10_1.h> #include <DXGI.h> #include <D2D1.h> #include <sstream> #include <dwrite.h> #include <dinput.h> #include <vector> #include <fstream> #include <istream> //Global Declarations - Interfaces// IDXGISwapChain* SwapChain; ID3D11Device* d3d11Device; ID3D11DeviceContext* d3d11DevCon; ID3D11RenderTargetView* renderTargetView; ID3D11DepthStencilView* depthStencilView; ID3D11Texture2D* depthStencilBuffer; ID3D11VertexShader* VS; ID3D11PixelShader* PS; ID3D11PixelShader* D2D_PS; ID3D10Blob* D2D_PS_Buffer; ID3D10Blob* VS_Buffer; ID3D10Blob* PS_Buffer; ID3D11InputLayout* vertLayout; ID3D11Buffer* cbPerObjectBuffer; ID3D11BlendState* d2dTransparency; ID3D11RasterizerState* CCWcullMode; ID3D11RasterizerState* CWcullMode; ID3D11SamplerState* CubesTexSamplerState; ID3D11Buffer* cbPerFrameBuffer; ID3D10Device1 *d3d101Device; IDXGIKeyedMutex *keyedMutex11; IDXGIKeyedMutex *keyedMutex10; ID2D1RenderTarget *D2DRenderTarget; ID2D1SolidColorBrush *Brush; ID3D11Texture2D *BackBuffer11; ID3D11Texture2D *sharedTex11; ID3D11Buffer *d2dVertBuffer; ID3D11Buffer *d2dIndexBuffer; ID3D11ShaderResourceView *d2dTexture; IDWriteFactory *DWriteFactory; IDWriteTextFormat *TextFormat; IDirectInputDevice8* DIKeyboard; IDirectInputDevice8* DIMouse; ID3D11Buffer* sphereIndexBuffer; ID3D11Buffer* sphereVertBuffer; ID3D11VertexShader* SKYMAP_VS; ID3D11PixelShader* SKYMAP_PS; ID3D10Blob* SKYMAP_VS_Buffer; ID3D10Blob* SKYMAP_PS_Buffer; ID3D11ShaderResourceView* smrv; ID3D11DepthStencilState* DSLessEqual; ID3D11RasterizerState* RSCullNone; ID3D11BlendState* Transparency; //Mesh variables. Each loaded mesh will need its own set of these ID3D11Buffer* meshVertBuff; ID3D11Buffer* meshIndexBuff; ///////////////**************new**************//////////////////// std::vector<XMFLOAT3> groundVertPosArray; std::vector<DWORD> groundVertIndexArray; ///////////////**************new**************//////////////////// XMMATRIX meshWorld; int meshSubsets = 0; std::vector<int> meshSubsetIndexStart; std::vector<int> meshSubsetTexture; ///////////////**************new**************//////////////////// //Bottle mesh variables// ID3D11Buffer* bottleVertBuff; ID3D11Buffer* bottleIndexBuff; std::vector<XMFLOAT3> bottleVertPosArray; std::vector<DWORD> bottleVertIndexArray; int bottleSubsets = 0; std::vector<int> bottleSubsetIndexStart; std::vector<int> bottleSubsetTexture; XMMATRIX bottleWorld[20]; int* bottleHit = new int[20]; int numBottles = 20; ///////////////**************new**************//////////////////// //Textures and material variables, used for all mesh's loaded std::vector<ID3D11ShaderResourceView*> meshSRV; std::vector<std::wstring> textureNameArray; std::wstring printText; //Global Declarations - Others// LPCTSTR WndClassName = L"firstwindow"; HWND hwnd = NULL; HRESULT hr; int Width = 1920; int Height = 1200; DIMOUSESTATE mouseLastState; LPDIRECTINPUT8 DirectInput; float rotx = 0; float rotz = 0; float scaleX = 1.0f; float scaleY = 1.0f; XMMATRIX Rotationx; XMMATRIX Rotationz; XMMATRIX Rotationy; XMMATRIX WVP; XMMATRIX camView; XMMATRIX camProjection; XMMATRIX d2dWorld; XMVECTOR camPosition; XMVECTOR camTarget; XMVECTOR camUp; XMVECTOR DefaultForward = XMVectorSet(0.0f,0.0f,1.0f, 0.0f); XMVECTOR DefaultRight = XMVectorSet(1.0f,0.0f,0.0f, 0.0f); XMVECTOR camForward = XMVectorSet(0.0f,0.0f,1.0f, 0.0f); XMVECTOR camRight = XMVectorSet(1.0f,0.0f,0.0f, 0.0f); XMMATRIX camRotationMatrix; float moveLeftRight = 0.0f; float moveBackForward = 0.0f; float camYaw = 0.0f; float camPitch = 0.0f; int NumSphereVertices; int NumSphereFaces; XMMATRIX sphereWorld; XMMATRIX Rotation; XMMATRIX Scale; XMMATRIX Translation; ///////////////**************new**************//////////////////// bool isShoot = false; int ClientWidth = 0; int ClientHeight = 0; int score = 0; float pickedDist = 0.0f; ///////////////**************new**************//////////////////// float rot = 0.01f; double countsPerSecond = 0.0; __int64 CounterStart = 0; int frameCount = 0; int fps = 0; __int64 frameTimeOld = 0; double frameTime; //Function Prototypes// bool InitializeDirect3d11App(HINSTANCE hInstance); void CleanUp(); bool InitScene(); void DrawScene(); bool InitD2D_D3D101_DWrite(IDXGIAdapter1 *Adapter); void InitD2DScreenTexture(); void UpdateScene(double time); void UpdateCamera(); void RenderText(std::wstring text, int inInt); void StartTimer(); double GetTime(); double GetFrameTime(); bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed); int messageloop(); bool InitDirectInput(HINSTANCE hInstance); void DetectInput(double time); void CreateSphere(int LatLines, int LongLines); ///////////////**************new**************//////////////////// void pickRayVector(float mouseX, float mouseY, XMVECTOR& pickRayInWorldSpacePos, XMVECTOR& pickRayInWorldSpaceDir); float pick(XMVECTOR pickRayInWorldSpacePos, XMVECTOR pickRayInWorldSpaceDir, std::vector<XMFLOAT3>& vertPosArray, std::vector<DWORD>& indexPosArray, XMMATRIX& worldSpace); bool PointInTriangle(XMVECTOR& triV1, XMVECTOR& triV2, XMVECTOR& triV3, XMVECTOR& point ); ///////////////**************new**************//////////////////// LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); //Create effects constant buffer's structure// struct cbPerObject { XMMATRIX WVP; XMMATRIX World; //These will be used for the pixel shader XMFLOAT4 difColor; BOOL hasTexture; //Because of HLSL structure packing, we will use windows BOOL //instead of bool because HLSL packs things into 4 bytes, and //bool is only one byte, where BOOL is 4 bytes BOOL hasNormMap; }; cbPerObject cbPerObj; //Create material structure struct SurfaceMaterial { std::wstring matName; XMFLOAT4 difColor; int texArrayIndex; int normMapTexArrayIndex; bool hasNormMap; bool hasTexture; bool transparent; }; std::vector<SurfaceMaterial> material; //Define LoadObjModel function after we create surfaceMaterial structure bool LoadObjModel(std::wstring filename, //.obj filename ID3D11Buffer** vertBuff, //mesh vertex buffer ID3D11Buffer** indexBuff, //mesh index buffer std::vector<int>& subsetIndexStart, //start index of each subset std::vector<int>& subsetMaterialArray, //index value of material for each subset std::vector<SurfaceMaterial>& material, //vector of material structures int& subsetCount, //Number of subsets in mesh bool isRHCoordSys, //true if model was created in right hand coord system bool computeNormals, //true to compute the normals, false to use the files normals ///////////////**************new**************//////////////////// std::vector<XMFLOAT3>& vertPosArray, //Used for CPU to do calculations on the Geometry std::vector<DWORD>& vertIndexArray); //Also used for CPU caculations on geometry ///////////////**************new**************//////////////////// struct Light { Light() { ZeroMemory(this, sizeof(Light)); } XMFLOAT3 pos; float range; XMFLOAT3 dir; float cone; XMFLOAT3 att; float pad2; XMFLOAT4 ambient; XMFLOAT4 diffuse; }; Light light; struct cbPerFrame { Light light; }; cbPerFrame constbuffPerFrame; struct Vertex //Overloaded Vertex Structure { Vertex(){} Vertex(float x, float y, float z, float u, float v, float nx, float ny, float nz, float tx, float ty, float tz) : pos(x,y,z), texCoord(u, v), normal(nx, ny, nz), tangent(tx, ty, tz){} XMFLOAT3 pos; XMFLOAT2 texCoord; XMFLOAT3 normal; XMFLOAT3 tangent; XMFLOAT3 biTangent; }; D3D11_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0}, { "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 32, D3D11_INPUT_PER_VERTEX_DATA, 0} }; UINT numElements = ARRAYSIZE(layout); int WINAPI WinMain(HINSTANCE hInstance, //Main windows function HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { if(!InitializeWindow(hInstance, nShowCmd, Width, Height, true)) { MessageBox(0, L"Window Initialization - Failed", L"Error", MB_OK); return 0; } if(!InitializeDirect3d11App(hInstance)) //Initialize Direct3D { MessageBox(0, L"Direct3D Initialization - Failed", L"Error", MB_OK); return 0; } if(!InitScene()) //Initialize our scene { MessageBox(0, L"Scene Initialization - Failed", L"Error", MB_OK); return 0; } if(!InitDirectInput(hInstance)) { MessageBox(0, L"Direct Input Initialization - Failed", L"Error", MB_OK); return 0; } messageloop(); CleanUp(); return 0; } bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed) { typedef struct _WNDCLASS { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; } WNDCLASS; WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = NULL; wc.cbWndExtra = NULL; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = NULL; wc.lpszClassName = WndClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if (!RegisterClassEx(&wc)) { MessageBox(NULL, L"Error registering class", L"Error", MB_OK | MB_ICONERROR); return 1; } hwnd = CreateWindowEx( NULL, WndClassName, L"Lesson 4 - Begin Drawing", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, NULL, NULL, hInstance, NULL ); if (!hwnd) { MessageBox(NULL, L"Error creating window", L"Error", MB_OK | MB_ICONERROR); return 1; } ShowWindow(hwnd, ShowWnd); UpdateWindow(hwnd); return true; } bool InitializeDirect3d11App(HINSTANCE hInstance) { //Describe our SwapChain Buffer DXGI_MODE_DESC bufferDesc; ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC)); bufferDesc.Width = Width; bufferDesc.Height = Height; bufferDesc.RefreshRate.Numerator = 60; bufferDesc.RefreshRate.Denominator = 1; bufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; //Describe our SwapChain DXGI_SWAP_CHAIN_DESC swapChainDesc; ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC)); swapChainDesc.BufferDesc = bufferDesc; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.BufferCount = 1; swapChainDesc.OutputWindow = hwnd; ///////////////**************new**************//////////////////// swapChainDesc.Windowed = true; ///////////////**************new**************//////////////////// swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; // Create DXGI factory to enumerate adapters/////////////////////////////////////////////////////////////////////////// IDXGIFactory1 *DXGIFactory; HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&DXGIFactory); // Use the first adapter IDXGIAdapter1 *Adapter; hr = DXGIFactory->EnumAdapters1(0, &Adapter); DXGIFactory->Release(); //Create our Direct3D 11 Device and SwapChain////////////////////////////////////////////////////////////////////////// hr = D3D11CreateDeviceAndSwapChain(Adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, D3D11_CREATE_DEVICE_BGRA_SUPPORT, NULL, NULL, D3D11_SDK_VERSION, &swapChainDesc, &SwapChain, &d3d11Device, NULL, &d3d11DevCon); //Initialize Direct2D, Direct3D 10.1, DirectWrite InitD2D_D3D101_DWrite(Adapter); //Release the Adapter interface Adapter->Release(); //Create our BackBuffer and Render Target hr = SwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), (void**)&BackBuffer11 ); hr = d3d11Device->CreateRenderTargetView( BackBuffer11, NULL, &renderTargetView ); //Describe our Depth/Stencil Buffer D3D11_TEXTURE2D_DESC depthStencilDesc; depthStencilDesc.Width = Width; depthStencilDesc.Height = Height; depthStencilDesc.MipLevels = 1; depthStencilDesc.ArraySize = 1; depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depthStencilDesc.SampleDesc.Count = 1; depthStencilDesc.SampleDesc.Quality = 0; depthStencilDesc.Usage = D3D11_USAGE_DEFAULT; depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; depthStencilDesc.CPUAccessFlags = 0; depthStencilDesc.MiscFlags = 0; //Create the Depth/Stencil View d3d11Device->CreateTexture2D(&depthStencilDesc, NULL, &depthStencilBuffer); d3d11Device->CreateDepthStencilView(depthStencilBuffer, NULL, &depthStencilView); return true; } bool InitD2D_D3D101_DWrite(IDXGIAdapter1 *Adapter) { //Create our Direc3D 10.1 Device/////////////////////////////////////////////////////////////////////////////////////// hr = D3D10CreateDevice1(Adapter, D3D10_DRIVER_TYPE_HARDWARE, NULL,D3D10_CREATE_DEVICE_BGRA_SUPPORT, D3D10_FEATURE_LEVEL_9_3, D3D10_1_SDK_VERSION, &d3d101Device ); //Create Shared Texture that Direct3D 10.1 will render on////////////////////////////////////////////////////////////// D3D11_TEXTURE2D_DESC sharedTexDesc; ZeroMemory(&sharedTexDesc, sizeof(sharedTexDesc)); sharedTexDesc.Width = Width; sharedTexDesc.Height = Height; sharedTexDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; sharedTexDesc.MipLevels = 1; sharedTexDesc.ArraySize = 1; sharedTexDesc.SampleDesc.Count = 1; sharedTexDesc.Usage = D3D11_USAGE_DEFAULT; sharedTexDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; sharedTexDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; hr = d3d11Device->CreateTexture2D(&sharedTexDesc, NULL, &sharedTex11); // Get the keyed mutex for the shared texture (for D3D11)/////////////////////////////////////////////////////////////// hr = sharedTex11->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)&keyedMutex11); // Get the shared handle needed to open the shared texture in D3D10.1/////////////////////////////////////////////////// IDXGIResource *sharedResource10; HANDLE sharedHandle10; hr = sharedTex11->QueryInterface(__uuidof(IDXGIResource), (void**)&sharedResource10); hr = sharedResource10->GetSharedHandle(&sharedHandle10); sharedResource10->Release(); // Open the surface for the shared texture in D3D10.1/////////////////////////////////////////////////////////////////// IDXGISurface1 *sharedSurface10; hr = d3d101Device->OpenSharedResource(sharedHandle10, __uuidof(IDXGISurface1), (void**)(&sharedSurface10)); hr = sharedSurface10->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)&keyedMutex10); // Create D2D factory/////////////////////////////////////////////////////////////////////////////////////////////////// ID2D1Factory *D2DFactory; hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), (void**)&D2DFactory); D2D1_RENDER_TARGET_PROPERTIES renderTargetProperties; ZeroMemory(&renderTargetProperties, sizeof(renderTargetProperties)); renderTargetProperties.type = D2D1_RENDER_TARGET_TYPE_HARDWARE; renderTargetProperties.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED); hr = D2DFactory->CreateDxgiSurfaceRenderTarget(sharedSurface10, &renderTargetProperties, &D2DRenderTarget); sharedSurface10->Release(); D2DFactory->Release(); // Create a solid color brush to draw something with hr = D2DRenderTarget->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), &Brush); //DirectWrite/////////////////////////////////////////////////////////////////////////////////////////////////////////// hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&DWriteFactory)); hr = DWriteFactory->CreateTextFormat( L"Script", NULL, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 24.0f, L"en-us", &TextFormat ); hr = TextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); hr = TextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR); d3d101Device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_POINTLIST); return true; } bool InitDirectInput(HINSTANCE hInstance) { hr = DirectInput8Create(hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&DirectInput, NULL); hr = DirectInput->CreateDevice(GUID_SysKeyboard, &DIKeyboard, NULL); hr = DirectInput->CreateDevice(GUID_SysMouse, &DIMouse, NULL); hr = DIKeyboard->SetDataFormat(&c_dfDIKeyboard); hr = DIKeyboard->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); hr = DIMouse->SetDataFormat(&c_dfDIMouse); ///////////////**************new**************//////////////////// hr = DIMouse->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_NOWINKEY | DISCL_FOREGROUND); ///////////////**************new**************//////////////////// return true; } void UpdateCamera() { camRotationMatrix = XMMatrixRotationRollPitchYaw(camPitch, camYaw, 0); camTarget = XMVector3TransformCoord(DefaultForward, camRotationMatrix ); camTarget = XMVector3Normalize(camTarget); XMMATRIX RotateYTempMatrix; RotateYTempMatrix = XMMatrixRotationY(camYaw); //walk camRight = XMVector3TransformCoord(DefaultRight, RotateYTempMatrix); camUp = XMVector3TransformCoord(camUp, RotateYTempMatrix); camForward = XMVector3TransformCoord(DefaultForward, RotateYTempMatrix); camPosition += moveLeftRight*camRight; camPosition += moveBackForward*camForward; moveLeftRight = 0.0f; moveBackForward = 0.0f; camTarget = camPosition + camTarget; camView = XMMatrixLookAtLH( camPosition, camTarget, camUp ); } void DetectInput(double time) { DIMOUSESTATE mouseCurrState; BYTE keyboardState[256]; DIKeyboard->Acquire(); DIMouse->Acquire(); DIMouse->GetDeviceState(sizeof(DIMOUSESTATE), &mouseCurrState); DIKeyboard->GetDeviceState(sizeof(keyboardState),(LPVOID)&keyboardState); if(keyboardState[DIK_ESCAPE] & 0x80) PostMessage(hwnd, WM_DESTROY, 0, 0); float speed = 10.0f * time; if(keyboardState[DIK_A] & 0x80) { moveLeftRight -= speed; } if(keyboardState[DIK_D] & 0x80) { moveLeftRight += speed; } if(keyboardState[DIK_W] & 0x80) { moveBackForward += speed; } if(keyboardState[DIK_S] & 0x80) { moveBackForward -= speed; } ///////////////**************new**************//////////////////// if(mouseCurrState.rgbButtons[0]) { if(isShoot == false) { POINT mousePos; GetCursorPos(&mousePos); ScreenToClient(hwnd, &mousePos); int mousex = mousePos.x; int mousey = mousePos.y; float tempDist; float closestDist = FLT_MAX; int hitIndex; XMVECTOR prwsPos, prwsDir; pickRayVector(mousex, mousey, prwsPos, prwsDir); for(int i = 0; i < numBottles; i++) { if(bottleHit[i] == 0) //No need to check bottles already hit { tempDist = pick(prwsPos, prwsDir, bottleVertPosArray, bottleVertIndexArray, bottleWorld[i]); if(tempDist < closestDist) { closestDist = tempDist; hitIndex = i; } } } if(closestDist < FLT_MAX) { bottleHit[hitIndex] = 1; pickedDist = closestDist; score++; } isShoot = true; } } if(!mouseCurrState.rgbButtons[0]) { isShoot = false; } ///////////////**************new**************//////////////////// if((mouseCurrState.lX != mouseLastState.lX) || (mouseCurrState.lY != mouseLastState.lY)) { camYaw += mouseLastState.lX * 0.001f; camPitch += mouseCurrState.lY * 0.001f; mouseLastState = mouseCurrState; } UpdateCamera(); return; } void CleanUp() { SwapChain->SetFullscreenState(false, NULL); PostMessage(hwnd, WM_DESTROY, 0, 0); //Release the COM Objects we created SwapChain->Release(); d3d11Device->Release(); d3d11DevCon->Release(); renderTargetView->Release(); VS->Release(); PS->Release(); VS_Buffer->Release(); PS_Buffer->Release(); vertLayout->Release(); depthStencilView->Release(); depthStencilBuffer->Release(); cbPerObjectBuffer->Release(); Transparency->Release(); CCWcullMode->Release(); CWcullMode->Release(); d3d101Device->Release(); keyedMutex11->Release(); keyedMutex10->Release(); D2DRenderTarget->Release(); Brush->Release(); BackBuffer11->Release(); sharedTex11->Release(); DWriteFactory->Release(); TextFormat->Release(); d2dTexture->Release(); cbPerFrameBuffer->Release(); DIKeyboard->Unacquire(); DIMouse->Unacquire(); DirectInput->Release(); sphereIndexBuffer->Release(); sphereVertBuffer->Release(); SKYMAP_VS->Release(); SKYMAP_PS->Release(); SKYMAP_VS_Buffer->Release(); SKYMAP_PS_Buffer->Release(); smrv->Release(); DSLessEqual->Release(); RSCullNone->Release(); meshVertBuff->Release(); meshIndexBuff->Release(); ///////////////**************new**************//////////////////// bottleVertBuff->Release(); bottleIndexBuff->Release(); ///////////////**************new**************//////////////////// } ///////////////**************new**************//////////////////// void pickRayVector(float mouseX, float mouseY, XMVECTOR& pickRayInWorldSpacePos, XMVECTOR& pickRayInWorldSpaceDir) { XMVECTOR pickRayInViewSpaceDir = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); XMVECTOR pickRayInViewSpacePos = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); float PRVecX, PRVecY, PRVecZ; //Transform 2D pick position on screen space to 3D ray in View space PRVecX = ((( 2.0f * mouseX) / ClientWidth ) - 1 ) / camProjection(0,0); PRVecY = -((( 2.0f * mouseY) / ClientHeight) - 1 ) / camProjection(1,1); PRVecZ = 1.0f; //View space's Z direction ranges from 0 to 1, so we set 1 since the ray goes "into" the screen pickRayInViewSpaceDir = XMVectorSet(PRVecX, PRVecY, PRVecZ, 0.0f); //Uncomment this line if you want to use the center of the screen (client area) //to be the point that creates the picking ray (eg. first person shooter) //pickRayInViewSpaceDir = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f); // Transform 3D Ray from View space to 3D ray in World space XMMATRIX pickRayToWorldSpaceMatrix; XMVECTOR matInvDeter; //We don't use this, but the xna matrix inverse function requires the first parameter to not be null pickRayToWorldSpaceMatrix = XMMatrixInverse(&matInvDeter, camView); //Inverse of View Space matrix is World space matrix pickRayInWorldSpacePos = XMVector3TransformCoord(pickRayInViewSpacePos, pickRayToWorldSpaceMatrix); pickRayInWorldSpaceDir = XMVector3TransformNormal(pickRayInViewSpaceDir, pickRayToWorldSpaceMatrix); } float pick(XMVECTOR pickRayInWorldSpacePos, XMVECTOR pickRayInWorldSpaceDir, std::vector<XMFLOAT3>& vertPosArray, std::vector<DWORD>& indexPosArray, XMMATRIX& worldSpace) { //Loop through each triangle in the object for(int i = 0; i < indexPosArray.size()/3; i++) { //Triangle's vertices V1, V2, V3 XMVECTOR tri1V1 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); XMVECTOR tri1V2 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); XMVECTOR tri1V3 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); //Temporary 3d floats for each vertex XMFLOAT3 tV1, tV2, tV3; //Get triangle tV1 = vertPosArray[indexPosArray[(i*3)+0]]; tV2 = vertPosArray[indexPosArray[(i*3)+1]]; tV3 = vertPosArray[indexPosArray[(i*3)+2]]; tri1V1 = XMVectorSet(tV1.x, tV1.y, tV1.z, 0.0f); tri1V2 = XMVectorSet(tV2.x, tV2.y, tV2.z, 0.0f); tri1V3 = XMVectorSet(tV3.x, tV3.y, tV3.z, 0.0f); //Transform the vertices to world space tri1V1 = XMVector3TransformCoord(tri1V1, worldSpace); tri1V2 = XMVector3TransformCoord(tri1V2, worldSpace); tri1V3 = XMVector3TransformCoord(tri1V3, worldSpace); //Find the normal using U, V coordinates (two edges) XMVECTOR U = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); XMVECTOR V = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); XMVECTOR faceNormal = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); U = tri1V2 - tri1V1; V = tri1V3 - tri1V1; //Compute face normal by crossing U, V faceNormal = XMVector3Cross(U, V); faceNormal = XMVector3Normalize(faceNormal); //Calculate a point on the triangle for the plane equation XMVECTOR triPoint = tri1V1; //Get plane equation ("Ax + By + Cz + D = 0") Variables float tri1A = XMVectorGetX(faceNormal); float tri1B = XMVectorGetY(faceNormal); float tri1C = XMVectorGetZ(faceNormal); float tri1D = (-tri1A*XMVectorGetX(triPoint) - tri1B*XMVectorGetY(triPoint) - tri1C*XMVectorGetZ(triPoint)); //Now we find where (on the ray) the ray intersects with the triangles plane float ep1, ep2, t = 0.0f; float planeIntersectX, planeIntersectY, planeIntersectZ = 0.0f; XMVECTOR pointInPlane = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); ep1 = (XMVectorGetX(pickRayInWorldSpacePos) * tri1A) + (XMVectorGetY(pickRayInWorldSpacePos) * tri1B) + (XMVectorGetZ(pickRayInWorldSpacePos) * tri1C); ep2 = (XMVectorGetX(pickRayInWorldSpaceDir) * tri1A) + (XMVectorGetY(pickRayInWorldSpaceDir) * tri1B) + (XMVectorGetZ(pickRayInWorldSpaceDir) * tri1C); //Make sure there are no divide-by-zeros if(ep2 != 0.0f) t = -(ep1 + tri1D)/(ep2); if(t > 0.0f) //Make sure you don't pick objects behind the camera { //Get the point on the plane planeIntersectX = XMVectorGetX(pickRayInWorldSpacePos) + XMVectorGetX(pickRayInWorldSpaceDir) * t; planeIntersectY = XMVectorGetY(pickRayInWorldSpacePos) + XMVectorGetY(pickRayInWorldSpaceDir) * t; planeIntersectZ = XMVectorGetZ(pickRayInWorldSpacePos) + XMVectorGetZ(pickRayInWorldSpaceDir) * t; pointInPlane = XMVectorSet(planeIntersectX, planeIntersectY, planeIntersectZ, 0.0f); //Call function to check if point is in the triangle if(PointInTriangle(tri1V1, tri1V2, tri1V3, pointInPlane)) { //Return the distance to the hit, so you can check all the other pickable objects in your scene //and choose whichever object is closest to the camera return t/2.0f; } } } //return the max float value (near infinity) if an object was not picked return FLT_MAX; } bool PointInTriangle(XMVECTOR& triV1, XMVECTOR& triV2, XMVECTOR& triV3, XMVECTOR& point ) { //To find out if the point is inside the triangle, we will check to see if the point //is on the correct side of each of the triangles edges. XMVECTOR cp1 = XMVector3Cross((triV3 - triV2), (point - triV2)); XMVECTOR cp2 = XMVector3Cross((triV3 - triV2), (triV1 - triV2)); if(XMVectorGetX(XMVector3Dot(cp1, cp2)) >= 0) { cp1 = XMVector3Cross((triV3 - triV1), (point - triV1)); cp2 = XMVector3Cross((triV3 - triV1), (triV2 - triV1)); if(XMVectorGetX(XMVector3Dot(cp1, cp2)) >= 0) { cp1 = XMVector3Cross((triV2 - triV1), (point - triV1)); cp2 = XMVector3Cross((triV2 - triV1), (triV3 - triV1)); if(XMVectorGetX(XMVector3Dot(cp1, cp2)) >= 0) { return true; } else return false; } else return false; } return false; } ///////////////**************new**************//////////////////// bool LoadObjModel(std::wstring filename, ID3D11Buffer** vertBuff, ID3D11Buffer** indexBuff, std::vector<int>& subsetIndexStart, std::vector<int>& subsetMaterialArray, std::vector<SurfaceMaterial>& material, int& subsetCount, bool isRHCoordSys, bool computeNormals, ///////////////**************new**************//////////////////// std::vector<XMFLOAT3>& vertPosArray, std::vector<DWORD>& vertIndexArray) ///////////////**************new**************//////////////////// { HRESULT hr = 0; std::wifstream fileIn (filename.c_str()); //Open file std::wstring meshMatLib; //String to hold our obj material library filename //Arrays to store our model's information std::vector<DWORD> indices; std::vector<XMFLOAT3> vertPos; std::vector<XMFLOAT3> vertNorm; std::vector<XMFLOAT2> vertTexCoord; std::vector<std::wstring> meshMaterials; //Vertex definition indices std::vector<int> vertPosIndex; std::vector<int> vertNormIndex; std::vector<int> vertTCIndex; //Make sure we have a default if no tex coords or normals are defined bool hasTexCoord = false; bool hasNorm = false; //Temp variables to store into vectors std::wstring meshMaterialsTemp; int vertPosIndexTemp; int vertNormIndexTemp; int vertTCIndexTemp; wchar_t checkChar; //The variable we will use to store one char from file at a time std::wstring face; //Holds the string containing our face vertices int vIndex = 0; //Keep track of our vertex index count int triangleCount = 0; //Total Triangles int totalVerts = 0; int meshTriangles = 0; //Check to see if the file was opened if (fileIn) { while(fileIn) { checkChar = fileIn.get(); //Get next char switch (checkChar) { case '#': checkChar = fileIn.get(); while(checkChar != '\n') checkChar = fileIn.get(); break; case 'v': //Get Vertex Descriptions checkChar = fileIn.get(); if(checkChar == ' ') //v - vert position { float vz, vy, vx; fileIn >> vx >> vy >> vz; //Store the next three types if(isRHCoordSys) //If model is from an RH Coord System vertPos.push_back(XMFLOAT3( vx, vy, vz * -1.0f)); //Invert the Z axis else vertPos.push_back(XMFLOAT3( vx, vy, vz)); } if(checkChar == 't') //vt - vert tex coords { float vtcu, vtcv; fileIn >> vtcu >> vtcv; //Store next two types if(isRHCoordSys) //If model is from an RH Coord System vertTexCoord.push_back(XMFLOAT2(vtcu, 1.0f-vtcv)); //Reverse the "v" axis else vertTexCoord.push_back(XMFLOAT2(vtcu, vtcv)); hasTexCoord = true; //We know the model uses texture coords } //Since we compute the normals later, we don't need to check for normals //In the file, but i'll do it here anyway if(checkChar == 'n') //vn - vert normal { float vnx, vny, vnz; fileIn >> vnx >> vny >> vnz; //Store next three types if(isRHCoordSys) //If model is from an RH Coord System vertNorm.push_back(XMFLOAT3( vnx, vny, vnz * -1.0f )); //Invert the Z axis else vertNorm.push_back(XMFLOAT3( vnx, vny, vnz )); hasNorm = true; //We know the model defines normals } break; //New group (Subset) case 'g': //g - defines a group checkChar = fileIn.get(); if(checkChar == ' ') { subsetIndexStart.push_back(vIndex); //Start index for this subset subsetCount++; } break; //Get Face Index case 'f': //f - defines the faces checkChar = fileIn.get(); if(checkChar == ' ') { face = L""; std::wstring VertDef; //Holds one vertex definition at a time triangleCount = 0; checkChar = fileIn.get(); while(checkChar != '\n') { face += checkChar; //Add the char to our face string checkChar = fileIn.get(); //Get the next Character if(checkChar == ' ') //If its a space... triangleCount++; //Increase our triangle count } //Check for space at the end of our face string if(face[face.length()-1] == ' ') triangleCount--; //Each space adds to our triangle count triangleCount -= 1; //Ever vertex in the face AFTER the first two are new faces std::wstringstream ss(face); if(face.length() > 0) { int firstVIndex, lastVIndex; //Holds the first and last vertice's index for(int i = 0; i < 3; ++i) //First three vertices (first triangle) { ss >> VertDef; //Get vertex definition (vPos/vTexCoord/vNorm) std::wstring vertPart; int whichPart = 0; //(vPos, vTexCoord, or vNorm) //Parse this string for(int j = 0; j < VertDef.length(); ++j) { if(VertDef[j] != '/') //If there is no divider "/", add a char to our vertPart vertPart += VertDef[j]; //If the current char is a divider "/", or its the last character in the string if(VertDef[j] == '/' || j == VertDef.length()-1) { std::wistringstream wstringToInt(vertPart); //Used to convert wstring to int if(whichPart == 0) //If vPos { wstringToInt >> vertPosIndexTemp; vertPosIndexTemp -= 1; //subtract one since c++ arrays start with 0, and obj start with 1 //Check to see if the vert pos was the only thing specified if(j == VertDef.length()-1) { vertNormIndexTemp = 0; vertTCIndexTemp = 0; } } else if(whichPart == 1) //If vTexCoord { if(vertPart != L"") //Check to see if there even is a tex coord { wstringToInt >> vertTCIndexTemp; vertTCIndexTemp -= 1; //subtract one since c++ arrays start with 0, and obj start with 1 } else //If there is no tex coord, make a default vertTCIndexTemp = 0; //If the cur. char is the second to last in the string, then //there must be no normal, so set a default normal if(j == VertDef.length()-1) vertNormIndexTemp = 0; } else if(whichPart == 2) //If vNorm { std::wistringstream wstringToInt(vertPart); wstringToInt >> vertNormIndexTemp; vertNormIndexTemp -= 1; //subtract one since c++ arrays start with 0, and obj start with 1 } vertPart = L""; //Get ready for next vertex part whichPart++; //Move on to next vertex part } } //Check to make sure there is at least one subset if(subsetCount == 0) { subsetIndexStart.push_back(vIndex); //Start index for this subset subsetCount++; } //Avoid duplicate vertices bool vertAlreadyExists = false; if(totalVerts >= 3) //Make sure we at least have one triangle to check { //Loop through all the vertices for(int iCheck = 0; iCheck < totalVerts; ++iCheck) { //If the vertex position and texture coordinate in memory are the same //As the vertex position and texture coordinate we just now got out //of the obj file, we will set this faces vertex index to the vertex's //index value in memory. This makes sure we don't create duplicate vertices if(vertPosIndexTemp == vertPosIndex[iCheck] && !vertAlreadyExists) { if(vertTCIndexTemp == vertTCIndex[iCheck]) { indices.push_back(iCheck); //Set index for this vertex vertAlreadyExists = true; //If we've made it here, the vertex already exists } } } } //If this vertex is not already in our vertex arrays, put it there if(!vertAlreadyExists) { vertPosIndex.push_back(vertPosIndexTemp); vertTCIndex.push_back(vertTCIndexTemp); vertNormIndex.push_back(vertNormIndexTemp); totalVerts++; //We created a new vertex indices.push_back(totalVerts-1); //Set index for this vertex } //If this is the very first vertex in the face, we need to //make sure the rest of the triangles use this vertex if(i == 0) { firstVIndex = indices[vIndex]; //The first vertex index of this FACE } //If this was the last vertex in the first triangle, we will make sure //the next triangle uses this one (eg. tri1(1,2,3) tri2(1,3,4) tri3(1,4,5)) if(i == 2) { lastVIndex = indices[vIndex]; //The last vertex index of this TRIANGLE } vIndex++; //Increment index count } meshTriangles++; //One triangle down //If there are more than three vertices in the face definition, we need to make sure //we convert the face to triangles. We created our first triangle above, now we will //create a new triangle for every new vertex in the face, using the very first vertex //of the face, and the last vertex from the triangle before the current triangle for(int l = 0; l < triangleCount-1; ++l) //Loop through the next vertices to create new triangles { //First vertex of this triangle (the very first vertex of the face too) indices.push_back(firstVIndex); //Set index for this vertex vIndex++; //Second Vertex of this triangle (the last vertex used in the tri before this one) indices.push_back(lastVIndex); //Set index for this vertex vIndex++; //Get the third vertex for this triangle ss >> VertDef; std::wstring vertPart; int whichPart = 0; //Parse this string (same as above) for(int j = 0; j < VertDef.length(); ++j) { if(VertDef[j] != '/') vertPart += VertDef[j]; if(VertDef[j] == '/' || j == VertDef.length()-1) { std::wistringstream wstringToInt(vertPart); if(whichPart == 0) { wstringToInt >> vertPosIndexTemp; vertPosIndexTemp -= 1; //Check to see if the vert pos was the only thing specified if(j == VertDef.length()-1) { vertTCIndexTemp = 0; vertNormIndexTemp = 0; } } else if(whichPart == 1) { if(vertPart != L"") { wstringToInt >> vertTCIndexTemp; vertTCIndexTemp -= 1; } else vertTCIndexTemp = 0; if(j == VertDef.length()-1) vertNormIndexTemp = 0; } else if(whichPart == 2) { std::wistringstream wstringToInt(vertPart); wstringToInt >> vertNormIndexTemp; vertNormIndexTemp -= 1; } vertPart = L""; whichPart++; } } //Check for duplicate vertices bool vertAlreadyExists = false; if(totalVerts >= 3) //Make sure we at least have one triangle to check { for(int iCheck = 0; iCheck < totalVerts; ++iCheck) { if(vertPosIndexTemp == vertPosIndex[iCheck] && !vertAlreadyExists) { if(vertTCIndexTemp == vertTCIndex[iCheck]) { indices.push_back(iCheck); //Set index for this vertex vertAlreadyExists = true; //If we've made it here, the vertex already exists } } } } if(!vertAlreadyExists) { vertPosIndex.push_back(vertPosIndexTemp); vertTCIndex.push_back(vertTCIndexTemp); vertNormIndex.push_back(vertNormIndexTemp); totalVerts++; //New vertex created, add to total verts indices.push_back(totalVerts-1); //Set index for this vertex } //Set the second vertex for the next triangle to the last vertex we got lastVIndex = indices[vIndex]; //The last vertex index of this TRIANGLE meshTriangles++; //New triangle defined vIndex++; } } } break; case 'm': //mtllib - material library filename checkChar = fileIn.get(); if(checkChar == 't') { checkChar = fileIn.get(); if(checkChar == 'l') { checkChar = fileIn.get(); if(checkChar == 'l') { checkChar = fileIn.get(); if(checkChar == 'i') { checkChar = fileIn.get(); if(checkChar == 'b') { checkChar = fileIn.get(); if(checkChar == ' ') { //Store the material libraries file name fileIn >> meshMatLib; } } } } } } break; case 'u': //usemtl - which material to use checkChar = fileIn.get(); if(checkChar == 's') { checkChar = fileIn.get(); if(checkChar == 'e') { checkChar = fileIn.get(); if(checkChar == 'm') { checkChar = fileIn.get(); if(checkChar == 't') { checkChar = fileIn.get(); if(checkChar == 'l') { checkChar = fileIn.get(); if(checkChar == ' ') { meshMaterialsTemp = L""; //Make sure this is cleared fileIn >> meshMaterialsTemp; //Get next type (string) meshMaterials.push_back(meshMaterialsTemp); } } } } } } break; default: break; } } } else //If we could not open the file { SwapChain->SetFullscreenState(false, NULL); //Make sure we are out of fullscreen //create message std::wstring message = L"Could not open: "; message += filename; MessageBox(0, message.c_str(), //display message L"Error", MB_OK); return false; } subsetIndexStart.push_back(vIndex); //There won't be another index start after our last subset, so set it here //sometimes "g" is defined at the very top of the file, then again before the first group of faces. //This makes sure the first subset does not conatain "0" indices. if(subsetIndexStart[1] == 0) { subsetIndexStart.erase(subsetIndexStart.begin()+1); meshSubsets--; } //Make sure we have a default for the tex coord and normal //if one or both are not specified if(!hasNorm) vertNorm.push_back(XMFLOAT3(0.0f, 0.0f, 0.0f)); if(!hasTexCoord) vertTexCoord.push_back(XMFLOAT2(0.0f, 0.0f)); //Close the obj file, and open the mtl file fileIn.close(); fileIn.open(meshMatLib.c_str()); std::wstring lastStringRead; int matCount = material.size(); //total materials //kdset - If our diffuse color was not set, we can use the ambient color (which is usually the same) //If the diffuse color WAS set, then we don't need to set our diffuse color to ambient bool kdset = false; if (fileIn) { while(fileIn) { checkChar = fileIn.get(); //Get next char switch (checkChar) { //Check for comment case '#': checkChar = fileIn.get(); while(checkChar != '\n') checkChar = fileIn.get(); break; //Set diffuse color case 'K': checkChar = fileIn.get(); if(checkChar == 'd') //Diffuse Color { checkChar = fileIn.get(); //remove space fileIn >> material[matCount-1].difColor.x; fileIn >> material[matCount-1].difColor.y; fileIn >> material[matCount-1].difColor.z; kdset = true; } //Ambient Color (We'll store it in diffuse if there isn't a diffuse already) if(checkChar == 'a') { checkChar = fileIn.get(); //remove space if(!kdset) { fileIn >> material[matCount-1].difColor.x; fileIn >> material[matCount-1].difColor.y; fileIn >> material[matCount-1].difColor.z; } } break; //Check for transparency case 'T': checkChar = fileIn.get(); if(checkChar == 'r') { checkChar = fileIn.get(); //remove space float Transparency; fileIn >> Transparency; material[matCount-1].difColor.w = Transparency; if(Transparency > 0.0f) material[matCount-1].transparent = true; } break; //Some obj files specify d for transparency case 'd': checkChar = fileIn.get(); if(checkChar == ' ') { float Transparency; fileIn >> Transparency; //'d' - 0 being most transparent, and 1 being opaque, opposite of Tr Transparency = 1.0f - Transparency; material[matCount-1].difColor.w = Transparency; if(Transparency > 0.0f) material[matCount-1].transparent = true; } break; //Get the diffuse map (texture) case 'm': checkChar = fileIn.get(); if(checkChar == 'a') { checkChar = fileIn.get(); if(checkChar == 'p') { checkChar = fileIn.get(); if(checkChar == '_') { //map_Kd - Diffuse map checkChar = fileIn.get(); if(checkChar == 'K') { checkChar = fileIn.get(); if(checkChar == 'd') { std::wstring fileNamePath; fileIn.get(); //Remove whitespace between map_Kd and file //Get the file path - We read the pathname char by char since //pathnames can sometimes contain spaces, so we will read until //we find the file extension bool texFilePathEnd = false; while(!texFilePathEnd) { checkChar = fileIn.get(); fileNamePath += checkChar; if(checkChar == '.') { for(int i = 0; i < 3; ++i) fileNamePath += fileIn.get(); texFilePathEnd = true; } } //check if this texture has already been loaded bool alreadyLoaded = false; for(int i = 0; i < textureNameArray.size(); ++i) { if(fileNamePath == textureNameArray[i]) { alreadyLoaded = true; material[matCount-1].texArrayIndex = i; material[matCount-1].hasTexture = true; } } //if the texture is not already loaded, load it now if(!alreadyLoaded) { ID3D11ShaderResourceView* tempMeshSRV; hr = D3DX11CreateShaderResourceViewFromFile( d3d11Device, fileNamePath.c_str(), NULL, NULL, &tempMeshSRV, NULL ); if(SUCCEEDED(hr)) { textureNameArray.push_back(fileNamePath.c_str()); material[matCount-1].texArrayIndex = meshSRV.size(); meshSRV.push_back(tempMeshSRV); material[matCount-1].hasTexture = true; } } } } //map_d - alpha map else if(checkChar == 'd') { //Alpha maps are usually the same as the diffuse map //So we will assume that for now by only enabling //transparency for this material, as we will already //be using the alpha channel in the diffuse map material[matCount-1].transparent = true; } //map_bump - bump map (we're usinga normal map though) else if(checkChar == 'b') { checkChar = fileIn.get(); if(checkChar == 'u') { checkChar = fileIn.get(); if(checkChar == 'm') { checkChar = fileIn.get(); if(checkChar == 'p') { std::wstring fileNamePath; fileIn.get(); //Remove whitespace between map_bump and file //Get the file path - We read the pathname char by char since //pathnames can sometimes contain spaces, so we will read until //we find the file extension bool texFilePathEnd = false; while(!texFilePathEnd) { checkChar = fileIn.get(); fileNamePath += checkChar; if(checkChar == '.') { for(int i = 0; i < 3; ++i) fileNamePath += fileIn.get(); texFilePathEnd = true; } } //check if this texture has already been loaded bool alreadyLoaded = false; for(int i = 0; i < textureNameArray.size(); ++i) { if(fileNamePath == textureNameArray[i]) { alreadyLoaded = true; material[matCount-1].normMapTexArrayIndex = i; material[matCount-1].hasNormMap = true; } } //if the texture is not already loaded, load it now if(!alreadyLoaded) { ID3D11ShaderResourceView* tempMeshSRV; hr = D3DX11CreateShaderResourceViewFromFile( d3d11Device, fileNamePath.c_str(), NULL, NULL, &tempMeshSRV, NULL ); if(SUCCEEDED(hr)) { textureNameArray.push_back(fileNamePath.c_str()); material[matCount-1].normMapTexArrayIndex = meshSRV.size(); meshSRV.push_back(tempMeshSRV); material[matCount-1].hasNormMap = true; } } } } } } } } } break; case 'n': //newmtl - Declare new material checkChar = fileIn.get(); if(checkChar == 'e') { checkChar = fileIn.get(); if(checkChar == 'w') { checkChar = fileIn.get(); if(checkChar == 'm') { checkChar = fileIn.get(); if(checkChar == 't') { checkChar = fileIn.get(); if(checkChar == 'l') { checkChar = fileIn.get(); if(checkChar == ' ') { //New material, set its defaults SurfaceMaterial tempMat; material.push_back(tempMat); fileIn >> material[matCount].matName; material[matCount].transparent = false; material[matCount].hasTexture = false; material[matCount].hasNormMap = false; material[matCount].normMapTexArrayIndex = 0; material[matCount].texArrayIndex = 0; matCount++; kdset = false; } } } } } } break; default: break; } } } else { SwapChain->SetFullscreenState(false, NULL); //Make sure we are out of fullscreen std::wstring message = L"Could not open: "; message += meshMatLib; MessageBox(0, message.c_str(), L"Error", MB_OK); return false; } //Set the subsets material to the index value //of the its material in our material array for(int i = 0; i < meshSubsets; ++i) { bool hasMat = false; for(int j = 0; j < material.size(); ++j) { if(meshMaterials[i] == material[j].matName) { subsetMaterialArray.push_back(j); hasMat = true; } } if(!hasMat) subsetMaterialArray.push_back(0); //Use first material in array } std::vector<Vertex> vertices; Vertex tempVert; //Create our vertices using the information we got //from the file and store them in a vector for(int j = 0 ; j < totalVerts; ++j) { tempVert.pos = vertPos[vertPosIndex[j]]; tempVert.normal = vertNorm[vertNormIndex[j]]; tempVert.texCoord = vertTexCoord[vertTCIndex[j]]; vertices.push_back(tempVert); ///////////////**************new**************//////////////////// //Copy just the vertex positions to the vector vertPosArray.push_back(tempVert.pos); ///////////////**************new**************//////////////////// } ///////////////**************new**************//////////////////// //Copy the index list to the array vertIndexArray = indices; ///////////////**************new**************//////////////////// //If computeNormals was set to true then we will create our own //normals, if it was set to false we will use the obj files normals if(computeNormals) { std::vector<XMFLOAT3> tempNormal; //normalized and unnormalized normals XMFLOAT3 unnormalized = XMFLOAT3(0.0f, 0.0f, 0.0f); //tangent stuff std::vector<XMFLOAT3> tempTangent; XMFLOAT3 tangent = XMFLOAT3(0.0f, 0.0f, 0.0f); float tcU1, tcV1, tcU2, tcV2; //Used to get vectors (sides) from the position of the verts float vecX, vecY, vecZ; //Two edges of our triangle XMVECTOR edge1 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); XMVECTOR edge2 = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); //Compute face normals //And Tangents for(int i = 0; i < meshTriangles; ++i) { //Get the vector describing one edge of our triangle (edge 0,2) vecX = vertices[indices[(i*3)]].pos.x - vertices[indices[(i*3)+2]].pos.x; vecY = vertices[indices[(i*3)]].pos.y - vertices[indices[(i*3)+2]].pos.y; vecZ = vertices[indices[(i*3)]].pos.z - vertices[indices[(i*3)+2]].pos.z; edge1 = XMVectorSet(vecX, vecY, vecZ, 0.0f); //Create our first edge //Get the vector describing another edge of our triangle (edge 2,1) vecX = vertices[indices[(i*3)+2]].pos.x - vertices[indices[(i*3)+1]].pos.x; vecY = vertices[indices[(i*3)+2]].pos.y - vertices[indices[(i*3)+1]].pos.y; vecZ = vertices[indices[(i*3)+2]].pos.z - vertices[indices[(i*3)+1]].pos.z; edge2 = XMVectorSet(vecX, vecY, vecZ, 0.0f); //Create our second edge //Cross multiply the two edge vectors to get the un-normalized face normal XMStoreFloat3(&unnormalized, XMVector3Cross(edge1, edge2)); tempNormal.push_back(unnormalized); //Find first texture coordinate edge 2d vector tcU1 = vertices[indices[(i*3)]].texCoord.x - vertices[indices[(i*3)+2]].texCoord.x; tcV1 = vertices[indices[(i*3)]].texCoord.y - vertices[indices[(i*3)+2]].texCoord.y; //Find second texture coordinate edge 2d vector tcU2 = vertices[indices[(i*3)+2]].texCoord.x - vertices[indices[(i*3)+1]].texCoord.x; tcV2 = vertices[indices[(i*3)+2]].texCoord.y - vertices[indices[(i*3)+1]].texCoord.y; //Find tangent using both tex coord edges and position edges tangent.x = (tcV1 * XMVectorGetX(edge1) - tcV2 * XMVectorGetX(edge2)) * (1.0f / (tcU1 * tcV2 - tcU2 * tcV1)); tangent.y = (tcV1 * XMVectorGetY(edge1) - tcV2 * XMVectorGetY(edge2)) * (1.0f / (tcU1 * tcV2 - tcU2 * tcV1)); tangent.z = (tcV1 * XMVectorGetZ(edge1) - tcV2 * XMVectorGetZ(edge2)) * (1.0f / (tcU1 * tcV2 - tcU2 * tcV1)); tempTangent.push_back(tangent); } //Compute vertex normals (normal Averaging) XMVECTOR normalSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); XMVECTOR tangentSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); int facesUsing = 0; float tX, tY, tZ; //temp axis variables //Go through each vertex for(int i = 0; i < totalVerts; ++i) { //Check which triangles use this vertex for(int j = 0; j < meshTriangles; ++j) { if(indices[j*3] == i || indices[(j*3)+1] == i || indices[(j*3)+2] == i) { tX = XMVectorGetX(normalSum) + tempNormal[j].x; tY = XMVectorGetY(normalSum) + tempNormal[j].y; tZ = XMVectorGetZ(normalSum) + tempNormal[j].z; normalSum = XMVectorSet(tX, tY, tZ, 0.0f); //If a face is using the vertex, add the unormalized face normal to the normalSum //We can reuse tX, tY, tZ to sum up tangents tX = XMVectorGetX(tangentSum) + tempTangent[j].x; tY = XMVectorGetY(tangentSum) + tempTangent[j].y; tZ = XMVectorGetZ(tangentSum) + tempTangent[j].z; tangentSum = XMVectorSet(tX, tY, tZ, 0.0f); //sum up face tangents using this vertex facesUsing++; } } //Get the actual normal by dividing the normalSum by the number of faces sharing the vertex normalSum = normalSum / facesUsing; tangentSum = tangentSum / facesUsing; //Normalize the normalSum vector and tangent normalSum = XMVector3Normalize(normalSum); tangentSum = XMVector3Normalize(tangentSum); //Store the normal and tangent in our current vertex vertices[i].normal.x = XMVectorGetX(normalSum); vertices[i].normal.y = XMVectorGetY(normalSum); vertices[i].normal.z = XMVectorGetZ(normalSum); vertices[i].tangent.x = XMVectorGetX(tangentSum); vertices[i].tangent.y = XMVectorGetY(tangentSum); vertices[i].tangent.z = XMVectorGetZ(tangentSum); //Clear normalSum, tangentSum and facesUsing for next vertex normalSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); tangentSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); facesUsing = 0; } } //Create index buffer D3D11_BUFFER_DESC indexBufferDesc; ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) ); indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; indexBufferDesc.ByteWidth = sizeof(DWORD) * meshTriangles*3; indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDesc.CPUAccessFlags = 0; indexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA iinitData; iinitData.pSysMem = &indices[0]; d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, indexBuff); //Create Vertex Buffer D3D11_BUFFER_DESC vertexBufferDesc; ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) ); vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; vertexBufferDesc.ByteWidth = sizeof( Vertex ) * totalVerts; vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertexBufferDesc.CPUAccessFlags = 0; vertexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA vertexBufferData; ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) ); vertexBufferData.pSysMem = &vertices[0]; hr = d3d11Device->CreateBuffer( &vertexBufferDesc, &vertexBufferData, vertBuff); return true; } void CreateSphere(int LatLines, int LongLines) { NumSphereVertices = ((LatLines-2) * LongLines) + 2; NumSphereFaces = ((LatLines-3)*(LongLines)*2) + (LongLines*2); float sphereYaw = 0.0f; float spherePitch = 0.0f; std::vector<Vertex> vertices(NumSphereVertices); XMVECTOR currVertPos = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f); vertices[0].pos.x = 0.0f; vertices[0].pos.y = 0.0f; vertices[0].pos.z = 1.0f; for(DWORD i = 0; i < LatLines-2; ++i) { spherePitch = (i+1) * (3.14f/(LatLines-1)); Rotationx = XMMatrixRotationX(spherePitch); for(DWORD j = 0; j < LongLines; ++j) { sphereYaw = j * (6.28f/(LongLines)); Rotationy = XMMatrixRotationZ(sphereYaw); currVertPos = XMVector3TransformNormal( XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f), (Rotationx * Rotationy) ); currVertPos = XMVector3Normalize( currVertPos ); vertices[i*LongLines+j+1].pos.x = XMVectorGetX(currVertPos); vertices[i*LongLines+j+1].pos.y = XMVectorGetY(currVertPos); vertices[i*LongLines+j+1].pos.z = XMVectorGetZ(currVertPos); } } vertices[NumSphereVertices-1].pos.x = 0.0f; vertices[NumSphereVertices-1].pos.y = 0.0f; vertices[NumSphereVertices-1].pos.z = -1.0f; D3D11_BUFFER_DESC vertexBufferDesc; ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) ); vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; vertexBufferDesc.ByteWidth = sizeof( Vertex ) * NumSphereVertices; vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertexBufferDesc.CPUAccessFlags = 0; vertexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA vertexBufferData; ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) ); vertexBufferData.pSysMem = &vertices[0]; hr = d3d11Device->CreateBuffer( &vertexBufferDesc, &vertexBufferData, &sphereVertBuffer); std::vector<DWORD> indices(NumSphereFaces * 3); int k = 0; for(DWORD l = 0; l < LongLines-1; ++l) { indices[k] = 0; indices[k+1] = l+1; indices[k+2] = l+2; k += 3; } indices[k] = 0; indices[k+1] = LongLines; indices[k+2] = 1; k += 3; for(DWORD i = 0; i < LatLines-3; ++i) { for(DWORD j = 0; j < LongLines-1; ++j) { indices[k] = i*LongLines+j+1; indices[k+1] = i*LongLines+j+2; indices[k+2] = (i+1)*LongLines+j+1; indices[k+3] = (i+1)*LongLines+j+1; indices[k+4] = i*LongLines+j+2; indices[k+5] = (i+1)*LongLines+j+2; k += 6; // next quad } indices[k] = (i*LongLines)+LongLines; indices[k+1] = (i*LongLines)+1; indices[k+2] = ((i+1)*LongLines)+LongLines; indices[k+3] = ((i+1)*LongLines)+LongLines; indices[k+4] = (i*LongLines)+1; indices[k+5] = ((i+1)*LongLines)+1; k += 6; } for(DWORD l = 0; l < LongLines-1; ++l) { indices[k] = NumSphereVertices-1; indices[k+1] = (NumSphereVertices-1)-(l+1); indices[k+2] = (NumSphereVertices-1)-(l+2); k += 3; } indices[k] = NumSphereVertices-1; indices[k+1] = (NumSphereVertices-1)-LongLines; indices[k+2] = NumSphereVertices-2; D3D11_BUFFER_DESC indexBufferDesc; ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) ); indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; indexBufferDesc.ByteWidth = sizeof(DWORD) * NumSphereFaces * 3; indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDesc.CPUAccessFlags = 0; indexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA iinitData; iinitData.pSysMem = &indices[0]; d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &sphereIndexBuffer); } void InitD2DScreenTexture() { //Create the vertex buffer Vertex v[] = { // Front Face Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f,-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f), Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 0.0f,-1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f), Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f), Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f), }; DWORD indices[] = { // Front Face 0, 1, 2, 0, 2, 3, }; D3D11_BUFFER_DESC indexBufferDesc; ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) ); indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; indexBufferDesc.ByteWidth = sizeof(DWORD) * 2 * 3; indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDesc.CPUAccessFlags = 0; indexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA iinitData; iinitData.pSysMem = indices; d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &d2dIndexBuffer); D3D11_BUFFER_DESC vertexBufferDesc; ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) ); vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; vertexBufferDesc.ByteWidth = sizeof( Vertex ) * 4; vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertexBufferDesc.CPUAccessFlags = 0; vertexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA vertexBufferData; ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) ); vertexBufferData.pSysMem = v; hr = d3d11Device->CreateBuffer( &vertexBufferDesc, &vertexBufferData, &d2dVertBuffer); //Create A shader resource view from the texture D2D will render to, //So we can use it to texture a square which overlays our scene d3d11Device->CreateShaderResourceView(sharedTex11, NULL, &d2dTexture); } bool InitScene() { InitD2DScreenTexture(); CreateSphere(10, 10); ///////////////**************new**************//////////////////// if(!LoadObjModel(L"ground.obj", &meshVertBuff, &meshIndexBuff, meshSubsetIndexStart, meshSubsetTexture, material, meshSubsets, true, true, groundVertPosArray, groundVertIndexArray)) return false; if(!LoadObjModel(L"bottle.obj", &bottleVertBuff, &bottleIndexBuff, bottleSubsetIndexStart, bottleSubsetTexture, material, bottleSubsets, true, true, bottleVertPosArray, bottleVertIndexArray)) return false; ///////////////**************new**************//////////////////// //Compile Shaders from shader file hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "VS", "vs_4_0", 0, 0, 0, &VS_Buffer, 0, 0); hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "PS", "ps_4_0", 0, 0, 0, &PS_Buffer, 0, 0); hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "D2D_PS", "ps_4_0", 0, 0, 0, &D2D_PS_Buffer, 0, 0); hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "SKYMAP_VS", "vs_4_0", 0, 0, 0, &SKYMAP_VS_Buffer, 0, 0); hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "SKYMAP_PS", "ps_4_0", 0, 0, 0, &SKYMAP_PS_Buffer, 0, 0); //Create the Shader Objects hr = d3d11Device->CreateVertexShader(VS_Buffer->GetBufferPointer(), VS_Buffer->GetBufferSize(), NULL, &VS); hr = d3d11Device->CreatePixelShader(PS_Buffer->GetBufferPointer(), PS_Buffer->GetBufferSize(), NULL, &PS); hr = d3d11Device->CreatePixelShader(D2D_PS_Buffer->GetBufferPointer(), D2D_PS_Buffer->GetBufferSize(), NULL, &D2D_PS); hr = d3d11Device->CreateVertexShader(SKYMAP_VS_Buffer->GetBufferPointer(), SKYMAP_VS_Buffer->GetBufferSize(), NULL, &SKYMAP_VS); hr = d3d11Device->CreatePixelShader(SKYMAP_PS_Buffer->GetBufferPointer(), SKYMAP_PS_Buffer->GetBufferSize(), NULL, &SKYMAP_PS); //Set Vertex and Pixel Shaders d3d11DevCon->VSSetShader(VS, 0, 0); d3d11DevCon->PSSetShader(PS, 0, 0); light.pos = XMFLOAT3(0.0f, 7.0f, 0.0f); light.dir = XMFLOAT3(-0.5f, 0.75f, -0.5f); light.range = 1000.0f; light.cone = 12.0f; light.att = XMFLOAT3(0.4f, 0.02f, 0.000f); light.ambient = XMFLOAT4(0.2f, 0.2f, 0.2f, 1.0f); light.diffuse = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f); //Create the Input Layout hr = d3d11Device->CreateInputLayout( layout, numElements, VS_Buffer->GetBufferPointer(), VS_Buffer->GetBufferSize(), &vertLayout ); //Set the Input Layout d3d11DevCon->IASetInputLayout( vertLayout ); //Set Primitive Topology d3d11DevCon->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); //Create the Viewport D3D11_VIEWPORT viewport; ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT)); viewport.TopLeftX = 0; viewport.TopLeftY = 0; viewport.Width = Width; viewport.Height = Height; viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; //Set the Viewport d3d11DevCon->RSSetViewports(1, &viewport); //Create the buffer to send to the cbuffer in effect file D3D11_BUFFER_DESC cbbd; ZeroMemory(&cbbd, sizeof(D3D11_BUFFER_DESC)); cbbd.Usage = D3D11_USAGE_DEFAULT; cbbd.ByteWidth = sizeof(cbPerObject); cbbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; cbbd.CPUAccessFlags = 0; cbbd.MiscFlags = 0; hr = d3d11Device->CreateBuffer(&cbbd, NULL, &cbPerObjectBuffer); //Create the buffer to send to the cbuffer per frame in effect file ZeroMemory(&cbbd, sizeof(D3D11_BUFFER_DESC)); cbbd.Usage = D3D11_USAGE_DEFAULT; cbbd.ByteWidth = sizeof(cbPerFrame); cbbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; cbbd.CPUAccessFlags = 0; cbbd.MiscFlags = 0; hr = d3d11Device->CreateBuffer(&cbbd, NULL, &cbPerFrameBuffer); //Camera information camPosition = XMVectorSet( 0.0f, 5.0f, -8.0f, 0.0f ); camTarget = XMVectorSet( 0.0f, 0.5f, 0.0f, 0.0f ); camUp = XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f ); //Set the View matrix camView = XMMatrixLookAtLH( camPosition, camTarget, camUp ); //Set the Projection matrix camProjection = XMMatrixPerspectiveFovLH( 0.4f*3.14f, (float)Width/Height, 1.0f, 1000.0f); D3D11_BLEND_DESC blendDesc; ZeroMemory( &blendDesc, sizeof(blendDesc) ); D3D11_RENDER_TARGET_BLEND_DESC rtbd; ZeroMemory( &rtbd, sizeof(rtbd) ); rtbd.BlendEnable = true; rtbd.SrcBlend = D3D11_BLEND_SRC_COLOR; rtbd.DestBlend = D3D11_BLEND_INV_SRC_ALPHA; rtbd.BlendOp = D3D11_BLEND_OP_ADD; rtbd.SrcBlendAlpha = D3D11_BLEND_ONE; rtbd.DestBlendAlpha = D3D11_BLEND_ZERO; rtbd.BlendOpAlpha = D3D11_BLEND_OP_ADD; rtbd.RenderTargetWriteMask = D3D10_COLOR_WRITE_ENABLE_ALL; blendDesc.AlphaToCoverageEnable = false; blendDesc.RenderTarget[0] = rtbd; d3d11Device->CreateBlendState(&blendDesc, &d2dTransparency); ZeroMemory( &rtbd, sizeof(rtbd) ); rtbd.BlendEnable = true; rtbd.SrcBlend = D3D11_BLEND_INV_SRC_ALPHA; rtbd.DestBlend = D3D11_BLEND_SRC_ALPHA; rtbd.BlendOp = D3D11_BLEND_OP_ADD; rtbd.SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; rtbd.DestBlendAlpha = D3D11_BLEND_SRC_ALPHA; rtbd.BlendOpAlpha = D3D11_BLEND_OP_ADD; rtbd.RenderTargetWriteMask = D3D10_COLOR_WRITE_ENABLE_ALL; blendDesc.AlphaToCoverageEnable = false; blendDesc.RenderTarget[0] = rtbd; d3d11Device->CreateBlendState(&blendDesc, &Transparency); ///Load Skymap's cube texture/// D3DX11_IMAGE_LOAD_INFO loadSMInfo; loadSMInfo.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE; ID3D11Texture2D* SMTexture = 0; hr = D3DX11CreateTextureFromFile(d3d11Device, L"skymap.dds", &loadSMInfo, 0, (ID3D11Resource**)&SMTexture, 0); D3D11_TEXTURE2D_DESC SMTextureDesc; SMTexture->GetDesc(&SMTextureDesc); D3D11_SHADER_RESOURCE_VIEW_DESC SMViewDesc; SMViewDesc.Format = SMTextureDesc.Format; SMViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; SMViewDesc.TextureCube.MipLevels = SMTextureDesc.MipLevels; SMViewDesc.TextureCube.MostDetailedMip = 0; hr = d3d11Device->CreateShaderResourceView(SMTexture, &SMViewDesc, &smrv); // Describe the Sample State D3D11_SAMPLER_DESC sampDesc; ZeroMemory( &sampDesc, sizeof(sampDesc) ); sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; sampDesc.MinLOD = 0; sampDesc.MaxLOD = D3D11_FLOAT32_MAX; //Create the Sample State hr = d3d11Device->CreateSamplerState( &sampDesc, &CubesTexSamplerState ); D3D11_RASTERIZER_DESC cmdesc; ZeroMemory(&cmdesc, sizeof(D3D11_RASTERIZER_DESC)); cmdesc.FillMode = D3D11_FILL_SOLID; cmdesc.CullMode = D3D11_CULL_BACK; cmdesc.FrontCounterClockwise = true; hr = d3d11Device->CreateRasterizerState(&cmdesc, &CCWcullMode); cmdesc.FrontCounterClockwise = false; hr = d3d11Device->CreateRasterizerState(&cmdesc, &CWcullMode); cmdesc.CullMode = D3D11_CULL_NONE; //cmdesc.FillMode = D3D11_FILL_WIREFRAME; hr = d3d11Device->CreateRasterizerState(&cmdesc, &RSCullNone); D3D11_DEPTH_STENCIL_DESC dssDesc; ZeroMemory(&dssDesc, sizeof(D3D11_DEPTH_STENCIL_DESC)); dssDesc.DepthEnable = true; dssDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; dssDesc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL; d3d11Device->CreateDepthStencilState(&dssDesc, &DSLessEqual); ///////////////**************new**************//////////////////// float bottleXPos = -30.0f; float bottleZPos = 30.0f; float bxadd = 0.0f; float bzadd = 0.0f; for(int i = 0; i < numBottles; i++) { bottleHit[i] = 0; //set the loaded bottles world space bottleWorld[i] = XMMatrixIdentity(); bxadd++; if(bxadd == 10) { bzadd -= 1.0f; bxadd = 0; } Rotation = XMMatrixRotationY(3.14f); Scale = XMMatrixScaling( 1.0f, 1.0f, 1.0f ); Translation = XMMatrixTranslation( bottleXPos + bxadd*10.0f, 4.0f, bottleZPos + bzadd*10.0f ); bottleWorld[i] = Rotation * Scale * Translation; } ///////////////**************new**************//////////////////// return true; } void StartTimer() { LARGE_INTEGER frequencyCount; QueryPerformanceFrequency(&frequencyCount); countsPerSecond = double(frequencyCount.QuadPart); QueryPerformanceCounter(&frequencyCount); CounterStart = frequencyCount.QuadPart; } double GetTime() { LARGE_INTEGER currentTime; QueryPerformanceCounter(&currentTime); return double(currentTime.QuadPart-CounterStart)/countsPerSecond; } double GetFrameTime() { LARGE_INTEGER currentTime; __int64 tickCount; QueryPerformanceCounter(&currentTime); tickCount = currentTime.QuadPart-frameTimeOld; frameTimeOld = currentTime.QuadPart; if(tickCount < 0.0f) tickCount = 0.0f; return float(tickCount)/countsPerSecond; } void UpdateScene(double time) { //Reset sphereWorld sphereWorld = XMMatrixIdentity(); //Define sphereWorld's world space matrix Scale = XMMatrixScaling( 5.0f, 5.0f, 5.0f ); //Make sure the sphere is always centered around camera Translation = XMMatrixTranslation( XMVectorGetX(camPosition), XMVectorGetY(camPosition), XMVectorGetZ(camPosition) ); //Set sphereWorld's world space using the transformations sphereWorld = Scale * Translation; //the loaded models world space meshWorld = XMMatrixIdentity(); Rotation = XMMatrixRotationY(3.14f); Scale = XMMatrixScaling( 1.0f, 1.0f, 1.0f ); Translation = XMMatrixTranslation( 0.0f, 0.0f, 0.0f ); meshWorld = Rotation * Scale * Translation; /*light.pos.x = XMVectorGetX(camPosition); light.pos.y = XMVectorGetY(camPosition); light.pos.z = XMVectorGetZ(camPosition); light.dir.x = XMVectorGetX(camTarget) - light.pos.x; light.dir.y = XMVectorGetY(camTarget) - light.pos.y; light.dir.z = XMVectorGetZ(camTarget) - light.pos.z;*/ } void RenderText(std::wstring text, int inInt) { d3d11DevCon->PSSetShader(D2D_PS, 0, 0); //Release the D3D 11 Device keyedMutex11->ReleaseSync(0); //Use D3D10.1 device keyedMutex10->AcquireSync(0, 5); //Draw D2D content D2DRenderTarget->BeginDraw(); //Clear D2D Background D2DRenderTarget->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f)); ///////////////**************new**************//////////////////// //Create our string std::wostringstream printString; printString << text << inInt << L"\n" << L"Score: " << score << L"\n" << L"Picked Dist: " << pickedDist; ///////////////**************new**************//////////////////// printText = printString.str(); //Set the Font Color D2D1_COLOR_F FontColor = D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f); //Set the brush color D2D will use to draw with Brush->SetColor(FontColor); //Create the D2D Render Area D2D1_RECT_F layoutRect = D2D1::RectF(0, 0, Width, Height); //Draw the Text D2DRenderTarget->DrawText( printText.c_str(), wcslen(printText.c_str()), TextFormat, layoutRect, Brush ); D2DRenderTarget->EndDraw(); //Release the D3D10.1 Device keyedMutex10->ReleaseSync(1); //Use the D3D11 Device keyedMutex11->AcquireSync(1, 5); //Use the shader resource representing the direct2d render target //to texture a square which is rendered in screen space so it //overlays on top of our entire scene. We use alpha blending so //that the entire background of the D2D render target is "invisible", //And only the stuff we draw with D2D will be visible (the text) //Set the blend state for D2D render target texture objects d3d11DevCon->OMSetBlendState(d2dTransparency, NULL, 0xffffffff); //Set the d2d Index buffer d3d11DevCon->IASetIndexBuffer( d2dIndexBuffer, DXGI_FORMAT_R32_UINT, 0); //Set the d2d vertex buffer UINT stride = sizeof( Vertex ); UINT offset = 0; d3d11DevCon->IASetVertexBuffers( 0, 1, &d2dVertBuffer, &stride, &offset ); WVP = XMMatrixIdentity(); cbPerObj.WVP = XMMatrixTranspose(WVP); d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 ); d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer ); d3d11DevCon->PSSetShaderResources( 0, 1, &d2dTexture ); d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState ); d3d11DevCon->RSSetState(CWcullMode); d3d11DevCon->DrawIndexed( 6, 0, 0 ); } void DrawScene() { //Clear our render target and depth/stencil view float bgColor[4] = { 0.1f, 0.1f, 0.1f, 1.0f }; d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor); d3d11DevCon->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0); constbuffPerFrame.light = light; d3d11DevCon->UpdateSubresource( cbPerFrameBuffer, 0, NULL, &constbuffPerFrame, 0, 0 ); d3d11DevCon->PSSetConstantBuffers(0, 1, &cbPerFrameBuffer); //Set our Render Target d3d11DevCon->OMSetRenderTargets( 1, &renderTargetView, depthStencilView ); //Set the default blend state (no blending) for opaque objects d3d11DevCon->OMSetBlendState(0, 0, 0xffffffff); //Set Vertex and Pixel Shaders d3d11DevCon->VSSetShader(VS, 0, 0); d3d11DevCon->PSSetShader(PS, 0, 0); UINT stride = sizeof( Vertex ); UINT offset = 0; /////Draw our model's NON-transparent subsets///// for(int i = 0; i < meshSubsets; ++i) { //Set the grounds index buffer d3d11DevCon->IASetIndexBuffer( meshIndexBuff, DXGI_FORMAT_R32_UINT, 0); //Set the grounds vertex buffer d3d11DevCon->IASetVertexBuffers( 0, 1, &meshVertBuff, &stride, &offset ); //Set the WVP matrix and send it to the constant buffer in effect file WVP = meshWorld * camView * camProjection; cbPerObj.WVP = XMMatrixTranspose(WVP); cbPerObj.World = XMMatrixTranspose(meshWorld); cbPerObj.difColor = material[meshSubsetTexture[i]].difColor; cbPerObj.hasTexture = material[meshSubsetTexture[i]].hasTexture; cbPerObj.hasNormMap = material[meshSubsetTexture[i]].hasNormMap; d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 ); d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer ); d3d11DevCon->PSSetConstantBuffers( 1, 1, &cbPerObjectBuffer ); if(material[meshSubsetTexture[i]].hasTexture) d3d11DevCon->PSSetShaderResources( 0, 1, &meshSRV[material[meshSubsetTexture[i]].texArrayIndex] ); if(material[meshSubsetTexture[i]].hasNormMap) d3d11DevCon->PSSetShaderResources( 1, 1, &meshSRV[material[meshSubsetTexture[i]].normMapTexArrayIndex] ); d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState ); d3d11DevCon->RSSetState(RSCullNone); int indexStart = meshSubsetIndexStart[i]; int indexDrawAmount = meshSubsetIndexStart[i+1] - meshSubsetIndexStart[i]; if(!material[meshSubsetTexture[i]].transparent) d3d11DevCon->DrawIndexed( indexDrawAmount, indexStart, 0 ); } ///////////////**************new**************//////////////////// //draw bottle's nontransparent subsets for(int j = 0; j < numBottles; j++) { if(bottleHit[j] == 0) { for(int i = 0; i < bottleSubsets; ++i) { //Set the grounds index buffer d3d11DevCon->IASetIndexBuffer( bottleIndexBuff, DXGI_FORMAT_R32_UINT, 0); //Set the grounds vertex buffer d3d11DevCon->IASetVertexBuffers( 0, 1, &bottleVertBuff, &stride, &offset ); //Set the WVP matrix and send it to the constant buffer in effect file WVP = bottleWorld[j] * camView * camProjection; cbPerObj.WVP = XMMatrixTranspose(WVP); cbPerObj.World = XMMatrixTranspose(bottleWorld[j]); cbPerObj.difColor = material[bottleSubsetTexture[i]].difColor; cbPerObj.hasTexture = material[bottleSubsetTexture[i]].hasTexture; cbPerObj.hasNormMap = material[bottleSubsetTexture[i]].hasNormMap; d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 ); d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer ); d3d11DevCon->PSSetConstantBuffers( 1, 1, &cbPerObjectBuffer ); if(material[bottleSubsetTexture[i]].hasTexture) d3d11DevCon->PSSetShaderResources( 0, 1, &meshSRV[material[bottleSubsetTexture[i]].texArrayIndex] ); if(material[bottleSubsetTexture[i]].hasNormMap) d3d11DevCon->PSSetShaderResources( 1, 1, &meshSRV[material[bottleSubsetTexture[i]].normMapTexArrayIndex] ); d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState ); d3d11DevCon->RSSetState(RSCullNone); int indexStart = bottleSubsetIndexStart[i]; int indexDrawAmount = bottleSubsetIndexStart[i+1] - bottleSubsetIndexStart[i]; if(!material[bottleSubsetTexture[i]].transparent) d3d11DevCon->DrawIndexed( indexDrawAmount, indexStart, 0 ); } } } ///////////////**************new**************//////////////////// /////Draw the Sky's Sphere////// //Set the spheres index buffer d3d11DevCon->IASetIndexBuffer( sphereIndexBuffer, DXGI_FORMAT_R32_UINT, 0); //Set the spheres vertex buffer d3d11DevCon->IASetVertexBuffers( 0, 1, &sphereVertBuffer, &stride, &offset ); //Set the WVP matrix and send it to the constant buffer in effect file WVP = sphereWorld * camView * camProjection; cbPerObj.WVP = XMMatrixTranspose(WVP); cbPerObj.World = XMMatrixTranspose(sphereWorld); d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 ); d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer ); //Send our skymap resource view to pixel shader d3d11DevCon->PSSetShaderResources( 0, 1, &smrv ); d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState ); //Set the new VS and PS shaders d3d11DevCon->VSSetShader(SKYMAP_VS, 0, 0); d3d11DevCon->PSSetShader(SKYMAP_PS, 0, 0); //Set the new depth/stencil and RS states d3d11DevCon->OMSetDepthStencilState(DSLessEqual, 0); d3d11DevCon->RSSetState(RSCullNone); d3d11DevCon->DrawIndexed( NumSphereFaces * 3, 0, 0 ); //Set the default VS, PS shaders and depth/stencil state d3d11DevCon->VSSetShader(VS, 0, 0); d3d11DevCon->PSSetShader(PS, 0, 0); d3d11DevCon->OMSetDepthStencilState(NULL, 0); /////Draw our model's TRANSPARENT subsets now///// //Set our blend state d3d11DevCon->OMSetBlendState(Transparency, NULL, 0xffffffff); for(int i = 0; i < meshSubsets; ++i) { //Set the grounds index buffer d3d11DevCon->IASetIndexBuffer( meshIndexBuff, DXGI_FORMAT_R32_UINT, 0); //Set the grounds vertex buffer d3d11DevCon->IASetVertexBuffers( 0, 1, &meshVertBuff, &stride, &offset ); //Set the WVP matrix and send it to the constant buffer in effect file WVP = meshWorld * camView * camProjection; cbPerObj.WVP = XMMatrixTranspose(WVP); cbPerObj.World = XMMatrixTranspose(meshWorld); cbPerObj.difColor = material[meshSubsetTexture[i]].difColor; cbPerObj.hasTexture = material[meshSubsetTexture[i]].hasTexture; cbPerObj.hasNormMap = material[meshSubsetTexture[i]].hasNormMap; d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 ); d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer ); d3d11DevCon->PSSetConstantBuffers( 1, 1, &cbPerObjectBuffer ); if(material[meshSubsetTexture[i]].hasTexture) d3d11DevCon->PSSetShaderResources( 0, 1, &meshSRV[material[meshSubsetTexture[i]].texArrayIndex] ); if(material[meshSubsetTexture[i]].hasNormMap) d3d11DevCon->PSSetShaderResources( 1, 1, &meshSRV[material[meshSubsetTexture[i]].normMapTexArrayIndex] ); d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState ); d3d11DevCon->RSSetState(RSCullNone); int indexStart = meshSubsetIndexStart[i]; int indexDrawAmount = meshSubsetIndexStart[i+1] - meshSubsetIndexStart[i]; if(material[meshSubsetTexture[i]].transparent) d3d11DevCon->DrawIndexed( indexDrawAmount, indexStart, 0 ); } //We could draw the transparent subsets of our bottle here if it had any// RenderText(L"FPS: ", fps); //Present the backbuffer to the screen SwapChain->Present(0, 0); } int messageloop(){ MSG msg; ZeroMemory(&msg, sizeof(MSG)); while(true) { BOOL PeekMessageL( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg ); if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } else{ // run game code frameCount++; if(GetTime() > 1.0f) { fps = frameCount; frameCount = 0; StartTimer(); } frameTime = GetFrameTime(); DetectInput(frameTime); UpdateScene(frameTime); DrawScene(); } } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) { case WM_KEYDOWN: if( wParam == VK_ESCAPE ){ DestroyWindow(hwnd); } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; ///////////////**************new**************//////////////////// case WM_SIZE: ClientWidth = LOWORD(lParam); ClientHeight = HIWORD(lParam); return 0; } ///////////////**************new**************//////////////////// return DefWindowProc(hwnd, msg, wParam, lParam); } Effects.fx struct Light { float3 pos; float range; float3 dir; float cone; float3 att; float4 ambient; float4 diffuse; }; cbuffer cbPerFrame { Light light; }; cbuffer cbPerObject { float4x4 WVP; float4x4 World; float4 difColor; bool hasTexture; bool hasNormMap; }; Texture2D ObjTexture; Texture2D ObjNormMap; SamplerState ObjSamplerState; TextureCube SkyMap; struct VS_OUTPUT { float4 Pos : SV_POSITION; float4 worldPos : POSITION; float2 TexCoord : TEXCOORD; float3 normal : NORMAL; float3 tangent : TANGENT; }; struct SKYMAP_VS_OUTPUT //output structure for skymap vertex shader { float4 Pos : SV_POSITION; float3 texCoord : TEXCOORD; }; VS_OUTPUT VS(float4 inPos : POSITION, float2 inTexCoord : TEXCOORD, float3 normal : NORMAL, float3 tangent : TANGENT) { VS_OUTPUT output; output.Pos = mul(inPos, WVP); output.worldPos = mul(inPos, World); output.normal = mul(normal, World); output.tangent = mul(tangent, World); output.TexCoord = inTexCoord; return output; } SKYMAP_VS_OUTPUT SKYMAP_VS(float3 inPos : POSITION, float2 inTexCoord : TEXCOORD, float3 normal : NORMAL, float3 tangent : TANGENT) { SKYMAP_VS_OUTPUT output = (SKYMAP_VS_OUTPUT)0; //Set Pos to xyww instead of xyzw, so that z will always be 1 (furthest from camera) output.Pos = mul(float4(inPos, 1.0f), WVP).xyww; output.texCoord = inPos; return output; } float4 PS(VS_OUTPUT input) : SV_TARGET { input.normal = normalize(input.normal); //Set diffuse color of material float4 diffuse = difColor; //If material has a diffuse texture map, set it now if(hasTexture == true) diffuse = ObjTexture.Sample( ObjSamplerState, input.TexCoord ); //If material has a normal map, we can set it now if(hasNormMap == true) { //Load normal from normal map float4 normalMap = ObjNormMap.Sample( ObjSamplerState, input.TexCoord ); //Change normal map range from [0, 1] to [-1, 1] normalMap = (2.0f*normalMap) - 1.0f; //Make sure tangent is completely orthogonal to normal input.tangent = normalize(input.tangent - dot(input.tangent, input.normal)*input.normal); //Create the biTangent float3 biTangent = cross(input.normal, input.tangent); //Create the "Texture Space" float3x3 texSpace = float3x3(input.tangent, biTangent, input.normal); //Convert normal from normal map to texture space and store in input.normal input.normal = normalize(mul(normalMap, texSpace)); } 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(ObjSamplerState, input.texCoord); } float4 D2D_PS(VS_OUTPUT input) : SV_TARGET { float4 diffuse = ObjTexture.Sample( ObjSamplerState, input.TexCoord ); return diffuse; }
Comments
Class you are the best, your tutorials are a great thing
on Aug 22 `18
ravencrest
https://www.youtube.com/watch?v=yeIXXxtNP14&index=2&list=PLOHlR07ldo-WH2VcROlkA0FZ4xu6oSW6l
on Aug 22 `18
ravencrest
hey thats pretty cool ravencrest!
on Aug 22 `18
iedoc