Криво парсится obj файл

200
01 ноября 2018, 11:00

Хотел написать свой парсер для моделей в формате .obj для дальнейшего отображения их с помощью OpenGL, нашел некоторые инструкции (тут и здесь, код отображения меша с learnopengl)

Написал код примерно по инструкциям, но модель загружается очень криво:

Насколько я понял, проблема в индексах. Я уже много, что пытался менять, возможно здесь сразу несколько ошибок. В любом случае, я не знаю, что не так. Помогите, пожалуйста, найти проблему. Ниже приведён код.

Меш и вершины:

struct Vertex {
    glm::vec3 Position;
    glm::vec3 Normal;
    glm::vec2 TexCoords;
};
class cMesh {
public:
    /*  Mesh Data  */
    vector<Vertex> vertices;
    vector<unsigned int> indices;
    unsigned int VAO;
    bool VAO_activated = false;
    // constructor
    cMesh(vector<Vertex> vertices = {}, vector<unsigned int> indices = {});
    void Draw();
private:
    unsigned int VBO, EBO;
    // initializes all the buffer objects/arrays
    void setupMesh();
};

Функции setupMesh и Draw:

void cMesh::Draw()
{
    if (!VAO_activated)
    {
        glBindVertexArray(VAO);
        VAO_activated = true;
    }
    glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
    if (!VAO_activated)
    {
        glBindVertexArray(0);
    }
    // always good practice to set everything back to defaults once configured.
    glActiveTexture(GL_TEXTURE0);
}
void cMesh::setupMesh()
{
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);
    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);  
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), 
                 &indices[0], GL_STATIC_DRAW);
    // vertex positions
    glEnableVertexAttribArray(0);   
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
    // vertex normals
    glEnableVertexAttribArray(1);   
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));
    // vertex texture coords
    glEnableVertexAttribArray(2);   
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords));
    glBindVertexArray(0);
}

Функция загрузки:

cMesh loadObjFile(string path)
{
    ifstream file(path);
    string str; 
    std::vector<unsigned int> vertexIndices, uvIndices, normalIndices;
    vector<glm::vec3> normals;
    vector<glm::vec3> vertices;
    vector<glm::vec2> uvs;
    while (getline(file, str))
    {
        if (str.length() < 5)
            continue;
        if ((str[0] == 'v')&&(str[1] == ' ')) // Vertex
        {
            glm::vec3 vertex;
            str[0] = ' ';
            str[1] = ' ';
            sscanf_s(str.c_str(), "%f %f %f", &vertex.x, &vertex.y, &vertex.z);
            vertices.push_back(vertex);
        }
        if ((str[0] == 'v') && (str[1] == 't')) // UV
        {
            glm::vec2 uv;
            str[0] = ' ';
            str[1] = ' ';
            sscanf_s(str.c_str(), "%f %f", &uv.x, &uv.y);
            uvs.push_back(uv);
        }
        if ((str[0] == 'v') && (str[1] == 'n')) // Normals
        {
            glm::vec3 normal;
            str[0] = ' ';
            str[1] = ' ';
            sscanf_s(str.c_str(), "%f %f %f", &normal.x, &normal.y, &normal.z);
            normals.push_back(normal);
        }
        if ((str[0] == 'f') && (str[1] == ' ')) // Faces
        {
            std::string vertex1, vertex2, vertex3;
            unsigned int vertexIndex[3], uvIndex[3], normalIndex[3];
            str[0] = ' ';
            str[1] = ' ';
            int matches = sscanf_s(str.c_str(), "%d/%d/%d %d/%d/%d %d/%d/%d", &vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex[1], &uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2], &normalIndex[2]);
            if (matches != 9)
                utils::throwError("File can't be paresed");
            vertexIndices.push_back(vertexIndex[0]);
            vertexIndices.push_back(vertexIndex[1]);
            vertexIndices.push_back(vertexIndex[2]);
            uvIndices.push_back(uvIndex[0]);
            uvIndices.push_back(uvIndex[1]);
            uvIndices.push_back(uvIndex[2]);
            normalIndices.push_back(normalIndex[0]);
            normalIndices.push_back(normalIndex[1]);
            normalIndices.push_back(normalIndex[2]);
        }
    }
    vector<glm::vec3> out_normals;
    vector<glm::vec3> out_vertices;
    vector<glm::vec2> out_uvs;
    for (size_t i = 0; i < vertexIndices.size(); i++)
    {
        unsigned int vertexIndex = vertexIndices[i];
        glm::vec3 vertex = vertices[vertexIndex-1];
        //cout << "index: " << vertexIndex << " (" << vertex.x << ", " << vertex.y << ", " << vertex.z << ")" << endl;
        out_vertices.push_back(vertex);
    }
    for (size_t i = 1; i < normalIndices.size(); i++)
        out_normals.push_back(normals[normalIndices[i]-1]);
    for (size_t i = 1; i < uvIndices.size(); i++)
        out_uvs.push_back(uvs[uvIndices[i]-1]);
    vector<Vertex> verts;
    for (size_t i = 0; i < out_vertices.size(); i++)
    {
        Vertex v;
        v.Normal = out_normals[i];
        v.Position = out_vertices[i];
        v.TexCoords = out_uvs[i];
        verts.push_back(v);
    }
    return cMesh(verts, vertexIndices);
}
Answer 1

Посмотри мой код, в нём я тоже парсил obj файл, и парсился он без ошибок. Только код на си. Я indices устанавливал так, через for, 0,1,2,3,4,5,6 и т.д. Делил длину вершин на три, потому как GL_TRIANGLES писать надо и заполнял величинами от нуля до нужного значения, потом в drawElements указывал indices, попробуй indices создать. А так вот ещё мой код посмотри на си, может что упустил? Например если будешь создавать анимацию или не каждый объект с текстурой будет, это тоже надо учитывать на будущее.

#define _XOPEN_SOURCE 500
#define _GNU_SOURCE 1
#include <stdio.h>
#include <string.h>
#include <ftw.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <libgen.h>
struct obj {
    unsigned int i_v;
    unsigned int i_vn;
    unsigned int i_vt;
    unsigned int length_v;
    unsigned int length_vn;
    unsigned int length_vt;
    float *vt;
    float *v;
    float *vn;
} *obj;
char *data_file;
char *object_name ( const char *s )
{
    int n = 0;
    const char *v = s;
    while ( *v != 0 ) {
        if ( *v == '/' ) n++;
        v++;
    }
    v = s;
    n--;
    for ( int i = 0; i < n; v++ ) {
        if ( *v == '/' ) i++;
    }
    char *end = strchr ( v, '/' );
    const char *x = v;
    int z = 0;
    for ( ; *x != '/'; x++, z++ );
    char *s_obj = calloc ( z + 1, 1 );
    strncpy ( s_obj, v, z );
    return s_obj;
}
int read_obj ( const char *fpath )
{
    printf ( "%s\n", fpath );
    FILE *fd = fopen ( fpath, "r" );
    if ( !fd ) {
        perror ( "fopen" );
        return -1;
    }
    unsigned int v = 0;
    unsigned int vt = 0;
    unsigned int vn = 0;
    unsigned int f = 0;
     char *line = calloc ( 255, 1 );
     while ( fgets ( line, 254, fd ) ) {
         if ( !strncmp ( line, "v ", 2 ) ) v++;
         if ( !strncmp ( line, "vt ", 3 ) ) vt++;
         if ( !strncmp ( line, "vn ", 3 ) ) vn++;
         if ( !strncmp ( line, "f ", 2 ) ) f++;
     }
     fseek ( fd, 0, SEEK_SET );
     obj    = calloc ( 1, sizeof ( struct obj ) );
     obj->v = calloc ( f * 9, sizeof ( float ) );
     obj->vn= calloc ( f * 9, sizeof ( float ) );
     obj->vt= calloc ( f * 6, sizeof ( float ) );
     float *vv = calloc ( v  * 3, sizeof ( float ) );
     float *vvn= calloc ( vn * 3, sizeof ( float ) );
     float *vvt= calloc ( vt * 2, sizeof ( float ) );
     int vv_i = 0;
     int vvn_i = 0;
     int vvt_i = 0;
     while ( fgets ( line, 254, fd ) ) {
         if ( !strncmp ( line, "v ", 2 ) ) {
             if ( sscanf ( line, "v %f %f %f", &vv[vv_i + 0], &vv[vv_i + 1], &vv[vv_i + 2] ) == -1 )
                 perror ( "sscanf 1" );
             vv_i += 3;
         }
         if ( !strncmp ( line, "vt ", 3 ) ) {
             if ( sscanf ( line, "vt %f %f", &vvt[vvt_i + 0], &vvt[vvt_i + 1] ) == -1 )
                 perror ( "sscanf 2" );
             vvt_i += 2;
         }
         if ( !strncmp ( line, "vn ", 3 ) ) {
             if ( sscanf ( line, "vn %f %f %f", &vvn[vvn_i + 0], &vvn[vvn_i + 1], &vvn[vvn_i + 2] ) == -1 ) {
                 perror ( "sscanf 3" );
             }
             vvn_i += 3;
         }
         if ( !strncmp ( line, "f ", 2 ) ) {
             int v1, v2, v3;
             int vt1, vt2, vt3;
             int vn1, vn2, vn3;
             int texture = 0;
             char *fo;
             fo = strstr ( line, "//" );
             if ( fo ) texture = 0;
             else texture = 1;
             if ( !texture ) {
                 if ( sscanf ( line, "f %d//%d %d//%d %d//%d",
                             &v1, &vn1, &v2, &vn2, &v3, &vn3 ) == -1 )
                     perror ( "sscanf 4" );
             }else {
                 if ( sscanf ( line, "f %d/%d/%d %d/%d/%d %d/%d/%d",
                             &v1, &vt1, &vn1, &v2, &vt2, &vn2, &v3, &vt3, &vn3 ) == -1 )
                     perror ( "sscanf 5" );
             }
             obj->v[obj->i_v + 0]       = vv [ 0 + ( v1 - 1 ) * 3 ];
             obj->v[obj->i_v + 1]       = vv [ 1 + ( v1 - 1 ) * 3 ];
             obj->v[obj->i_v + 2]       = vv [ 2 + ( v1 - 1 ) * 3 ];
             obj->v[obj->i_v + 3]       = vv [ 0 + ( v2 - 1 ) * 3 ];
             obj->v[obj->i_v + 4]       = vv [ 1 + ( v2 - 1 ) * 3 ];
             obj->v[obj->i_v + 5]       = vv [ 2 + ( v2 - 1 ) * 3 ];
             obj->v[obj->i_v + 6]       = vv [ 0 + ( v3 - 1 ) * 3 ];
             obj->v[obj->i_v + 7]       = vv [ 1 + ( v3 - 1 ) * 3 ];
             obj->v[obj->i_v + 8]       = vv [ 2 + ( v3 - 1 ) * 3 ];
             obj->vn[obj->i_vn + 0]     = vvn [ 0 + ( vn1 - 1 ) * 3 ];
             obj->vn[obj->i_vn + 1]     = vvn [ 1 + ( vn1 - 1 ) * 3 ];
             obj->vn[obj->i_vn + 2]     = vvn [ 2 + ( vn1 - 1 ) * 3 ];
             obj->vn[obj->i_vn + 3]     = vvn [ 0 + ( vn2 - 1 ) * 3 ];
             obj->vn[obj->i_vn + 4]     = vvn [ 1 + ( vn2 - 1 ) * 3 ];
             obj->vn[obj->i_vn + 5]     = vvn [ 2 + ( vn2 - 1 ) * 3 ];
             obj->vn[obj->i_vn + 6]     = vvn [ 0 + ( vn3 - 1 ) * 3 ];
             obj->vn[obj->i_vn + 7]     = vvn [ 1 + ( vn3 - 1 ) * 3 ];
             obj->vn[obj->i_vn + 8]     = vvn [ 2 + ( vn3 - 1 ) * 3 ];
             if ( texture ) {
                 obj->vt[obj->i_vt + 0] = vvt [ 0 + ( vt1 -1 ) * 2 ];
                 obj->vt[obj->i_vt + 1] = vvt [ 1 + ( vt1 -1 ) * 2 ];
                 obj->vt[obj->i_vt + 2] = vvt [ 0 + ( vt2 -1 ) * 2 ];
                 obj->vt[obj->i_vt + 3] = vvt [ 1 + ( vt2 -1 ) * 2 ];
                 obj->vt[obj->i_vt + 4] = vvt [ 0 + ( vt3 -1 ) * 2 ];
                 obj->vt[obj->i_vt + 5] = vvt [ 1 + ( vt3 -1 ) * 2 ];
             }
             obj->i_v += 9;
             obj->i_vn += 9;
             obj->i_vt += 6;
         }
     }
     fclose ( fd );
     obj->length_v = obj->i_v;
     obj->length_vt = obj->i_vt;
     obj->length_vn = obj->i_vn;
     fd = fopen ( data_file, "a" );
     if ( !fd ) {
         perror ( "data_file" );
         return -1;
     }
     {
         long pos;
         pos = ftell ( fd );
         FILE *outlog = fopen ( "log", "a" );
         char *dirn = object_name ( fpath );
         fprintf ( outlog, "%s %ld %s\n", fpath, pos, dirn );
         fclose ( outlog );
     }
     fwrite ( &obj->length_v, sizeof ( unsigned int ), 1, fd );
     fwrite ( &obj->length_vt, sizeof ( unsigned int ), 1, fd );
     fwrite ( &obj->length_vn, sizeof ( unsigned int ), 1, fd );
     for ( int i = 0; i < obj->length_v; i++ ) {
         fwrite ( &obj->v[i], sizeof ( float ), 1, fd );
     }
     for ( int i = 0; i < obj->length_vn; i++ ) {
         fwrite ( &obj->vn[i], sizeof ( float ), 1, fd );
     }
     for ( int i = 0; i < obj->length_vt; i++ ) {
         fwrite ( &obj->vt[i], sizeof ( float ), 1, fd );
     }
     fclose ( fd );
}
void init_data_file ( const char *p ) {
    int len = strlen ( p );
    data_file = calloc ( len + 7, 1 );
    snprintf ( data_file, len + 6, "%s/data", p );
}
int found ( const char *fpath, const struct stat *sb,
        int typeflag, struct FTW *ftwbuf )
{
    char *ops = strstr ( fpath, ".obj" );
    if ( ops ) {
        if ( !strncmp ( ops, ".obj\0", 5 ) ) {
            if ( read_obj ( fpath ) == -1 ) return 0;
        }
    }
}
int main ( int argc, char **argv )
{
    char *p = get_current_dir_name ( );
    init_data_file ( p );
    nftw ( p, found, 20, 0 );
}
Answer 2

В общем, давно уже разобрался с этим, оставлю тут ответ, может кому помогу.

Не исключаю наличие других ошибок в том примере, т.к. я уже сто раз его переписал, и он работает, но главной ошибкой, было то, что индексы не работают так гладко, как хотелось бы, при использовании такого метода отрисовки.

glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);

Чтобы модель нормально отображалась, надо передать indices как просто цифры от 0 до кол-ва фейсов*3 модели. (Как уже написал xverizex, но ответ был крайне невнятным)

А как реализовать так, чтобы они работали как это задумывалось, я пока не разобрался, возможно допишу потом, если разберусь.

READ ALSO
C++ добавление (= default/delete) в конце функции

C++ добавление (= default/delete) в конце функции

Описывается, что если указать void func() = delete/default, то это указывает, что компилятор сам должен сгенерировать код для этой функции и именно он будет...

188
Для чего нужны мьютексы в C++

Для чего нужны мьютексы в C++

В C++ существет такое понятие, как мьютексыКто может объяснить мне, зачем они нужны?

173
Electronjs вызов функций из c++ модуля

Electronjs вызов функций из c++ модуля

Всем доброго, недавно начал рассматривать варианты использования с++ аддонов из electron js и не особо нашел что хотелДело в том что стандартный...

194
Malloc большого количества данных в си

Malloc большого количества данных в си

У меня скорее вопрос чем проблемаMalloc не выводит ошибки если я пытаюсь выделить 8gb памяти, но на ноутбуке ее всего 6gb

176