Хотел написать свой парсер для моделей в формате .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);
}
Посмотри мой код, в нём я тоже парсил 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 );
}
В общем, давно уже разобрался с этим, оставлю тут ответ, может кому помогу.
Не исключаю наличие других ошибок в том примере, т.к. я уже сто раз его переписал, и он работает, но главной ошибкой, было то, что индексы не работают так гладко, как хотелось бы, при использовании такого метода отрисовки.
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
Чтобы модель нормально отображалась, надо передать indices как просто цифры от 0 до кол-ва фейсов*3 модели. (Как уже написал xverizex, но ответ был крайне невнятным)
А как реализовать так, чтобы они работали как это задумывалось, я пока не разобрался, возможно допишу потом, если разберусь.
Виртуальный выделенный сервер (VDS) становится отличным выбором
Описывается, что если указать void func() = delete/default, то это указывает, что компилятор сам должен сгенерировать код для этой функции и именно он будет...
В C++ существет такое понятие, как мьютексыКто может объяснить мне, зачем они нужны?
Всем доброго, недавно начал рассматривать варианты использования с++ аддонов из electron js и не особо нашел что хотелДело в том что стандартный...
У меня скорее вопрос чем проблемаMalloc не выводит ошибки если я пытаюсь выделить 8gb памяти, но на ноутбуке ее всего 6gb