将.obj转换为二进制格式(VBO)

时间:2014-06-14 15:41:14

标签: c++ directx vertex-buffer .obj

我创建了一个Mesh Loader来将.obj文件转换为二进制文件,这样我就可以直接将它们加载到我的VertexBuffer中,而无需先解析它们。 我的代码适用于我用于测试的特定模型。但是在测试了我从互联网上下载的一些模型后,我注意到那些模型没有完全转换。

这就是我如何转换.obj文件(这是直接来自微软在一个例子中提供的obj2vbo.cpp文件,稍作修改):

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();

这就是我再次阅读它的方式:

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);
}

我已经确定结构BasicVertexVertex是相同的。这是一个示例模型(Hulk)以及它的外观呈现方式:

enter image description here

似乎不仅顶点错了,而且纹理坐标也是如此,但这是另一个问题。 如果您需要更多信息,请告诉我。 谢谢你的提示

0 个答案:

没有答案