DirectX 11 MD5 Loading tutorial Index Buffer too small
I've been going over the MD5 model loading tutorial and I decided to put the code into it's own class. But when I get round to drawing the object, it only draws the body of my mesh. .[http://][https://i.imgur.com/mZeVNJs.png] When I tried the code as is, with an InitIndexBuffer and InitVertexBuffer method inside the Load function, the code break at IASetIndexBuffer(MD5Model.subsets[i].indexBuff, DXGI_FORMAT_R32_UINT, 0); When I try the code with the private ModelSubset variable it works but with only half the model. **Here's the MD5Model.h** #pragma once #include <windows.h> #include <d3d11_1.h> #include <d3dcompiler.h> #include <directxmath.h> #include <vector> #include <fstream> #include "Structures.h" #include "DDSTextureLoader.h" using namespace DirectX; using namespace std; class MD5Model { public: MD5Model(); ~MD5Model(); // Load an animation from the animation file HRESULT LoadMD5Model(wstring filename, Model3D& MD5Model, vector<ID3D11ShaderResourceView*>& shaderResourceViewArray, ID3D11Device* d3d11Device, ID3D11DeviceContext* d3d11DeviceContext, vector<wstring> texFileNameArray); HRESULT InitIndexBuffer(ID3D11Device* d3d11Device, ModelSubset subset); HRESULT InitVertexBuffer(ID3D11Device* d3d11Device, ModelSubset subset); void Update(float fDeltaTime); void Render(); void Draw(ID3D11DeviceContext* immediateContext, Model3D& MD5Model); UINT stride = sizeof(Vertex); private: //ModelSubset subset; //UINT stride = 0; UINT offset = 0; unsigned int numIndices; }; **And the MD5Model.cpp** #include "MD5Model.h" #include <iostream> #include <string> MD5Model::MD5Model() { } MD5Model::~MD5Model() { } HRESULT MD5Model::LoadMD5Model(wstring filename, Model3D& MD5Model, vector<ID3D11ShaderResourceView*>& shaderResourceViewArray, ID3D11Device* d3d11Device, ID3D11DeviceContext* d3d11DeviceContext, vector<wstring> texFileNameArray) { HRESULT hr; wifstream fileIn(filename.c_str()); // Open file wstring checkString; // Stores the next string from our file fileIn >> checkString; if (fileIn) // Check if the file was opened { while (fileIn) // Loop until the end of the file is reached { fileIn >> checkString; // Get next string from file if (checkString == L"MD5Version") // Get MD5 version (this function supports version 10) { /*fileIn >> checkString; MessageBox(0, checkString.c_str(), //display message L"MD5Version", MB_OK);*/ } else if (checkString == L"commandline") { std::getline(fileIn, checkString); // Ignore the rest of this line } else if (checkString == L"numJoints") { fileIn >> MD5Model.numJoints; // Store number of joints } else if (checkString == L"numMeshes") { fileIn >> MD5Model.numSubsets; // Store number of meshes or subsets which we will call them } else if (checkString == L"joints") { Joint tempJoint; fileIn >> checkString; // Skip the "{" for (int i = 0; i < MD5Model.numJoints; i++) { fileIn >> tempJoint.name; // Store joints name // Sometimes the names might contain spaces. If that is the case, we need to continue // to read the name until we get to the closing " (quotation marks) if (tempJoint.name[tempJoint.name.size() - 1] != '"') { wchar_t checkChar; bool jointNameFound = false; while (!jointNameFound) { checkChar = fileIn.get(); if (checkChar == '"') jointNameFound = true; tempJoint.name += checkChar; } } fileIn >> tempJoint.parentID; // Store Parent joint's ID fileIn >> checkString; // Skip the "(" // Store position of this joint (swap y and z axis if model was made in RH Coord Sys) fileIn >> tempJoint.pos.x >> tempJoint.pos.z >> tempJoint.pos.y; fileIn >> checkString >> checkString; // Skip the ")" and "(" // Store orientation of this joint fileIn >> tempJoint.orientation.x >> tempJoint.orientation.z >> tempJoint.orientation.y; // Remove the quotation marks from joints name tempJoint.name.erase(0, 1); //tempJoint.name.erase(tempJoint.name.size()-1, 1); // Compute the w axis of the quaternion (The MD5 model uses a 3D vector to describe the // direction the bone is facing. However, we need to turn this into a quaternion, and the way // quaternions work, is the xyz values describe the axis of rotation, while the w is a value // between 0 and 1 which describes the angle of rotation) float t = 1.0f - (tempJoint.orientation.x * tempJoint.orientation.x) - (tempJoint.orientation.y * tempJoint.orientation.y) - (tempJoint.orientation.z * tempJoint.orientation.z); if (t < 0.0f) { tempJoint.orientation.w = 0.0f; } else { tempJoint.orientation.w = -sqrtf(t); } getline(fileIn, checkString); // Skip rest of this line MD5Model.joints.push_back(tempJoint); // Store the joint into this models joint vector } fileIn >> checkString; // Skip the "}" } else if (checkString == L"mesh") { ModelSubset subset; int numVerts, numTris, numWeights; fileIn >> checkString; // Skip the "{" fileIn >> checkString; while (checkString != L"}") // Read until '}' { // In this lesson, for the sake of simplicity, we will assume a textures filename is givin here. // Usually though, the name of a material (stored in a material library. Think back to the lesson on // loading .obj files, where the material library was contained in the file .mtl) is givin. Let this // be an exercise to load the material from a material library such as obj's .mtl file, instead of // just the texture like we will do here. if (checkString == L"shader") // Load the texture or material { wstring fileNamePath; fileIn >> fileNamePath; // Get texture's filename // Take spaces into account if filename or material name has a space in it if (fileNamePath[fileNamePath.size() - 1] != '"') { wchar_t checkChar; bool fileNameFound = false; while (!fileNameFound) { checkChar = fileIn.get(); if (checkChar == '"') fileNameFound = true; fileNamePath += checkChar; } } // Remove the quotation marks from texture path fileNamePath.erase(0, 1); fileNamePath.erase(fileNamePath.size() - 1, 1); //check if this texture has already been loaded bool alreadyLoaded = false; for (int i = 0; i < texFileNameArray.size(); ++i) { if (fileNamePath == texFileNameArray[i]) { alreadyLoaded = true; subset.texArrayIndex = i; } } //if the texture is not already loaded, load it now if (!alreadyLoaded) { ID3D11ShaderResourceView* tempMeshSRV; hr = CreateDDSTextureFromFile(d3d11Device, fileNamePath.c_str(), NULL, &tempMeshSRV); if (SUCCEEDED(hr)) { texFileNameArray.push_back(fileNamePath.c_str()); subset.texArrayIndex = shaderResourceViewArray.size(); shaderResourceViewArray.push_back(tempMeshSRV); } else { MessageBox(0, fileNamePath.c_str(), L"Could Not Open:", MB_OK); return false; } } getline(fileIn, checkString); // Skip rest of this line } else if (checkString == L"numverts") { fileIn >> numVerts; // Store number of vertices getline(fileIn, checkString); // Skip rest of this line for (int i = 0; i < numVerts; i++) { Vertex tempVert; fileIn >> checkString // Skip "vert # (" >> checkString >> checkString; fileIn >> tempVert.texCoord.x // Store tex coords >> tempVert.texCoord.y; fileIn >> checkString; // Skip ")" fileIn >> tempVert.StartWeight; // Index of first weight this vert will be weighted to fileIn >> tempVert.WeightCount; // Number of weights for this vertex getline(fileIn, checkString); // Skip rest of this line subset.vertices.push_back(tempVert); // Push back this vertex into subsets vertex vector } } else if (checkString == L"numtris") { fileIn >> numTris; subset.numTriangles = numTris; getline(fileIn, checkString); // Skip rest of this line for (int i = 0; i < numTris; i++) // Loop through each triangle { DWORD tempIndex; fileIn >> checkString; // Skip "tri" fileIn >> checkString; // Skip tri counter for (int k = 0; k < 3; k++) // Store the 3 indices { fileIn >> tempIndex; subset.indices.push_back(tempIndex); } getline(fileIn, checkString); // Skip rest of this line } } else if (checkString == L"numweights") { fileIn >> numWeights; getline(fileIn, checkString); // Skip rest of this line for (int i = 0; i < numWeights; i++) { Weight tempWeight; fileIn >> checkString >> checkString; // Skip "weight #" fileIn >> tempWeight.jointID; // Store weight's joint ID fileIn >> tempWeight.bias; // Store weight's influence over a vertex fileIn >> checkString; // Skip "(" fileIn >> tempWeight.pos.x // Store weight's pos in joint's local space >> tempWeight.pos.z >> tempWeight.pos.y; getline(fileIn, checkString); // Skip rest of this line subset.weights.push_back(tempWeight); // Push back tempWeight into subsets Weight array } } else getline(fileIn, checkString); // Skip anything else fileIn >> checkString; // Skip "}" } //*** find each vertex's position using the joints and weights ***// for (int i = 0; i < subset.vertices.size(); ++i) { Vertex tempVert = subset.vertices[i]; tempVert.pos = XMFLOAT3(0, 0, 0); // Make sure the vertex's pos is cleared first // Sum up the joints and weights information to get vertex's position for (int j = 0; j < tempVert.WeightCount; ++j) { Weight tempWeight = subset.weights[tempVert.StartWeight + j]; Joint tempJoint = MD5Model.joints[tempWeight.jointID]; // Convert joint orientation and weight pos to vectors for easier computation // When converting a 3d vector to a quaternion, you should put 0 for "w", and // When converting a quaternion to a 3d vector, you can just ignore the "w" XMVECTOR tempJointOrientation = XMVectorSet(tempJoint.orientation.x, tempJoint.orientation.y, tempJoint.orientation.z, tempJoint.orientation.w); XMVECTOR tempWeightPos = XMVectorSet(tempWeight.pos.x, tempWeight.pos.y, tempWeight.pos.z, 0.0f); // We will need to use the conjugate of the joint orientation quaternion // To get the conjugate of a quaternion, all you have to do is inverse the x, y, and z XMVECTOR tempJointOrientationConjugate = XMVectorSet(-tempJoint.orientation.x, -tempJoint.orientation.y, -tempJoint.orientation.z, tempJoint.orientation.w); // Calculate vertex position (in joint space, eg. rotate the point around (0,0,0)) for this weight using the joint orientation quaternion and its conjugate // We can rotate a point using a quaternion with the equation "rotatedPoint = quaternion * point * quaternionConjugate" XMFLOAT3 rotatedPoint; XMStoreFloat3(&rotatedPoint, XMQuaternionMultiply(XMQuaternionMultiply(tempJointOrientation, tempWeightPos), tempJointOrientationConjugate)); // Now move the verices position from joint space (0,0,0) to the joints position in world space, taking the weights bias into account // The weight bias is used because multiple weights might have an effect on the vertices final position. Each weight is attached to one joint. tempVert.pos.x += (tempJoint.pos.x + rotatedPoint.x) * tempWeight.bias; tempVert.pos.y += (tempJoint.pos.y + rotatedPoint.y) * tempWeight.bias; tempVert.pos.z += (tempJoint.pos.z + rotatedPoint.z) * tempWeight.bias; // Basically what has happened above, is we have taken the weights position relative to the joints position // we then rotate the weights position (so that the weight is actually being rotated around (0, 0, 0) in world space) using // the quaternion describing the joints rotation. We have stored this rotated point in rotatedPoint, which we then add to // the joints position (because we rotated the weight's position around (0,0,0) in world space, and now need to translate it // so that it appears to have been rotated around the joints position). Finally we multiply the answer with the weights bias, // or how much control the weight has over the final vertices position. All weight's bias effecting a single vertex's position // must add up to 1. } subset.positions.push_back(tempVert.pos); // Store the vertices position in the position vector instead of straight into the vertex vector // since we can use the positions vector for certain things like collision detection or picking // without having to work with the entire vertex structure. } // Put the positions into the vertices for this subset for (int i = 0; i < subset.vertices.size(); i++) { subset.vertices[i].pos = subset.positions[i]; } //*** Calculate vertex normals using normal averaging ***/// vector<XMFLOAT3> tempNormal; //normalized and unnormalized normals XMFLOAT3 unnormalized = XMFLOAT3(0.0f, 0.0f, 0.0f); //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 for (int i = 0; i < subset.numTriangles; ++i) { //Get the vector describing one edge of our triangle (edge 0,2) vecX = subset.vertices[subset.indices[(i * 3)]].pos.x - subset.vertices[subset.indices[(i * 3) + 2]].pos.x; vecY = subset.vertices[subset.indices[(i * 3)]].pos.y - subset.vertices[subset.indices[(i * 3) + 2]].pos.y; vecZ = subset.vertices[subset.indices[(i * 3)]].pos.z - subset.vertices[subset.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 = subset.vertices[subset.indices[(i * 3) + 2]].pos.x - subset.vertices[subset.indices[(i * 3) + 1]].pos.x; vecY = subset.vertices[subset.indices[(i * 3) + 2]].pos.y - subset.vertices[subset.indices[(i * 3) + 1]].pos.y; vecZ = subset.vertices[subset.indices[(i * 3) + 2]].pos.z - subset.vertices[subset.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); } //Compute vertex normals (normal Averaging) XMVECTOR normalSum = 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 < subset.vertices.size(); ++i) { //Check which triangles use this vertex for (int j = 0; j < subset.numTriangles; ++j) { if (subset.indices[j * 3] == i || subset.indices[(j * 3) + 1] == i || subset.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 facesUsing++; } } //Get the actual normal by dividing the normalSum by the number of faces sharing the vertex normalSum = normalSum / facesUsing; //Normalize the normalSum vector normalSum = XMVector3Normalize(normalSum); //Store the normal and tangent in our current vertex subset.vertices[i].normal.x = -XMVectorGetX(normalSum); subset.vertices[i].normal.y = -XMVectorGetY(normalSum); subset.vertices[i].normal.z = -XMVectorGetZ(normalSum); //Clear normalSum, facesUsing for next vertex normalSum = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); facesUsing = 0; } InitIndexBuffer(d3d11Device, subset); InitVertexBuffer(d3d11Device, subset); // Push back the temp subset into the models subset vector MD5Model.subsets.push_back(subset); ////// Create index buffer //D3D11_BUFFER_DESC indexBufferDesc; //ZeroMemory(&indexBufferDesc, sizeof(indexBufferDesc)); //indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; //indexBufferDesc.ByteWidth = sizeof(DWORD) * subset.numTriangles * 3; //indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; //indexBufferDesc.CPUAccessFlags = 0; //indexBufferDesc.MiscFlags = 0; // //D3D11_SUBRESOURCE_DATA iinitData; //iinitData.pSysMem = &subset.indices[0]; //d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &subset.indexBuff); // ////Create Vertex Buffer //D3D11_BUFFER_DESC vertexBufferDesc; //ZeroMemory(&vertexBufferDesc, sizeof(vertexBufferDesc)); //vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC; // We will be updating this buffer, so we must set as dynamic //vertexBufferDesc.ByteWidth = sizeof(Vertex) * subset.vertices.size(); //vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; //vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; // Give CPU power to write to buffer //vertexBufferDesc.MiscFlags = 0; // //D3D11_SUBRESOURCE_DATA vertexBufferData; //ZeroMemory(&vertexBufferData, sizeof(vertexBufferData)); //vertexBufferData.pSysMem = &subset.vertices[0]; //hr = d3d11Device->CreateBuffer(&vertexBufferDesc, &vertexBufferData, &subset.vertBuff); //Set the grounds index buffer //d3d11DeviceContext->IASetIndexBuffer(MD5Model.subsets[i].indexBuff, DXGI_FORMAT_R32_UINT, 0); ////Set the grounds vertex buffer //d3d11DeviceContext->IASetVertexBuffers(0, 1, &MD5Model.subsets[i].vertBuff, &stride, 0); } } } else { // create message wstring message = L"Could not open: "; message += filename; MessageBox(0, message.c_str(), L"Error", MB_OK); return false; } return hr; } HRESULT MD5Model::InitIndexBuffer(ID3D11Device* d3d11Device, ModelSubset subset) { HRESULT hr; // Create index buffer D3D11_BUFFER_DESC indexBufferDesc; ZeroMemory(&indexBufferDesc, sizeof(indexBufferDesc)); indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; indexBufferDesc.ByteWidth = sizeof(DWORD) * (subset.numTriangles * 3); indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDesc.CPUAccessFlags = 0; indexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA iinitData; iinitData.pSysMem = &subset.indices[0]; hr = d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &subset.indexBuff); if (FAILED(hr)) return hr; } HRESULT MD5Model::InitVertexBuffer(ID3D11Device* d3d11Device, ModelSubset subset) { HRESULT hr; // Create Vertex Buffer D3D11_BUFFER_DESC vertexBufferDesc; ZeroMemory(&vertexBufferDesc, sizeof(vertexBufferDesc)); vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC; // We will be updating this buffer, so we must set as dynamic vertexBufferDesc.ByteWidth = sizeof(Vertex) * subset.vertices.size(); vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; // Give CPU power to write to buffer vertexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA vertexBufferData; ZeroMemory(&vertexBufferData, sizeof(vertexBufferData)); vertexBufferData.pSysMem = &subset.vertices[0]; hr = d3d11Device->CreateBuffer(&vertexBufferDesc, &vertexBufferData, &subset.vertBuff); if (FAILED(hr)) return hr; } void MD5Model::Draw(ID3D11DeviceContext* immediateContext, Model3D& MD5Model) { for (int i = 0; i < MD5Model.numSubsets; i++) { //Set the grounds index buffer immediateContext->IASetIndexBuffer(MD5Model.subsets[i].indexBuff, DXGI_FORMAT_R32_UINT, 0); //Set the grounds vertex buffer immediateContext->IASetVertexBuffers(0, 1, &MD5Model.subsets[i].vertBuff, &stride, &offset); //immediateContext->PSSetShaderResources(0, 1, &cInfo._pModelRV[characterModel.subsets[i].texArrayIndex]); immediateContext->DrawIndexed(MD5Model.subsets[i].indices.size(), 0, 0); } }
Are you using iedoc's tutorial model?
on May 02 `16
IamU4
Can you please try out this line of code : IASetIndexBuffer(MD5Model.subsets[i].indexBuff.data(), DXGI_FORMAT_R32_UINT, 0);
on May 02 `16
IamU4
Sign in to comment
Hey! Are you sure that MD5Model.numSubsets is the value that you want? Please use Graphics Debugger which is in visual studio compiler. In there just check your vertex and index buffers, maybe some values are uninitialized or not the value that you want.
Yes I am using iedoc's tutorial. I tried IASetIndexBuffer(MD5Model.subsets[i].indexBuff.data(), DXGI_FORMAT_R32_UINT, 0); but it said it required a class type. Having never used the Graphics Debugger before, aren't I supposed to see a Frame Analysis tab and/or a Graphics Log? When I capture a Frame I can't seem to see anything appear.
on May 02 `16
JJ_Flame
But are you using iedoc's model? I'll try to find tutorial which shows how to use graphics debugger, it's really cool thing and probably will solve your problem
on May 02 `16
IamU4
https://msdn.microsoft.com/en-us/library/dn217886.aspx Let me know if you understood everything correctly.
on May 02 `16
IamU4
Ah no I'm using a separate model. I'll use that model and follow the Graphics Debugger tutorial and let you know in a short while. Thanks.
on May 02 `16
JJ_Flame
Hi, I've used the boy model and checked the Graphics Debugger, what exactly am I looking for? A few of the Draw Indexed call correctly but most don't https://i.imgur.com/HSCSszH.png
on May 02 `16
JJ_Flame
now see obj:(somekindofnumber). Press that and that will lead to some kind of page. Send me an image of that page, I'll tell ya what to press. I don't have VS atm
on May 02 `16
IamU4
So yea, press obj:24 if you want to check your vertex buffer, you need to set your buffer's format, f.e(float4, float4, float4). If you want to check index buffer, it's obj:23 and set format (float). Just check in there if values are right and if your buffers size is right.
on May 02 `16
IamU4
The values in the vertex buffer https://i.imgur.com/1w87doX.png don't seem to match the MD5mesh https://i.imgur.com/dFChySa.png
on May 02 `16
JJ_Flame
And it only draws the head of the model no matter which DrawIndexed I select.
on May 02 `16
JJ_Flame
So here you go dude, you found out what's wrong with your code, now try to find the solution. I'm busy atm, but I'll try to help you tomorrow, in the meantime try to fix it by yourself. Graphics debugger is really fucking helpful.
on May 02 `16
IamU4