I wanted to implement an obj loader that would take 'v' (vertice), 'vt' (texture), 'vn' (normals), and 'f' (faces) coordinates from an obj file exported from blender and render it on my program. However, after implementing it I found that there was a problem with it. Nothing shows when I create it. Now, ever since I started, mesh code was always a problem for me so I'm hoping someone could find something that I cannot. Not only that but the texture code states (in terminal), Trying to access the pixels of an empty image
. However, the image I give it has pixels and I have absolutely no idea why this is happening.
// Chest.cpp
objLoader.loadObjModel("res/models/test.obj");
mesh.loadToVAO(objLoader.getVertices(), objLoader.getTextures(), objLoader.getNormals(), objLoader.getIndices());
mesh.loadTexture(Texture::get("res/images/wallpaper.png"));
program = Program::get(BASIC_VERTEX_SHADER, BASIC_FRAGMENT_SHADER);
This loads an obj model from test.obj
and then in mesh.loadToVAO
it gets the vertice, textures, normals, and indices. Then the mesh also gets the texture location.
// Mesh.cpp // Loading
void Mesh::loadToVAO(std::vector<glm::vec3> vertices, std::vector<glm::vec2> textures, std::vector<glm::vec3> normals, std::vector<int> indices) {
// create a VAO
GLuint vaoID = createVAO();
indicesSize = indices.size();
bindIndicesBuffer(indices.data(), indicesSize);
// Store the data in attribute lists
storeDataInAttrubeList(0, 3, &vertices[0], vertices.size() * sizeof(glm::vec3));
storeDataInAttrubeList(1, 2, &textures[0], textures.size() * sizeof(glm::vec2));
storeDataInAttrubeList(2, 3, &normals[0], normals.size() * sizeof(glm::vec3));
unbindVAO();
}
// Rendering
void Mesh::renderVAO() {
// Texture
texture->useTexture();
// Binding
glBindVertexArray(VAO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBO);
// Rendering
glDrawElements(GL_TRIANGLES, indicesSize, GL_UNSIGNED_INT, 0);
// Unbinding
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
// createVAO/storeDataInAttrubeList/bindIndicesBuffer
GLuint Mesh::createVAO() {
GLuint vaoID;
glGenVertexArrays(1, &vaoID);
VAO = vaoID;
glBindVertexArray(vaoID);
return vaoID;
}
void Mesh::storeDataInAttrubeList(GLuint attribNumber, int attribSize, void* data, int dataSize) {
GLuint vboID;
// Create a new buffer
glGenBuffers(1, &vboID);
// Store the buffer in the list
VBO = vboID;
// Bind the buffer to use it
glBindBuffer(GL_ARRAY_BUFFER, vboID);
// Store the data in the buffer
glBufferData(GL_ARRAY_BUFFER, dataSize, data, GL_STATIC_DRAW);
// Tell OpenGL how and where to store this VBO in the VAO
glVertexAttribPointer(attribNumber, attribSize, GL_FLOAT, GL_FALSE, 0, nullptr);
}
void Mesh::bindIndicesBuffer(int* indices, int& count) {
GLuint vboID;
// Generate a buffer and bind it for use
glGenBuffers(1, &vboID);
// Store the buffer in the list
VBO = vboID;
// Bind the buffer to use it
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboID);
// Store the indices in the buffer
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * count, indices, GL_STATIC_DRAW);
}
This goes through every line and finds the appropriate coordinate.
while (fgets(line, 256, file) != NULL) {
token = NULL;
type = strtok_s(line, " ", &token);
// Vertex
if (type[0] == 'v' && type[1] == NULL) {
x = strtod(token, &stop);
token = stop + 1;
y = strtod(token, &stop);
token = stop + 1;
z = strtod(token, &stop);
vertices.push_back(glm::vec3(x, y, z));
}
// Textures
else if (type[0] == 'v' && type[1] == 't') {
x = strtod(token, &stop);
token = stop + 1;
y = 1 - strtod(token, &stop);
tempTextures.push_back(glm::vec2(x, y));
}
// Normals
else if (type[0] == 'v' && type[1] == 'n') {
x = strtod(token, &stop);
token = stop + 1;
y = strtod(token, &stop);
token = stop + 1;
z = strtod(token, &stop);
tempNormals.push_back(glm::vec3(x, y, z));
}
// Faces
else if (type[0] == 'f') {
if (indices.size() == 0) {
// Set size of the array
textures.resize(vertices.size());
normals.resize(vertices.size());
}
// Process vertices data
processVertices(token, indices, tempTextures, textures, tempNormals, normals);
}
}
// This method process the vertice and indice information
void OBJLoader::processVertices(char* vertexData, std::vector<int>& indices, std::vector<glm::vec2>& tempTextures,
std::vector<glm::vec2>& textures, std::vector<glm::vec3>& tempNormals, std::vector<glm::vec3>& normals) {
char* stop;
int vertexPointer;
for (unsigned int i = 0; i < 3; i++) {
// Get and store index
vertexPointer = strtol(vertexData, &stop, 10) - 1;
indices.push_back(vertexPointer);
vertexData = stop + 1;
textures[vertexPointer] = tempTextures[strtol(vertexData, &stop, 10) - 1];
vertexData = stop + 1;
normals[vertexPointer] = tempNormals[strtol(vertexData, &stop, 10) - 1];
vertexData = stop + 1;
}
}
In Chest.cpp
you can see that I'm calling a method called get(something)
, this is what it looks like
std::vector<glm::vec3> getVertices() { return vertices; } // Note that vertices and etc. are vectors
So finally, is this not rendering or is it just not creating? I know the code is rather long, however I don't exactly know which parts are most important or most responsible for my problem.