I'm writing a simple class that parses an OBJ and renders the obj after that. I've already written a method for drawing vertex arrays from RAM and I wanted to optimize it a bit (I read that using VBOs gets you x4 times the framerate!).
Sadly I'm failing spectacularly, I've followed a bunch of tutorials to no avail. Here's the parser and the calls to store the vertices and indices buffers on GPU, I've througly debugged the parser itself and it's ok. Both vertexBuffer and vertexIndices are loaded up fine (I've printed them and manually checked them against the obj file).
Mesh::Mesh(string path) {
float* normals;
float* faces;
float* textures;
string fullpath = path + ".obj";
this->mats = loadMtl(path); //Loads a Material list, not used now drawing white
string line;
ifstream objFile(fullpath.c_str());
if (objFile.is_open()) {
objFile.seekg(0, ios::end);
long fileSize = long(objFile.tellg());
objFile.seekg(0, ios::beg);
// Init buffers and indices
unsigned int inserted = 0;
string currentMtl = "";
float* vertexBuffer = new float[fileSize];
float* textureBuffer = new float[fileSize];
float* normalBuffer = new float[fileSize];
unsigned int* vertexIndices = new unsigned int[fileSize];
unsigned int* normalIndices = new unsigned int[fileSize];
unsigned int* textureIndices = new unsigned int[fileSize];
int connectedVert = 0;
int textureCoords = 0;
int normalsCount = 0;
int vertexIndex = 0;
string prefix = "usemtl";
while (!objFile.eof()) {
getline(objFile, line);
if (line.c_str()[0] == 'v' && line.c_str()[1] == ' ') {
line[0] = ' ';
sscanf_s(line.c_str(), "%f %f %f ",
&vertexBuffer[connectedVert],
&vertexBuffer[connectedVert + 1],
&vertexBuffer[connectedVert + 2]);
connectedVert += 3;
}
else if (line.c_str()[0] == 'v' && line.c_str()[1] == 't') {
line[0] = ' ';
line[1] = ' ';
sscanf_s(line.c_str(), "%f %f ",
&textureBuffer[textureCoords],
&textureBuffer[textureCoords + 1]);
textureCoords += 2;
}
else if (line.c_str()[0] == 'v' && line.c_str()[1] == 'n') {
line[0] = ' ';
line[1] = ' ';
sscanf_s(line.c_str(), "%f %f %f ",
&normalBuffer[normalsCount],
&normalBuffer[normalsCount + 1],
&normalBuffer[normalsCount + 2]);
normalsCount += 3;
}
else if (line.c_str()[0] == 'f') {
line[0] = ' ';
if (textureCoords > 0) {
sscanf_s(line.c_str(), "%i/%i/%i %i/%i/%i %i/%i/%i",
&vertexIndices[vertexIndex], &textureIndices[vertexIndex], &normalIndices[vertexIndex],
&vertexIndices[vertexIndex + 1], &textureIndices[vertexIndex +1], &normalIndices[vertexIndex +1],
&vertexIndices[vertexIndex + 2], &textureIndices[vertexIndex +2], &normalIndices[vertexIndex +2]);
}
else {
sscanf_s(line.c_str(), "%i//%i %i//%i %i//%i",
&vertexIndices[vertexIndex], &normalIndices[vertexIndex],
&vertexIndices[vertexIndex+1], &normalIndices[vertexIndex +1],
&vertexIndices[vertexIndex+2], &normalIndices[vertexIndex +2]);
}
for (int j = 0; j < 3; j++) {
vertexIndices[vertexIndex+j] -= 1;
normalIndices[vertexIndex +j] -= 1;
textureIndices[vertexIndex +j] -= 1;
}
vertexIndex += 3;
}
else if (!strncmp(line.c_str(), prefix.c_str(), strlen(prefix.c_str()))) {
inserted++;
// No more vertices, normals or texture coords to load, gen arrays on GPU
if (currentMtl == "") {
//Copy vertices to GPU
glGenBuffers(1, &this->verticesID);
glBindBuffer(GL_ARRAY_BUFFER, this->verticesID);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * connectedVert, vertexBuffer, GL_STATIC_DRAW);
/*
//Won't be bothered with normals or textures right now...
//Copy normals to GPU
glGenBuffers(1, &this->normalsID);
glBindBuffer(GL_ARRAY_BUFFER, this->normalsID);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * normalsCount, &normalBuffer[0], GL_STATIC_DRAW);
//Copy UV to GPU
if (textureCoords > 0) {
glGenBuffers(1, &this->textID);
glBindBuffer(GL_ARRAY_BUFFER, this->textID);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * textureCoords, &textureBuffer[0], GL_STATIC_DRAW);
}*/
}
else {
// Filled up an index array, copy it.
this->indices.push_back(0);
this->faces.push_back(vertexIndex);
glGenBuffers(1, &this->indices[this->indices.size() - 1]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->indices[this->indices.size() - 1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * vertexIndex, vertexIndices, GL_STATIC_DRAW);
}
currentMtl = line.substr(7, line.length());
vertexIndex = 0;
}
}
// Copy the last indices buffer.
if (currentMtl != "" && indices.size() < inserted) {
this->indices.push_back(0);
this->faces.push_back(vertexIndex);
glGenBuffers(1, &this->indices[this->indices.size() - 1]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->indices[this->indices.size() - 1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * vertexIndex, vertexIndices, GL_STATIC_DRAW);
}
objFile.close();
delete[] vertexBuffer;
delete[] textureBuffer;
delete[] normalBuffer;
delete[] vertexIndices;
delete[] normalIndices;
delete[] textureIndices;
}
else {
throw runtime_error("Unable to load obj file: " + path + ".obj");
}
}
Finally, here's the draw call. I'm guessing the problem is here but I can't quite put my finger on the issue. I've used glIntercept to see if there's something messed up but I got no errors there.
void Mesh::draw() {
//Binding Vertex array
glBindBuffer(GL_ARRAY_BUFFER, this->verticesID);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, (void*)0);
for (unsigned int i = 0; i < this->indices.size(); i++) {
glColor3f(1.0f, 1.0f, 1.0f); // Draw it in plain ol' white
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->indices[i]);
glDrawElements(GL_TRIANGLES,this->faces[i], GL_UNSIGNED_INT,(void*)0);
}
glDisableClientState(GL_VERTEX_ARRAY);
}
In case it's needed, here's the Mesh class declaration:
class Mesh {
public:
GLuint verticesID, textID, normalsID;
vector<GLuint> indices;
vector<GLuint> faces;
vector<Material> mats;
public:
Mesh(string path);
void draw();
~Mesh();
};
Setting up OpenGL major version as 2, and minor as 1. As I'm mentioned before, I think OpenGL is set up correctly because I'm able to draw triangles in both direct mode and using vertex pointers from RAM. Any help welcomed :)