如何基于Wavefront(.obj)文件中提供的纹理索引对纹理位置进行排序? [英] How do I sort the texture positions based on the texture indices given in a Wavefront (.obj) file?

查看:115
本文介绍了如何基于Wavefront(.obj)文件中提供的纹理索引对纹理位置进行排序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在尝试为OpenGL项目制作Wavefront(.obj)文件加载器.我目前使用的方法逐行移动,并在矢量(std :: vectors)中分离顶点位置,纹理位置和法线位置,并将它们的索引(顶点,纹理和法线索引)存储在三个单独的位置向量(从文件的"f"行开始,针对每个面).

I'm currently trying to make a Wavefront (.obj) file loader for an OpenGL project. The method I'm currently using goes line-by-line and separates the vertex positions, texture positions and normal positions in vectors (std::vectors) and I'm storing their indices (vertex, texture and normal indices) in three separate vectors (from the 'f' lines of the file, for each face).

我无法根据纹理索引对充满纹理坐标的向量进行排序.我可以在正确的位置渲染顶点,因为我的加载程序"类需要索引,但是我无法弄清楚如何以任何方式对纹理坐标进行排序,因此纹理看起来像是在某些三角形上偏移结果.

I'm having trouble sorting the vector full of texture coordinates based on the texture indices. I'm able to render the vertices in the correct positions because my 'loader' class calls for the indices, but I can't figure out how to sort the texture coordinates in any way, so the textures look offset on some triangles as a result.

具有偏移纹理的多维数据集的图像:

Image of cube with offset textures:

纹理(.png)的图像,其在每个面上的显示方式:

Image of texture (.png), how it should be on each face:

这是指向.obj文件和.mtl文件的链接. Google云端硬盘.

Here is a link to both the .obj file and .mtl file. Google Drive.

这是我的OBJLoader.cpp文件:

Here is my OBJLoader.cpp file:

    rawObj.open(filePath); // Open file

    while (!rawObj.eof()) {
        getline(rawObj, line); // Read line

        // Read values from each line 
        // starting with a 'v' for 
        // the vertex positions with
        // a custom function (gets the word in a line
        // at position i)

        if (strWord(line, 1) == "v") {   
            for (int i = 2; i <= 4; i++) {
                std::string temp;
                temp = strWord(line, i);
                vertexStrings.push_back(temp);
            }

        // Same for texture positions

        } else if (strWord(line, 1) == "vt") {     
            for (int i = 2; i <= 3; i++) {
                std::string temp;
                temp = strWord(line, i);
                textureStrings.push_back(temp);
            }

        // Same for normal positions

        } else if (strWord(line, 1) == "vn") {     // normals
            for (int i = 2; i <= 4; i++) {
                std::string temp;
                temp = strWord(line, i);
                normalStrings.push_back(temp);
            }

        // Separate each of the three vertices and then separate 
        // each vertex into its vertex index, texture index and
        // normal index

        } else if (strWord(line, 1) == "f") {      // faces (indices)
            std::string temp;

            for (int i = 2; i <= 4; i++) {
                temp = strWord(line, i);
                chunks.push_back(temp);

                k = std::stoi(strFaces(temp, 1));
                vertexIndices.push_back(k-1);

                l = std::stoi(strFaces(temp, 2));
                textureIndices.push_back(l-1);

                m = std::stoi(strFaces(temp, 3));
                normalIndices.push_back(m-1);

            }

        }
    }

    // Convert from string to float

    for (auto &s : vertexStrings) {
        std::stringstream parser(s);
        float x = 0;

        parser >> x;

        vertices.push_back(x);
    }

    for (auto &s : textureStrings) {
        std::stringstream parser(s);
        float x = 0;

        parser >> x;

        texCoords.push_back(x);
    }

    // Y coords are from top left instead of bottom left
    for (int i = 0; i < texCoords.size(); i++) {
        if (i % 2 != 0)
            texCoords[i] = 1 - texCoords[i];
    }

    // Passes vertex positions, vertex indices and texture coordinates 
    // to loader class
    return loader.loadToVao(vertices, vertexIndices, texCoords);
}

我尝试将texCoords [textureIndices [i]]中的值(vector.insert)插入循环中,但这没有用,并且使输出更糟.我尝试了一个简单的方法:

I've tried inserting the values (vector.insert) from texCoords[textureIndices[i]] in a loop but that didn't work and made the output worse. I tried a simple:

tempVec[i] = texCoords[textureIndices[i]] 

在for循环中,但这也不起作用.

in a for loop but that didn't work either.

我遍历了整个项目,并确定排序是造成此问题的原因,因为当我插入多维数据集的硬编码值时,它可以完美工作并且纹理完全​​没有偏移. (OpenGL命令/图像加载器正在按预期方式工作.)

I've gone through the whole project and I determined that the sorting is the cause of the issue, because when I plug in hard-coded values for the cube it works perfectly and the textures aren't offset at all. (The OpenGL commands / image loader are working as they should.)

最终,还有另一种方法可以基于textureIndices对texCoords进行排序吗?

Ultimately, is there another way to sort the texCoords based on the textureIndices?

推荐答案

我想在引擎中实现此功能很长时间(为obj文件添加纹理),您的问题让我很想实际执行此操作:).

I wanted to implement this (adding textures for obj file) into my engine for a long time and your Question got me the mood to actually do it :).

作为纹理提供的图像看起来更像是预览而不是纹理.此外,纹理坐标与预览中所见不符:

The image you provided as texture looks more like a preview than a texture. Also the texture coordinates does not correspond to it as you can see in preview:

如果您查看纹理坐标:

vt 0.736102 0.263898
vt 0.263898 0.736102
vt 0.263898 0.263898
vt 0.736102 0.263898
vt 0.263898 0.736102
vt 0.263898 0.263898
vt 0.736102 0.263898
vt 0.263898 0.736102
vt 0.263898 0.263898
vt 0.736102 0.263898
vt 0.263898 0.736102
vt 0.263898 0.263898
vt 0.736102 0.263898
vt 0.263898 0.736102
vt 0.263898 0.263898
vt 0.736102 0.736102
vt 0.736102 0.736102
vt 0.736102 0.736102
vt 0.736102 0.736102
vt 0.736102 0.736102 

只有两个数字:

0.736102
0.263898

对于纹理中不存在的轴对齐的四边形或正方形子图像,这是有意义的.同样,纹理点的数量20没有意义,应该只是4.因此,您会感到困惑.

Which makes sense for axis aligned quad or square sub-image in the texture which isnot present in your texture. Also the number of texture points makes no sense 20 it should be just 4. Hence the confusions you got.

无论如何, Rabbid76 是正确的,您需要复制点...相对来说比较容易:

Anyway Rabbid76 is right you need to duplicate points ... Its relatively easy so:

  1. 提取所有位置,颜色,纹理点和法线

到单独的表中.因此,分析以v,vt,vn开头的行,并从中创建4个表.是4,因为某些3D扫描仪的输出有时会将v中的颜色编码为v x y z r g b.

from your obj file into separate tables. So parse lines starting with v,vt,vn and create 4 tables from it. Yes 4 as color is sometimes encoded in v as v x y z r g b as output from some 3D scanners.

所以你应该有这样的东西:

So you should have something like this:

double ppos[]= // v
    {
    -1.000000, 1.000000, 1.000000,
    -1.000000,-1.000000,-1.000000,
    -1.000000,-1.000000, 1.000000,
    -1.000000, 1.000000,-1.000000,
     1.000000,-1.000000,-1.000000,
     1.000000, 1.000000,-1.000000,
     1.000000,-1.000000, 1.000000,
     1.000000, 1.000000, 1.000000,
     };
double pcol[]= // v
    {
    };
double ptxr[]= // vt
    {
    0.736102,0.263898,
    0.263898,0.736102,
    0.263898,0.263898,
    0.736102,0.263898,
    0.263898,0.736102,
    0.263898,0.263898,
    0.736102,0.263898,
    0.263898,0.736102,
    0.263898,0.263898,
    0.736102,0.263898,
    0.263898,0.736102,
    0.263898,0.263898,
    0.736102,0.263898,
    0.263898,0.736102,
    0.263898,0.263898,
    0.736102,0.736102,
    0.736102,0.736102,
    0.736102,0.736102,
    0.736102,0.736102,
    0.736102,0.736102,
    };
double pnor[]=  // vn
    {
    -0.5774, 0.5774, 0.5774,
    -0.5774,-0.5774,-0.5774,
    -0.5774,-0.5774, 0.5774,
    -0.5774, 0.5774,-0.5774,
     0.5774,-0.5774,-0.5774,
     0.5774, 0.5774,-0.5774,
     0.5774,-0.5774, 0.5774,
     0.5774, 0.5774, 0.5774,
    };

  • 过程面f

  • process faces f

    现在您应该将上述表格作为临时数据处理,并为网格从头开始为新结构创建真实数据(或直接将其加载到VBO).因此,您需要将所有f数据重新索引为存在的所有索引的唯一组合.为此,您需要跟踪已经获得的信息.为此,我在使用这种结构:

    now you should handle the above tables as temp data and create real data for your mesh from scratch into new structure (or load it directly to VBOs). So what you need is to reindex all the f data to unique combinations of all the indexes present. To do that you need to keep track what you already got. For that I amusing this structure:

    class vertex
        {
    public:
        int pos,txr,nor;
        vertex(){}; vertex(vertex& a){ *this=a; }; ~vertex(){}; vertex* operator = (const vertex *a) { *this=*a; return this; }; /*vertex* operator = (const vertex &a) { ...copy... return this; };*/
        int operator == (vertex &a) { return (pos==a.pos)&&(txr==a.txr)&&(nor==a.nor); }
        int operator != (vertex &a) { return (pos!=a.pos)||(txr!=a.txr)||(nor!=a.nor); }
        };
    

    因此创建一个vertex的空列表,现在处理第一条f行并提取索引

    so create empty list of vertex now process first f line and extract indexes

    f 1/1/1 2/2/2 3/3/3
    

    因此,针对面部中的每个点(一次仅处理一个)提取其ppos,ptxr,pnor索引.现在检查它是否已经存在于最终网格数据中.如果是,请改用其索引.如果未将新点添加到所有表中,则网格具有(pos,col,txr,nor)并使用新添加点的索引.

    so for each point (process just one at a time) in the face extract its ppos,ptxr,pnor index. Now check if it is already present in your final mesh data. If yes use its index instead. If not add new point to all tables your mesh have (pos,col,txr,nor) and use index of newly added point.

    处理完一个面的所有点后,将带有重新索引索引的面添加到最终的网格面中,并处理下一个f行.

    When all points of a face was processed add the face with the reindexed indexes into your final mesh faces and process next f line.

    可以肯定的是,这是我在引擎中使用的 Wavefront OBJ 加载程序C ++类(但是它取决于引擎本身,因此您不能直接使用它,只是看它的结构)代码以及如何对此进行编码……从头开始可能很困难.

    Just for sure here is my Wavefront OBJ loader C++ class I am using in my engine (but it depends on the engine itself so you can not use it directly it is just to see the structure of code and how to encode this... as starting with this from scratch might be difficult).

    //---------------------------------------------------------------------------
    //--- Wavefront obj librrary ver: 2.11 --------------------------------------
    //---------------------------------------------------------------------------
    #ifndef _model_obj_h
    #define _model_obj_h
    //---------------------------------------------------------------------------
    //---------------------------------------------------------------------------
    //---------------------------------------------------------------------------
    class model_obj
        {
    public:
        class vertex
            {
        public:
            int pos,txr,nor;
            vertex(){}; vertex(vertex& a){ *this=a; }; ~vertex(){}; vertex* operator = (const vertex *a) { *this=*a; return this; }; /*vertex* operator = (const vertex &a) { ...copy... return this; };*/
            int operator == (vertex &a) { return (pos==a.pos)&&(txr==a.txr)&&(nor==a.nor); }
            int operator != (vertex &a) { return (pos!=a.pos)||(txr!=a.txr)||(nor!=a.nor); }
            };
    
        OpenGL_VAO obj;
    
        model_obj();
        ~model_obj();
        void reset();
    
        void load(AnsiString name);
        int  save(OpenGL_VAOs &vaos);
        };
    //---------------------------------------------------------------------------
    //---------------------------------------------------------------------------
    //---------------------------------------------------------------------------
    model_obj::model_obj()
        {
        reset();
        }
    //---------------------------------------------------------------------------
    model_obj::~model_obj()
        {
        reset();
        }
    //---------------------------------------------------------------------------
    void model_obj::reset()
        {
        obj.reset();
        }
    //---------------------------------------------------------------------------
    void model_obj::load(AnsiString name)
        {
        int   adr,siz,hnd;
        BYTE *dat;
    
        reset();
        siz=0;
        hnd=FileOpen(name,fmOpenRead);
        if (hnd<0) return;
        siz=FileSeek(hnd,0,2);
            FileSeek(hnd,0,0);
        dat=new BYTE[siz];
        if (dat==NULL) { FileClose(hnd); return; }
        FileRead(hnd,dat,siz);
        FileClose(hnd);
    
        AnsiString s,s0,t;
        int     a,i,j;
        double  alpha=1.0;
        List<double> f;
        List<int> pos,txr,nor;
        List<double> ppos,pcol,pnor,ptxr;   // OBJ parsed data
        vertex v;
        List<vertex> pv;
    
        f.allocate(6);
    
        ppos.num=0;
        pcol.num=0;
        pnor.num=0;
        ptxr.num=0;
        obj.reset();
    //                              purpose,    location,                   type,datatype,datacomponents,pack_acc);
        obj.addVBO(_OpenGL_VBO_purpose_pos ,vbo_loc_pos ,        GL_ARRAY_BUFFER,GL_FLOAT,             3,     0.0001);
        obj.addVBO(_OpenGL_VBO_purpose_col ,vbo_loc_col ,        GL_ARRAY_BUFFER,GL_FLOAT,             4,     0.0001);
        obj.addVBO(_OpenGL_VBO_purpose_txr0,vbo_loc_txr0,        GL_ARRAY_BUFFER,GL_FLOAT,             2,     0.0001);
        obj.addVBO(_OpenGL_VBO_purpose_nor ,vbo_loc_nor ,        GL_ARRAY_BUFFER,GL_FLOAT,             3,     0.0001);
        obj.addVBO(_OpenGL_VBO_purpose_fac ,          -1,GL_ELEMENT_ARRAY_BUFFER,  GL_INT,             3,     0.0);
        obj.draw_mode=GL_TRIANGLES;
        obj.rep.reset();
        obj.filename=name;
    
        _progress_init(siz); int progress_cnt=0;
        for (adr=0;adr<siz;)
            {
            progress_cnt++; if (progress_cnt>=1024) { progress_cnt=0; _progress(adr); }
    
            s0=txt_load_lin(dat,siz,adr,true);
            a=1; s=str_load_str(s0,a,true);
    
            // clear temp vector in case of bug in obj file
            f.num=0; for (i=0;i<6;i++) f.dat[i]=0.0;
    
            if (s=="v")
                {
                f.num=0;
                for (;;)
                    {
                    s=str_load_str(s0,a,true);
                    if ((s=="")||(!str_is_num(s))) break;
                    f.add(str2num(s));
                    }
                if (f.num>=3)
                    {
                    ppos.add(f[0]);
                    ppos.add(f[1]);
                    ppos.add(f[2]);
                    }
                if (f.num==6)
                    {
                    pcol.add(f[3]);
                    pcol.add(f[4]);
                    pcol.add(f[5]);
                    }
                }
            else if (s=="vn")
                {
                f.num=0;
                for (;;)
                    {
                    s=str_load_str(s0,a,true);
                    if ((s=="")||(!str_is_num(s))) break;
                    f.add(str2num(s));
                    }
                pnor.add(f[0]);
                pnor.add(f[1]);
                pnor.add(f[2]);
                }
            else if (s=="vt")
                {
                f.num=0;
                for (;;)
                    {
                    s=str_load_str(s0,a,true);
                    if ((s=="")||(!str_is_num(s))) break;
                    f.add(str2num(s));
                    }
                ptxr.add(f[0]);
                ptxr.add(f[1]);
                }
            else if (s=="f")
                {
                pos.num=0;
                txr.num=0;
                nor.num=0;
                for (;;)
                    {
                    s=str_load_str(s0,a,true); if (s=="") break;
                    for (t="",i=1;i<=s.Length();i++) if (s[i]=='/') break; else t+=s[i]; if ((t!="")&&(str_is_num(t))) pos.add(str2int(t)-1);
                    for (t="",i++;i<=s.Length();i++) if (s[i]=='/') break; else t+=s[i]; if ((t!="")&&(str_is_num(t))) txr.add(str2int(t)-1);
                    for (t="",i++;i<=s.Length();i++) if (s[i]=='/') break; else t+=s[i]; if ((t!="")&&(str_is_num(t))) nor.add(str2int(t)-1);
                    }
                // reindex and or duplicate vertexes if needed
                for (i=0;i<pos.num;i++)
                    {
                    // wanted vertex
                                   v.pos=pos[i];
                    if (txr.num>0) v.txr=txr[i]; else v.txr=-1;
                    if (nor.num>0) v.nor=nor[i]; else v.nor=-1;
                    // is present in VBO?
                    for (j=0;j<pv.num;j++)
                     if (v==pv[j])
                      { pos[i]=j; j=-1; break; }
                    // if not add it
                    if (j>=0)
                        {
                        j=v.pos; j=j+j+j;   if (pcol.num>0) obj.addpntcol(ppos[j+0],ppos[j+1],ppos[j+2],pcol[j+0],pcol[j+1],pcol[j+2],alpha);
                                             else           obj.addpnt   (ppos[j+0],ppos[j+1],ppos[j+2]);
                        j=v.nor; j=j+j+j;   if (v.nor>=0)   obj.addnor   (pnor[j+0],pnor[j+1],pnor[j+2]);
                        j=v.txr; j=j+j;     if (v.txr>=0)   obj.addtxr   (ptxr[j+0],ptxr[j+1]);
                        pos[i]=pv.num; pv.add(v);
                        }
                    }
                for (i=2;i<pos.num;i++) obj.addface(pos[0],pos[i-1],pos[i]);
                }
            }
        _progress_done();
        delete[] dat;
    
    
        }
    //---------------------------------------------------------------------------
    int model_obj::save(OpenGL_VAOs &vaos)
        {
        int vaoix0=-1;
        OpenGL_VBO *vn=obj.getVBO(_OpenGL_VBO_purpose_nor );
        if (vn->data.num==0) obj.nor_compute();
        vaos.vao=obj;
        vaoix0=vaos.add(obj);
        return vaoix0;
        }
    //---------------------------------------------------------------------------
    //---------------------------------------------------------------------------
    //---------------------------------------------------------------------------
    #endif
    //---------------------------------------------------------------------------
    

    它还没有使用*.mtl文件(我将纹理硬编码用于预览).

    It does not use the *.mtl file yet (I hardcoded the texture for the preview).

    PS.(如果我将其用作纹理:

    PS. if I use this as texture:

    结果如下:

    我在这里使用了很多我自己的东西,所以有一些解释:


    str_load_str(s,i,true)返回表示字符串s中从索引i开始的第一个有效单词的字符串. true表示i被更新为s中的新位置.
    str_load_lin(s,i,true)从字符串s中的索引i返回代表行的字符串(直到CRLFCRLFLFCR). true表示i用该行之后的新位置更新.
    txt_load_...是相同的,但是如果需要的话,它会读取BYTE*CHAR*形式,而不是从字符串中读取.


    str_load_str(s,i,true) returns string representing first valid word from index i in string s. The true means just that i is updated with new position in s.
    str_load_lin(s,i,true) returns string representing line (till CR or LF or CRLF or LFCR) from index i in string s. The true means just that i is updated with new position after that line.
    txt_load_... is the same but instead of reading from string it reads form BYTE* or CHAR* if you want.

    当心AnsiString0BYTE*,CHAR*的索引,来自0.

    Beware AnsiString is indexed form 1 and BYTE*,CHAR* from 0.

    我还使用了我的动态列表模板,所以:

    I also use mine dynamic list template so:


    List<double> xxx;double xxx[];相同
    xxx.add(5);5添加到列表的末尾
    xxx[7]访问数组元素(安全)
    xxx.dat[7]访问数组元素(不安全但快速的直接访问)
    xxx.num是数组的实际使用大小
    xxx.reset()清除数组并设置xxx.num=0
    xxx.allocate(100)100个项目预分配空间


    List<double> xxx; is the same as double xxx[];
    xxx.add(5); adds 5 to end of the list
    xxx[7] access array element (safe)
    xxx.dat[7] access array element (unsafe but fast direct access)
    xxx.num is the actual used size of the array
    xxx.reset() clears the array and set xxx.num=0
    xxx.allocate(100) preallocate space for 100 items

    这里是来自mtl文件的带有纹理的更新的更快的重新索引代码(其他内容已被忽略,目前仅支持单个对象/纹理):

    Here the updated faster reindex code with textures from mtl file (otherstuff is ignored and only single object/texture is supported for now):

    //---------------------------------------------------------------------------
    //--- Wavefront obj librrary ver: 2.11 --------------------------------------
    //---------------------------------------------------------------------------
    #ifndef _model_obj_h
    #define _model_obj_h
    //---------------------------------------------------------------------------
    class model_obj
        {
    public:
    
        class vertex
            {
        public:
            int pos,txr,nor;
            vertex(){}; vertex(vertex& a){ *this=a; }; ~vertex(){}; vertex* operator = (const vertex *a) { *this=*a; return this; }; /*vertex* operator = (const vertex &a) { ...copy... return this; };*/
            int operator == (vertex &a) { return (pos==a.pos)&&(txr==a.txr)&&(nor==a.nor); }
            int operator != (vertex &a) { return (pos!=a.pos)||(txr!=a.txr)||(nor!=a.nor); }
            int operator <  (vertex &a)
                {
                if (pos>a.pos) return 0;
                if (pos<a.pos) return 1;
                if (txr>a.txr) return 0;
                if (txr<a.txr) return 1;
                if (nor<a.nor) return 1;
                return 0;
                }
            void ld(int p,int t,int n) { pos=p; txr=t; nor=n; }
            };
    
        class vertexes
            {
        public:
            List<vertex> pv;    // vertexes in order
            List<int> ix;       // inex sort ASC for faster access
            int m;              // power of 2 >= ix.num
            vertexes(){}; vertexes(vertexes& a){ *this=a; }; ~vertexes(){}; vertexes* operator = (const vertexes *a) { *this=*a; return this; }; /*vertexes* operator = (const vertexes &a) { ...copy... return this; };*/
            void reset() { m=0; pv.num=0; ix.num=0; }
            bool get(int &idx,vertex &v)        // find idx so pv[idx]<=v and return if new vertex was added
                {
                int i,j;
                // handle first point
                if (ix.num<=0)
                    {
                    m=1;
                    idx=0;
                    pv.add(v);
                    ix.add(0);
                    return true;
                    }
                // bin search closest idx
                for (j=0,i=m;i;i>>=1)
                    {
                    j|=i;
                    if (j>=ix.num) { j^=i; continue; }
                    if (v<pv.dat[ix.dat[j]]) j^=i;
                    }
                // stop if match found
                idx=ix.dat[j];
                if (v==pv.dat[idx]) return false;
                // add new index,vertex if not
                idx=pv.num; pv.add(v); j++;
                if (j>=ix.num) ix.add(idx);
                 else ix.ins(j,idx);
                if (ix.num>=m+m) m<<=1;
                return true;
                }
            };
    
        struct material
            {
            AnsiString nam,txr;
            material(){}; material(material& a){ *this=a; }; ~material(){}; material* operator = (const material *a) { *this=*a; return this; }; /*material* operator = (const material &a) { ...copy... return this; };*/
            };
    
        List<material> mat;
        OpenGL_VAO obj;
    
        model_obj();
        ~model_obj();
        void reset();
    
        void load(AnsiString name);
        int  save(OpenGL_VAOs &vaos);
        };
    //---------------------------------------------------------------------------
    //---------------------------------------------------------------------------
    //---------------------------------------------------------------------------
    model_obj::model_obj()
        {
        reset();
        }
    //---------------------------------------------------------------------------
    model_obj::~model_obj()
        {
        reset();
        }
    //---------------------------------------------------------------------------
    void model_obj::reset()
        {
        obj.reset();
        mat.reset();
        }
    //---------------------------------------------------------------------------
    void model_obj::load(AnsiString name)
        {
        AnsiString path=ExtractFilePath(name);
        int   adr,siz,hnd;
        BYTE *dat;
    
        reset();
        siz=0;
        hnd=FileOpen(name,fmOpenRead);
        if (hnd<0) return;
        siz=FileSeek(hnd,0,2);
            FileSeek(hnd,0,0);
        dat=new BYTE[siz];
        if (dat==NULL) { FileClose(hnd); return; }
        FileRead(hnd,dat,siz);
        FileClose(hnd);
    
        AnsiString s,s0,t;
        int     a,i,j;
        double  alpha=1.0;
        List<double> f;
        List<int> pos,txr,nor;
        List<double> ppos,pcol,pnor,ptxr;   // OBJ parsed data
        vertex v;
        vertexes pver;
        material m0,*m=NULL;
    
        f.allocate(6);
        pver.reset();
    
        ppos.num=0;
        pcol.num=0;
        pnor.num=0;
        ptxr.num=0;
        obj.reset();
    //                              purpose,    location,                   type,datatype,datacomponents,pack_acc);
        obj.addVBO(_OpenGL_VBO_purpose_pos ,vbo_loc_pos ,        GL_ARRAY_BUFFER,GL_FLOAT,             3,     0.0001);
        obj.addVBO(_OpenGL_VBO_purpose_col ,vbo_loc_col ,        GL_ARRAY_BUFFER,GL_FLOAT,             4,     0.0001);
        obj.addVBO(_OpenGL_VBO_purpose_txr0,vbo_loc_txr0,        GL_ARRAY_BUFFER,GL_FLOAT,             2,     0.0001);
        obj.addVBO(_OpenGL_VBO_purpose_nor ,vbo_loc_nor ,        GL_ARRAY_BUFFER,GL_FLOAT,             3,     0.0001);
        obj.addVBO(_OpenGL_VBO_purpose_fac ,          -1,GL_ELEMENT_ARRAY_BUFFER,  GL_INT,             3,     0.0);
        obj.draw_mode=GL_TRIANGLES;
        obj.rep.reset();
        obj.filename=name;
    
        _progress_init(siz); int progress_cnt=0;
        for (adr=0;adr<siz;)
            {
            progress_cnt++; if (progress_cnt>=1024) { progress_cnt=0; _progress(adr); }
    
            s0=txt_load_lin(dat,siz,adr,true);
            a=1; s=str_load_str(s0,a,true);
    
            // clear temp vector in case of bug in obj file
            f.num=0; for (i=0;i<6;i++) f.dat[i]=0.0;
    
            if (s=="v")
                {
                f.num=0;
                for (;;)
                    {
                    s=str_load_str(s0,a,true);
                    if ((s=="")||(!str_is_num(s))) break;
                    f.add(str2num(s));
                    }
                if (f.num>=3)
                    {
                    ppos.add(f[0]);
                    ppos.add(f[1]);
                    ppos.add(f[2]);
                    }
                if (f.num==6)
                    {
                    pcol.add(f[3]);
                    pcol.add(f[4]);
                    pcol.add(f[5]);
                    }
                }
            else if (s=="vn")
                {
                f.num=0;
                for (;;)
                    {
                    s=str_load_str(s0,a,true);
                    if ((s=="")||(!str_is_num(s))) break;
                    f.add(str2num(s));
                    }
                pnor.add(f[0]);
                pnor.add(f[1]);
                pnor.add(f[2]);
                }
            else if (s=="vt")
                {
                f.num=0;
                for (;;)
                    {
                    s=str_load_str(s0,a,true);
                    if ((s=="")||(!str_is_num(s))) break;
                    f.add(str2num(s));
                    }
                ptxr.add(f[0]);
                ptxr.add(f[1]);
                }
            else if (s=="f")
                {
                pos.num=0;
                txr.num=0;
                nor.num=0;
                for (;;)
                    {
                    s=str_load_str(s0,a,true); if (s=="") break;
                    for (t="",i=1;i<=s.Length();i++) if (s[i]=='/') break; else t+=s[i]; if ((t!="")&&(str_is_num(t))) pos.add(str2int(t)-1);
                    for (t="",i++;i<=s.Length();i++) if (s[i]=='/') break; else t+=s[i]; if ((t!="")&&(str_is_num(t))) txr.add(str2int(t)-1);
                    for (t="",i++;i<=s.Length();i++) if (s[i]=='/') break; else t+=s[i]; if ((t!="")&&(str_is_num(t))) nor.add(str2int(t)-1);
                    }
                // reindex and or duplicate vertexes if needed
                for (i=0;i<pos.num;i++)
                    {
                    // wanted vertex
                                   v.pos=pos[i];
                    if (txr.num>0) v.txr=txr[i]; else v.txr=-1;
                    if (nor.num>0) v.nor=nor[i]; else v.nor=-1;
                    if (pver.get(pos[i],v)) // is present in VBO? if not add it
                        {
                        j=v.pos; j=j+j+j;   if (pcol.num>0) obj.addpntcol(ppos[j+0],ppos[j+1],ppos[j+2],pcol[j+0],pcol[j+1],pcol[j+2],alpha);
                                             else           obj.addpnt   (ppos[j+0],ppos[j+1],ppos[j+2]);
                        j=v.nor; j=j+j+j;   if (v.nor>=0)   obj.addnor   (pnor[j+0],pnor[j+1],pnor[j+2]);
                        j=v.txr; j=j+j;     if (v.txr>=0)   obj.addtxr   (ptxr[j+0],ptxr[j+1]);
                        }
                    }
                for (i=2;i<pos.num;i++) obj.addface(pos[0],pos[i-1],pos[i]);
                }
            else if (s=="mtllib")
                {
                AnsiString s1;
                int   adr,siz,hnd;
                BYTE *dat;
                // extract mtl filename
                s=str_load_str(s0,a,true);
                s+=str_load_lin(s0,a,true);
                // load it to memory
                siz=0;
                hnd=FileOpen(path+s,fmOpenRead);
                if (hnd<0) continue;
                siz=FileSeek(hnd,0,2);
                    FileSeek(hnd,0,0);
                dat=new BYTE[siz];
                if (dat==NULL) { FileClose(hnd); continue; }
                FileRead(hnd,dat,siz);
                FileClose(hnd);
                // extract textures and stuff
                m=&m0;
                for (adr=0;adr<siz;)
                    {
                    s1=txt_load_lin(dat,siz,adr,true);
                    a=1; s=str_load_str(s1,a,true);
                    if (s=="newmtl")
                        {
                        s=str_load_str(s1,a,true);
                        s+=str_load_lin(s1,a,true);
                        mat.add();
                        m=&mat[mat.num-1];
                        m->nam=s;
                        m->txr="";
                        }
                    else if (s=="map_Kd")
                        {
                        s=str_load_str(s1,a,true);
                        s+=str_load_lin(s1,a,true);
                        m->txr=s;
                        }
                    }
                delete[] dat;
                m=NULL;
                }
            else if (s=="usemtl")
                {
                // extract material name
                s=str_load_str(s0,a,true);
                s+=str_load_lin(s0,a,true);
                // find it in table
                for (m=mat.dat,i=0;i<mat.num;i++,m++)
                 if (m->nam==s) { i=-1; break; }
                if (i>=0) m=NULL;
                }
            }
        // textures
        for (i=0;i<mat.num;i++)
         if (mat[i].txr!="")
            {
            OpenGL_VAO::_TXR txr;
            txr.ix=-1;
            txr.unit=txr_unit_map;
            txr.filename=mat[i].txr;
            txr.txrtype=GL_TEXTURE_2D;
            txr.repeat=GL_REPEAT;
            obj.txr.add(txr);
            }
    
        _progress_done();
        delete[] dat;
        }
    //---------------------------------------------------------------------------
    int model_obj::save(OpenGL_VAOs &vaos)
        {
        int vaoix0=-1,i;
        OpenGL_VBO *vn=obj.getVBO(_OpenGL_VBO_purpose_nor );
        if (vn) if (vn->data.num==0) obj.nor_compute();
        vaos.vao=obj;
    
        vaoix0=vaos.add(obj);
        return vaoix0;
        }
    //---------------------------------------------------------------------------
    //---------------------------------------------------------------------------
    //---------------------------------------------------------------------------
    #endif
    //---------------------------------------------------------------------------
    

    应用添加的材料(现在仅是纹理和材料名称),我更改了重新索引,以便对文本进行索引排序,并使用二进制搜索按需获取顶点索引.与此在3.7版中加载了100K张斯坦福巨龙(3.4MByte)秒:

    Appart the added materials (just texture and material name for now) I changed the re-indexing so the vertextes are index sorted and binary search is used to obtain vertex index on demand. With this 100K faces Standford dragon (3.4MByte) is loaded in 3.7sec:

    这篇关于如何基于Wavefront(.obj)文件中提供的纹理索引对纹理位置进行排序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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