I have created a Mesh Loader to convert .obj
files to a binary file so i can load them directly into my VertexBuffer
without parsing them first.
My code was working fine for the particular model i used for testing. But after testing some models i downloaded from the internet i noticed that those are not getting converted completely.
This is how i convert the .obj files (this is taken straight from the obj2vbo.cpp file Microsoft provided in a example, with some minor changes):
ifstream objFile(this->fileName.toStdString().c_str(), ifstream::in);
if (!objFile.is_open()) {
ui.statusBar->showMessage("Could not open obj file!");
return false;
}
vector<XMFLOAT3> positions;
vector<XMFLOAT3> normals;
vector<XMFLOAT2> texcoords;
vector<vector<IndexTriplet>> faces;
unsigned int lineNum = 1;
while (objFile.good()) {
string line;
getline(objFile, line);
istringstream lineStream(line);
if (lineStream.peek() != '#') {
string tag;
lineStream >> tag;
if (
tag.compare("mtllib") == 0 ||
tag.compare("o") == 0 ||
tag.compare("g") == 0 ||
tag.compare("usemtl") == 0 ||
tag.compare("s") == 0
) {
cout << "(" << lineNum << "): Warning: Does not support tag \"" << tag << "\"" << endl;
}
else if (tag.compare("v") == 0) {
XMFLOAT3 pos;
lineStream >> pos.x >> pos.y >> pos.z;
positions.push_back(pos);
}
else if (tag.compare("vn") == 0) {
XMFLOAT3 normal;
lineStream >> normal.x >> normal.y >> normal.z;
normals.push_back(normal);
}
else if (tag.compare("vt") == 0) {
XMFLOAT2 texcoord;
lineStream >> texcoord.x >> texcoord.y;
texcoords.push_back(texcoord);
}
else if (tag.compare("f") == 0) {
vector<IndexTriplet> face;
while (lineStream.good()) {
string tripletString;
lineStream >> tripletString;
if (tripletString.size() > 0) {
istringstream tripletStream(tripletString);
IndexTriplet triplet;
triplet.pos = 0;
triplet.norm = 0;
triplet.tex = 0;
tripletStream >> triplet.pos;
if (tripletStream.get() == '/') {
if (tripletStream.peek() != '/') {
tripletStream >> triplet.tex;
}
if (tripletStream.get() == '/') {
tripletStream >> triplet.norm;
}
}
face.push_back(triplet);
}
}
faces.push_back(face);
}
else if (tag.size() > 0) {
// unsupported tag
}
}
lineNum++;
}
objFile.close();
if (positions.size() == 0 || faces.size() == 0) {
ui.statusBar->showMessage("Error: File contains no geometry!");
return false;
}
// Validate Mesh
for (auto face = faces.begin(); face != faces.end(); face++) {
if (face->size() < 3) {
ui.statusBar->showMessage("Error: Face size < 3 invalid!");
return false;
}
for (auto triplet = face->begin(); triplet != face->end(); triplet++) {
if (triplet->pos > positions.size() || triplet->pos < 1) {
ui.statusBar->showMessage("Error: Position index out of range");
return false;
}
if (triplet->norm > normals.size() || triplet->norm < 1) {
ui.statusBar->showMessage("Error: Normal index out of range");
return false;
}
if (triplet->tex > texcoords.size() || triplet->tex < 1) {
ui.statusBar->showMessage("Error: Texcoord index out of range");
return false;
}
}
}
XMFLOAT3 boxMin = positions[faces[0][0].pos - 1];
XMFLOAT3 boxMax = boxMin;
for (auto face = faces.begin(); face != faces.end(); face++)
{
for (auto triplet = face->begin(); triplet != face->end(); triplet++)
{
XMFLOAT3 pos = positions[triplet->pos - 1];
boxMin.x = min(boxMin.x, pos.x);
boxMin.y = min(boxMin.y, pos.y);
boxMin.z = min(boxMin.z, pos.z);
boxMax.x = max(boxMax.x, pos.x);
boxMax.y = max(boxMax.y, pos.y);
boxMax.z = max(boxMax.z, pos.z);
}
}
XMFLOAT3 boxCenter;
XMStoreFloat3(&boxCenter, ((XMLoadFloat3(&boxMax) + XMLoadFloat3(&boxMin)) / 2.0f));
// If specified in the arguments, normalize geometry to fit within a unit cube
if (true) // Bool variable later...
{
float maxAxis = max(max(boxMax.x - boxMin.x, boxMax.y - boxMin.y), boxMax.z - boxMin.z);
for (auto pos = positions.begin(); pos != positions.end(); pos++)
{
XMStoreFloat3(&(*pos), ((XMLoadFloat3(&(*pos)) - XMLoadFloat3(&boxCenter)) / maxAxis));
}
}
// Generate missing normals
for (auto face = faces.begin(); face != faces.end(); face++)
{
XMFLOAT3 normal(0, 0, 0);
bool normalGenerated = false;
for (auto triplet = face->begin(); triplet != face->end(); triplet++)
{
if (!normalGenerated && triplet->norm == 0)
{
for (auto triplet = face->begin(); triplet != face->end(); triplet++)
{
XMFLOAT3 posThis = positions[triplet->pos - 1];
XMFLOAT3 posPrev = positions[(triplet == face->begin() ? (face->end() - 1)->pos : (triplet - 1)->pos) - 1];
XMFLOAT3 posNext = positions[(triplet == face->end() - 1 ? (face->begin())->pos : (triplet + 1)->pos) - 1];
XMStoreFloat3(
&normal,
XMVectorAdd(
XMLoadFloat3(&normal),
XMVector3Cross(XMVectorSubtract(XMLoadFloat3(&posNext), XMLoadFloat3(&posThis)), XMVectorSubtract(XMLoadFloat3(&posPrev), XMLoadFloat3(&posThis)))
)
);
triplet->norm = normals.size() + 1;
}
normals.push_back(normal);
normalGenerated = true;
}
}
}
// Fill in missing texture coordinates with (0, 0)
bool missingTexcoordCreated = false;
unsigned int missingTexcoordIndex = 0;
for (auto face = faces.begin(); face != faces.end(); face++)
{
for (auto triplet = face->begin(); triplet != face->end(); triplet++)
{
if (triplet->tex == 0)
{
if (!missingTexcoordCreated)
{
texcoords.push_back(XMFLOAT2(0.0f, 0.0f));
missingTexcoordIndex = texcoords.size();
missingTexcoordCreated = true;
}
triplet->tex = missingTexcoordIndex;
}
}
}
// Generate unique vertices and convert counter-clockwise faces to clockwise triangles
vector<Vertex> vertices;
vector<unsigned short> indices;
map<IndexTriplet, unsigned short> tripletIndices;
for (auto face = faces.begin(); face != faces.end(); face++)
{
for (auto triplet = face->begin(); triplet != face->end(); triplet++)
{
if (tripletIndices.find(*triplet) == tripletIndices.end())
{
tripletIndices[*triplet] = static_cast<unsigned short>(vertices.size());
Vertex vertex;
vertex.position = positions[triplet->pos - 1];
vertex.normals = normals[triplet->norm - 1];
vertex.uv = texcoords[triplet->tex - 1];
vertices.push_back(vertex);
}
if (triplet >= face->begin() + 2)
{
indices.push_back(tripletIndices[*face->begin()]);
indices.push_back(tripletIndices[*triplet]);
indices.push_back(tripletIndices[*(triplet - 1)]);
}
}
}
// Dump vertex and index data to the output VBO file
ofstream vboFile("binaryMesh.bin", ofstream::out | ofstream::binary);
if (!vboFile.is_open())
{
ui.statusBar->showMessage("Could not open file for writing!");
return false;
}
unsigned int numVertices = vertices.size();
unsigned int numIndices = indices.size();
vboFile.write(reinterpret_cast<char*>(&numVertices), sizeof(unsigned int));
vboFile.write(reinterpret_cast<char*>(&numIndices), sizeof(unsigned int));
vboFile.write(reinterpret_cast<char*>(&vertices[0]), sizeof(Vertex)* vertices.size());
vboFile.write(reinterpret_cast<char*>(&indices[0]), sizeof(unsigned short)* indices.size());
vboFile.close();
And this is how i read it again:
void createMeshData(_In_ byte* meshData, _Out_ VertexBuffer** vertexBuffer, _Out_ uint32* vertexCount)
{
*vertexCount = *reinterpret_cast<uint32*>(meshData);
BasicVertex* vertices = reinterpret_cast<BasicVertex*>(meshData + sizeof(uint32)* 2);
*vertexBuffer = this->m_renderer->createVertexBuffer(vertices, sizeof(BasicVertex) * (*vertexCount), false);
}
I already made sure that both structs BasicVertex
and Vertex
are the same. This is a example model (Hulk) and how it looks rendered:
It seems that not only the vertices are wrong but the texture coordinates are aswell, but this is another problem.. If you need any more information, please let me know. Thanks for any tip