如何将简单的.obj文件解析为三角形?

时间:2018-10-15 21:23:18

标签: c++ opengl glm-math wavefront

我正在尝试实现射线投射器,并且我从简单的.obj文件(犹他-茶壶)开始,目前我只为Spheres和Triangles创建类,我基本上具有交集的所有功能,生成查看光线等。所有这些都准备好了,但是我似乎无法将.obj文件解析为三角形(每个三个向量),因此我可以在自定义.obj文件而不是球体上进行光线投射。 / p>

这是我当前的.obj文件解析器(此处未包含完整的工作代码)

char lineHeader[512];
// read the first word of the line
int res = fscanf(file, "%s", lineHeader);
if (res == EOF)
    break; // EOF

// else : parse lineHeader

if (strcmp(lineHeader, "v") == 0) {
    glm::vec3 vertex;
    fscanf(file, "%f %f %f\n", &vertex.x, &vertex.y, &vertex.z);
    vertex.x *= scale;
    vertex.y *= scale;
    vertex.z *= scale;
    temp_vertices.push_back(vertex);
}
else if (strcmp(lineHeader, "vt") == 0) {
    glm::vec2 uv;
    fscanf(file, "%f %f\n", &uv.x, &uv.y);
    uv.y = -uv.y; // Invert V coordinate since we will only use DDS texture, which are inverted. Remove if you want to use TGA or BMP loaders.
    temp_uvs.push_back(uv);
}
else if (strcmp(lineHeader, "vn") == 0) {
    glm::vec3 normal;
    fscanf(file, "%f %f %f\n", &normal.x, &normal.y, &normal.z);
    temp_normals.push_back(normal);
}
else if (strcmp(lineHeader, "f") == 0) {
    std::string vertex1, vertex2, vertex3;
    unsigned int vertexIndex[3] = { 0 }, uvIndex[3] = { 0 }, normalIndex[3] = { 0 };
    char stupidBuffer[1024];
    fgets(stupidBuffer, 1024, file);
    int matches = sscanf(stupidBuffer, "%d/%d/%d %d/%d/%d %d/%d/%d\n", &vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex[1], &uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2], &normalIndex[2]);
    if (matches != 9) {
        vertexIndex[3] = { 0 }, uvIndex[3] = { 0 }, normalIndex[3] = { 0 };
        matches = sscanf(stupidBuffer, "%d//%d %d//%d %d//%d\n", &vertexIndex[0], &normalIndex[0], &vertexIndex[1], &normalIndex[1], &vertexIndex[2], &normalIndex[2]);
        if (matches != 6) {
            vertexIndex[3] = { 0 }, uvIndex[3] = { 0 }, normalIndex[3] = { 0 };
            matches = sscanf(stupidBuffer, "%d %d %d\n", &vertexIndex[0], &vertexIndex[1], &vertexIndex[2]);
            if (matches != 3) {
                printf("File can't be read \n");
                fclose(file);
                return false;
            }
        }
    }
}

这是我的三角课

class Triangle {
public:
    Vector p0, p1, p2;
    Vector color;
    Vector normal(void);
};

我不知道如何将.obj文件中的信息解析为由三个3d向量(点)组成的三角形。我不需要代码,我只需要了解如何(如果可能的话)将所有信息解析为三角形。 任何其他想法都欢迎。从长远来看,我想制作一个简单的益智游戏,但一次只是一步。

1 个答案:

答案 0 :(得分:1)

您已到达目的地的90%。在您的人脸元素解析器中,使用每个人脸顶点的已解析位置/法线/ texcoord索引从temp_*向量中获取信息。如果一个面元素具有三个顶点,则可以按原样发射一个三角形,否则对于4个以上的顶点,我通常假定结果多边形是凸面和共面的,在这种情况下,您可以triangulate by pretending it's a triangle fan

一起:

struct Vertex
{
    glm::vec3 position;
    glm::vec2 texcoord;
    glm::vec3 normal;
};

struct VertRef
{
    VertRef( int v, int vt, int vn ) : v(v), vt(vt), vn(vn) { }
    int v, vt, vn;
};

std::vector< Vertex > LoadOBJ( std::istream& in )
{
    std::vector< Vertex > verts;

    std::vector< glm::vec4 > positions( 1, glm::vec4( 0, 0, 0, 0 ) );
    std::vector< glm::vec3 > texcoords( 1, glm::vec3( 0, 0, 0 ) );
    std::vector< glm::vec3 > normals( 1, glm::vec3( 0, 0, 0 ) );
    std::string lineStr;
    while( std::getline( in, lineStr ) )
    {
        std::istringstream lineSS( lineStr );
        std::string lineType;
        lineSS >> lineType;

        // vertex
        if( lineType == "v" )
        {
            float x = 0, y = 0, z = 0, w = 1;
            lineSS >> x >> y >> z >> w;
            positions.push_back( glm::vec4( x, y, z, w ) );
        }

        // texture
        if( lineType == "vt" )
        {
            float u = 0, v = 0, w = 0;
            lineSS >> u >> v >> w;
            texcoords.push_back( glm::vec3( u, v, w ) );
        }

        // normal
        if( lineType == "vn" )
        {
            float i = 0, j = 0, k = 0;
            lineSS >> i >> j >> k;
            normals.push_back( glm::normalize( glm::vec3( i, j, k ) ) );
        }

        // polygon
        if( lineType == "f" )
        {
            std::vector< VertRef > refs;
            std::string refStr;
            while( lineSS >> refStr )
            {
                std::istringstream ref( refStr );
                std::string vStr, vtStr, vnStr;
                std::getline( ref, vStr, '/' );
                std::getline( ref, vtStr, '/' );
                std::getline( ref, vnStr, '/' );
                int v = atoi( vStr.c_str() );
                int vt = atoi( vtStr.c_str() );
                int vn = atoi( vnStr.c_str() );
                v  = (  v >= 0 ?  v : positions.size() +  v );
                vt = ( vt >= 0 ? vt : texcoords.size() + vt );
                vn = ( vn >= 0 ? vn : normals.size()   + vn );
                refs.push_back( VertRef( v, vt, vn ) );
            }

            // triangulate, assuming n>3-gons are convex and coplanar
            for( size_t i = 1; i+1 < refs.size(); ++i )
            {
                const VertRef* p[3] = { &refs[0], &refs[i], &refs[i+1] };

                // http://www.opengl.org/wiki/Calculating_a_Surface_Normal
                glm::vec3 U( positions[ p[1]->v ] - positions[ p[0]->v ] );
                glm::vec3 V( positions[ p[2]->v ] - positions[ p[0]->v ] );
                glm::vec3 faceNormal = glm::normalize( glm::cross( U, V ) );

                for( size_t j = 0; j < 3; ++j )
                {
                    Vertex vert;
                    vert.position = glm::vec3( positions[ p[j]->v ] );
                    vert.texcoord = glm::vec2( texcoords[ p[j]->vt ] );
                    vert.normal = ( p[j]->vn != 0 ? normals[ p[j]->vn ] : faceNormal );
                    verts.push_back( vert );
                }
            }
        }
    }

    return verts;
}

请参见the full program here