如何将简单的.obj文件解析为三角形? [英] How can I parse a simple .obj file into triangles?

查看:63
本文介绍了如何将简单的.obj文件解析为三角形?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

  char lineHeader [512];//读取该行的第一个字int res = fscanf(file,%s",lineHeader);如果(res == EOF)休息;//EOF//else:解析lineHeader如果(strcmp(lineHeader,"v")== 0){glm :: vec3顶点;fscanf(文件,%f%f%f \ n",& vertex.x,& vertex.y,& vertex.z);vertex.x * =比例;vertex.y * =比例;vertex.z * =比例;temp_vertices.push_back(vertex);}否则,如果(strcmp(lineHeader,"vt")== 0){glm :: vec2 uv;fscanf(file,%f%f \ n",& uv.x,& uv.y);uv.y = -uv.y;//反转V坐标,因为我们将仅使用DDS纹理,该纹理将被反转.如果要使用TGA或BMP装载机,则卸下.temp_uvs.push_back(uv);}否则,如果(strcmp(lineHeader,"vn")== 0){glm :: vec3正常;fscanf(file,%f%f%f \ n",& normal.x,& normal.y,& normal.z);temp_normals.push_back(正常);}否则,如果(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,文件);int matchs = 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(matchs!= 9){vertexIndex [3] = {0},uvIndex [3] = {0},normalIndex [3] = {0};matchs = sscanf(stupidBuffer,%d//%d%d//%d%d//%d \ n",& vertexIndex [0],& normalIndex [0],& vertexIndex [1],& normalIndex [1],& vertexIndex [2],& normalIndex [2]);if(matchs!= 6){vertexIndex [3] = {0},uvIndex [3] = {0},normalIndex [3] = {0};matchs = sscanf(stupidBuffer,%d%d%d \ n",& vertexIndex [0],& vertexIndex [1],& vertexIndex [2]);if(matchs!= 3){printf(无法读取文件\ n");fclose(文件);返回false;}}}} 

这是我的三角课

  class三角{上市:向量p0,p1,p2;矢量颜色;向量法线(无效);}; 

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

解决方案

您已经完成了90%的工作.在您的人脸元素解析器中,使用每个人脸顶点的解析位置/法线/texcoord索引从 temp _ * 向量中获取信息.如果面元素具有三个顶点,则可以按原样发射三角形,否则对于4个以上的顶点,我通常假定所得多边形为凸&.共面,在这种情况下,您可以通过假装是三角扇来进行三角剖分.

一起:

  struct顶点{glm :: vec3位置;glm :: vec2 texcoord;glm :: vec3正常;};结构VertRef{VertRef(int v,int vt,int vn):v(v),vt(vt),vn(vn){}int v,vt,vn;};std :: vector<顶点>LoadOBJ(std :: istream& in){std :: vector<顶点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;//顶点if(lineType =="v"){浮点数x = 0,y = 0,z = 0,w = 1;lineSS>>x>>y>>z>>w;position.push_back(glm :: vec4(x,y,z,w));}//质地if(lineType =="vt"){浮点型u = 0,v = 0,w = 0;lineSS>>u>>v>>w;texcoords.push_back(glm :: vec3(u,v,w));}//普通的if(lineType =="vn"){浮点型i = 0,j = 0,k = 0;lineSS>>我>>j>>k;normals.push_back(glm :: normalize(glm :: vec3(i,j,k))));}//多边形if(lineType =="f"){std :: vector<VertRef>裁判;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:position.size()+ v);vt =(vt> = 0?vt:texcoords.size()+ vt);vn =(vn> = 0?vn:normals.size()+ vn);refs.push_back(VertRef(v,vt,vn));}//三角剖分,假设n> 3-gons是凸且共面的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_Normalglm :: vec3 U(position [p [1]-> v]-position [p [0]-> v]);glm :: vec3 V(position [p [2]-> v]-positions [p [0]-> v]);glm :: vec3 faceNormal = glm :: normalize(glm :: cross(U,V));for(size_t j = 0; j< 3; ++ j){顶点vert;vert.position = glm :: vec3(position [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);}}}}返回顶点} 

请参见此处的完整程序.

I'm trying to implement a ray caster and I'm starting out with simple .obj files (utah-teapot) and currently I only made classes for Spheres and Triangles, I basically have all the functions for the intersections, generating view rays, etc.. all ready but I just can't seem to be able to parse the .obj file into triangles (three vectors each) so I can have the ray casting possible on custom .obj files instead of just spheres.

This is my current .obj file parser (didn't include the full working code here)

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

This is my triangle class

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

I can't figure out how to parse the info from the .obj file into triangles that consist of three 3d vectors (points). I don't need code, I just need to understand how (if possible?) to parse all that info into triangles. Any other ideas are welcome. I want to make a simple puzzle game on the long run but I'm just taking it a step at a time.

解决方案

You're 90% of the way there. In your face element parser use the parsed position/normal/texcoord indexes of each face-vertex to grab info from the temp_* vectors. If a face element has three vertices you can emit a triangle as-is, otherwise for 4+ vertices I've generally assumed the the resulting polygon is convex & co-planar, in which case you can triangulate by pretending it's a triangle fan.

All together:

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

See the full program here.

这篇关于如何将简单的.obj文件解析为三角形?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆