opengl — ошибка нарушения прав чтения — C ++ Obj Loader

Я написал основной загрузчик .obj. Он работает с кубом и некоторыми другими базовыми вещами. Как только я даю ему сложную модель, она терпит неудачу.

Вот некоторый основной код:

Functions.h

#pragma once
#include <string>

struct face {
bool triangle;
int faceNumber;
int faces[4];
face(int faceNumber2,int f1,int f2, int f3, int f4) {
triangle = false;
faceNumber = faceNumber2;
faces[0] = f1;
faces[1] = f2;
faces[2] = f3;
faces[3] = f4;
}
face(int faceNumber2,int f1,int f2, int f3) {
triangle = true;
faceNumber = faceNumber2;
faces[0] = f1;
faces[1] = f2;
faces[2] = f3;
}};

struct position {
float x, y, z;
position(float X,float Y, float Z) {
x = X;
y = Y;
z = Z;
}
};

class Functions
{
public:
Functions(void);
~Functions(void);
int loadObject(std::string fileName);
void drawCube(float size);
unsigned int loadTexture(const char* fileName);
};

Functions.cpp

int Functions::loadObject(string fileName) {
ifstream file(fileName);
vector<string*> line;
vector<position*> normals;
vector<face*> faces;
vector<position*> vertices;if (file.is_open()) {
char buffer[256];
while (!file.eof()) {
file.getline(buffer,256);
line.push_back(new string(buffer));
}
for (int i = 0; i<line.size(); i++) {
if ((*line[i])[0] == 'v' && (*line[i])[1] == ' ') { //Vertice
float x,y,z;
sscanf_s((*line[i]).c_str(),"v %f %f %f",&x,&y,&z);
vertices.push_back(new position(x,y,z));
}else if ((*line[i])[0] == 'v' && (*line[i])[1] == 'n') { //Normals
float x,y,z;
sscanf_s((*line[i]).c_str(),"vn %f %f %f",&x,&y,&z);
normals.push_back(new position(x,y,z));
} else if ((*line[i])[0] == 'f') {
if (count((*line[i]).begin(),(*line[i]).end(),' ') == 4) {
float a,b,c,d,e;
sscanf_s((*line[i]).c_str(),"f %f//%f %f//%f %f//%f %f//%f",&a,&e,&b,&e,&c,&e,&d,&e);

faces.push_back(new face(e,a,b,c,d));
} else {
float a,b,c,e;
sscanf_s((*line[i]).c_str(),"f %f//%f %f//%f %f//%f",&a,&e,&b,&e,&c,&e);
faces.push_back(new face(e,a,b,c));
}

}
}
file.close();
} else {
file.close();
throw exception("Fail to load file");
return -1;
}

int num;
num = glGenLists(1);
glNewList(num,GL_COMPILE);
for (int i = 0; i<faces.size(); i++) {
face tempFace = (*faces[i]);
if (tempFace.triangle) {
glBegin(GL_TRIANGLES);
glNormal3f((*normals[tempFace.faceNumber-1]).x, (*normals[tempFace.faceNumber-1]).y, (*normals[tempFace.faceNumber-1]).z);
glVertex3f((*vertices[tempFace.faces[0]-1]).x, (*vertices[tempFace.faces[0]-1]).y, (*vertices[tempFace.faces[0]-1]).z);
**//Errors below.**
glVertex3f((*vertices[tempFace.faces[1]-1]).x, (*vertices[tempFace.faces[1]-1]).y, (*vertices[tempFace.faces[1]-1]).z);
glVertex3f((*vertices[tempFace.faces[2]-1]).x, (*vertices[tempFace.faces[2]-1]).y, (*vertices[tempFace.faces[2]-1]).z);
glEnd();
} else {
//glNormal3f((*normals[tempFace.faceNumber-1]).x,(*normals[tempFace.faceNumber-1]).y,(*normals[tempFace.faceNumber-1]).z)
glBegin(GL_QUADS);
glNormal3f((*normals[tempFace.faceNumber-1]).x, (*normals[tempFace.faceNumber-1]).y, (*normals[tempFace.faceNumber-1]).z);
glVertex3f((*vertices[tempFace.faces[0]-1]).x, (*vertices[tempFace.faces[0]-1]).y, (*vertices[tempFace.faces[0]-1]).z);
glVertex3f((*vertices[tempFace.faces[1]-1]).x, (*vertices[tempFace.faces[1]-1]).y, (*vertices[tempFace.faces[1]-1]).z);
glVertex3f((*vertices[tempFace.faces[2]-1]).x, (*vertices[tempFace.faces[2]-1]).y, (*vertices[tempFace.faces[2]-1]).z);
glVertex3f((*vertices[tempFace.faces[3]-1]).x, (*vertices[tempFace.faces[3]-1]).y, (*vertices[tempFace.faces[3]-1]).z);
glEnd();
}
}
glEndList();for (int i = 0; i<line.size(); i++) {
delete line[i];
}
for (int i = 0; i<normals.size(); i++) {
delete normals[i];
}
for (int i = 0; i<vertices.size(); i++) {
delete vertices[i];
}
for (int i = 0; i<faces.size(); i++) {
delete faces[i];
}

return 1;
}

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

0

Решение

Есть несколько проблем с кодом, каждая из которых технически требует отдельного ответа. Ну да ладно, вот так. Если вы хотите использовать C ++, то делайте это правильно. Прежде всего мы хотим, чтобы наши объекты были в состоянии RAII. Это означает, что нам нужен конструктор копирования и оператор присваивания.

// A face should always be a triangle. Quads are ambigous
// when it comes to rendering, and most OpenGL implementations
// break them into triangles anyway. We can do this better.
// Also the "faceNumber" element doesn't do what you think it
// does. We get back to that later.
struct face {
int i[3];

void copy(face const &f_) {
for(int j = 0; j < 3; j++) { // could use memcpy as well
i[j] = f_.i[j]
}
}
face const &operator=(face const &f_) {
copy(f_);
return *this;
}
face(face const &f_) { copy(f_); }
face(int i_a, int i_b, int i_c) {
i[0] = i_a;
i[1] = i_b;
i[2] = i_c;

}
};

struct tuple3f {
float x, y, z;

tuple3f(float x_,float y_, float z_) : x(x_), y(y_), z(z_) {}
tuple3f(tuple3f const &p_) : x(p_.x), y(p_.y), z(p_.z) {}
tuple3f const &operator=(tuple3f const &p_) {
x = p_.x; y = p_.y; z = p_.z;
return *this;
}
};

// Struct for unrolling the vertices into.
struct vertex3p3n {
tuple3f position, normal;

// Implementing the constructor, copy constructor and
// assignment operator is left as en exercise.
vertex3p3n(tuple3 const &p_, tuple3 const &n_) : ... {}
};

namespace glhelpers {
enum error {
NoError = 0,
FileNotOpened
};

error loadWavefrontOBJ(
std::string fileName,
std::vector<vertex3p3n> &out_unrolledvertexarray ) {
ifstream file(fileName);

vector<tuple3f> positions;
vector<tuple3f> normals;

vector<face>    faces_position;
vector<face>    faces_normal;

if (file.is_open()) {
// It's totally inefficient to first load a file line by line
// into memory, making a new allocation for each line's string
// which internally does another new allocation,
// only to process it line by line later.
//
// Just do the line by line processing on the read lines without
// intermediary allocation. And there's a nice std::getline
// function which does the job just nicely for us.
while( !file.eof() ) {
std::string line;
std::getline(file, line);

if( 'v' == line[0] && ' ' == line[1] ) { // Vertex
float x,y,z;
sscanf_s(line.c_str()+2, "%f %f %f", &x,&y,&z);
positions.push_back(tuple3f(x,y,z));
continue;
}

if( 'v' == line[0] && 'n' == line[1] ) { // Normal
float x,y,z;
sscanf_s(line[i].c_str()+3, "%f %f %f", &x,&y,&z);
normals.push_back(tuple3f(x,y,z));
continue;
}

// Here begins the nasty part. The Wavefront OBJ file format
// treats positions, normals and texture coordinates as separate
// entities. Which is fine, as long as you don't want to shove
// them into a renderer like OpenGL is. You see, in OpenGL
// every *vertex* consists of the whole combination of
// [position, normal, texture coordinate, ...] and other attributes.
// They're not separable (even if the immediate mode API may make
// people think set). Which leads to a problem: Somehow we've to
// unite those later on. There are several ways to do this.
// Renumbering and unrolling are the easiest once. Putting the
// numbers into immediate mode is a form of unrolling.
//
// What the Wavefront actually is giving you for each face is the
// index into the separated position, texture coordinate and
// normal arrays.
if ( 'f' == line[0] ) {
if( count(line.begin(), line.end(), ' ') == 4) {
float pa, pb, pc, pd;
float na, nb, nc, nd;
sscanf_s(
line.c_str()+2
"%f//%f %f//%f %f//%f %f//%f",
&pa,&na, &pb,&nb, &pc,&nc, &pd,&nd);

faces_position.push_back(face(pa, pb, pc));
faces_normal.push_back(  face(na, nb, nc));

// Decompose the quad into two triangles

faces_position.push_back(face(pc, pd, pa));
faces_normal.push_back(  face(nc, nd, na));
} else {
float pa, pb, pc;
float na, nb, nc;
sscanf_s(
line.c_str()+2,"%f//%f %f//%f %f//%f",
&pa,&na, &pb,&nb, &pc,&nc);

faces_position.push_back(face(pa, pb, pc));
faces_normal.push_back(  face(na, nb, nc));
}
continue;
}
}
file.close();
} else {
// If the file couldn't be opened, there's no point
// in trying to close it.

// throwing an exception diverts the regular control
// flow, i.e. the function won't return.
// My advice: Don't use exceptions, they're harmful
// in most situations. Use proper error codes instead.
return FileNotOpened;
}

out_unrolledvertexarray.resize(faces_position.size()*3),
for(int j = 0; j < faces_position.size(); j++) {
for(int k = 0; k < 3; k++ {
out_unrolledvertexarray[j*3 + k] =
vertex3p3n(
positions[faces_positions[j].i[k]],
normals[  faces_normals  [j].i[k]]);
}
}
return NoError;
}
}

Теперь, как мы это рисуем? Ну, мы используем для этого вершинные массивы. В старом и очищенном конвейере фиксированных функций они доступны через состояние клиента и функции gl… Pointer.

std::vector<vertex3p3n> model;

void loadmodel();
{  // Do this only one time at startup
glhelpers::loadWavefrontOBJ("...", model);
}

void drawmodel()
{
glEnableClientState(GL_VERTEX_ARRAY); // a much better name was GL_POSITION_ARRAY
glEnableClientState(GL_NORMAL_ARRAY);

glVertexPointer(
3, // 3 elements per position tuple
GL_FLOAT, // each element is a float
sizeof(vertex3p3n), // distance between vertex position[0]
&model[0].position );

glNormalPointer( // a normal always has 3 elements
GL_FLOAT, // each element is a float
sizeof(vertex3p3n), // distance between vertex position[0]
&model[0].normal );

glDrawArrays(GL_TRIANGLES, 0, model.size();
}
3

Другие решения

Других решений пока нет …