I've been following the OpenGL tutorials from ThinMatrix and I've been able to load and render his stall.obj model along with his stall.png texture just fine. But when I try with my own models and textures from Blender, the texture appears "distorted" and I can't seem to find the problem. I'm guessing the problem is with my texture, but I can't find anything that would cause this problem. What could be the cause of this problem?
Here's how I load the .obj file:
void OBJLoader::load_file(const std::string& filename, std::vector<float>& vertices, std::vector<float>& uvs, std::vector<float>& normals, std::vector<int>& indices)
{
std::vector<float> temp_uvs;
std::vector<float> temp_normals;
std::ifstream myfile(filename);
std::string line;
if(myfile.is_open())
{
while(std::getline(myfile, line))
{
if(std::string("#os").find(line.front()) != std::string::npos) // Ignore lines which starts with #, o, s
continue;
std::vector<std::string> line_split = split(line, ' ');
std::string data_type = line_split[0];
line_split.erase(line_split.begin()); // Keep only numerical data
if(data_type == "v")
{
store_float_in_vector(vertices, line_split);
}
else if(data_type == "vt")
{
store_float_in_vector(temp_uvs, line_split);
}
else if(data_type == "vn")
{
store_float_in_vector(temp_normals, line_split);
}
if(data_type == "f")
{
for(std::string& vertex: line_split)
{
std::vector<std::string> data_split = split(vertex, '/');
int v = std::stoi(data_split[0]);
int vt = std::stoi(data_split[1]);
int vn = std::stoi(data_split[2]);
if(uvs.size() < (unsigned)(v-1)*2+2)
uvs.resize((v-1)*2+2);
if(normals.size() < (unsigned)(v-1)*3+3)
normals.resize((v-1)*3+3);
indices.push_back(v-1); // Indices in .obj files start at 1
uvs[(v-1)*2] = temp_uvs[(vt-1)*2];
uvs[(v-1)*2+1] = 1.0f - temp_uvs[(vt-1)*2+1]; // Blender ¯\_(ツ)_/¯
normals[(v-1)*3] = temp_normals[(vn-1)*3];
normals[(v-1)*3+1] = temp_normals[(vn-1)*3+1];
normals[(v-1)*3+2] = temp_normals[(vn-1)*3+2];
}
}
}
myfile.close();
}
else
std::cout << "Can't open: " << filename << std::endl;
}
Here's the obj file:
# Blender v2.82 (sub 7) OBJ File: ''
# www.blender.org
o Cube
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
vt 0.875000 0.500000
vt 0.625000 0.750000
vt 0.625000 0.500000
vt 0.375000 1.000000
vt 0.375000 0.750000
vt 0.625000 0.000000
vt 0.375000 0.250000
vt 0.375000 0.000000
vt 0.375000 0.500000
vt 0.125000 0.750000
vt 0.125000 0.500000
vt 0.625000 0.250000
vt 0.875000 0.750000
vt 0.625000 1.000000
vn 0.0000 1.0000 0.0000
vn 0.0000 0.0000 1.0000
vn -1.0000 0.0000 0.0000
vn 0.0000 -1.0000 0.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
s off
f 5/1/1 3/2/1 1/3/1
f 3/2/2 8/4/2 4/5/2
f 7/6/3 6/7/3 8/8/3
f 2/9/4 8/10/4 6/11/4
f 1/3/5 4/5/5 2/9/5
f 5/12/6 2/9/6 6/7/6
f 5/1/1 7/13/1 3/2/1
f 3/2/2 7/14/2 8/4/2
f 7/6/3 5/12/3 6/7/3
f 2/9/4 4/5/4 8/10/4
f 1/3/5 3/2/5 4/5/5
f 5/12/6 1/3/6 2/9/6
And here's the png file I used for the texture:
And here's how I load the texture with SDL:
void Texture::load(const std::string& filename)
{
glBindTexture(GL_TEXTURE_2D, texture_id);
GLenum gl_format;
SDL_Surface* surface_texture = IMG_Load(filename.c_str());
if(surface_texture == nullptr)
std::cout << "IMG_Load error: " << IMG_GetError() << std::endl;
if(surface_texture->format->BytesPerPixel == 4)
{
if(surface_texture->format->Rmask == 0x000000FF)
gl_format = GL_RGBA;
else
gl_format = GL_BGRA;
}
else
{
if(surface_texture->format->Rmask == 0x000000FF)
gl_format = GL_RGB;
else
gl_format = GL_BGR;
}
glTexImage2D(GL_TEXTURE_2D, 0, gl_format, surface_texture->w, surface_texture->h, 0, gl_format, GL_UNSIGNED_BYTE, surface_texture->pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
SDL_FreeSurface(surface_texture); // Free SDL_Surface memory
glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture
}
Here's the code for rendering the model:
void Model::create_EBO(int indices[], size_t s_indices)
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, s_indices, indices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // Unbind buffer
}
void Model::create_VBO(float data[], size_t s_data, unsigned int index, unsigned int coord_format)
{
vbos.push_back(0); // Declare new vbo
GLuint& vbo = vbos.back();
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, s_data, data, GL_STATIC_DRAW);
glVertexAttribPointer(index, coord_format, GL_FLOAT, GL_FALSE, 0, (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0); // Unbind buffer
}
void Model::load(float vertices[], size_t s_vertices, float uvs[], size_t s_uvs, float normals[], size_t s_normals, int indices[], size_t s_indices)
{
glBindVertexArray(vao);
create_EBO(indices, s_indices);
create_VBO(vertices, s_vertices, 0, 3);
create_VBO(uvs, s_uvs, 1, 2);
create_VBO(normals, s_normals, 2, 3);
glBindVertexArray(0); // Unbind VAO and associated VBOs
vertex_count = s_indices/sizeof(indices[0]);
}
void Model::render()
{
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glDrawElements(GL_TRIANGLES, vertex_count, GL_UNSIGNED_INT, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // Unbind EBO
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glBindVertexArray(0); // Unbind VAO
}