I'm currently doing simple 3D drawing in OpenGL but I have hit a roadblock I just can't seem to get around. The problem is happening with trying to load .obj files. I've written my own .obj reader.
I am using SDL2 and GLEW. I use SOIL for loading textures.
When loading in a texture on a model, the texture on "front" and "back" are always correct, like this:
But on the other sides, it's completely warped, like this:
Now I think it has something to do with the texture coordinates, but I have no clue what. I've tried multiple .obj files, even simple cubes. All experience the same weird glitch.
Model loader
ModelData* ObjFormatLoader::loadModel(std::string t_filename)
{
std::string fileAdress = "..//Assets//Models//" + t_filename + ".obj";
Logger::GetInstance()->logAction("loading .obj file " + t_filename);
if(!std::experimental::filesystem::exists(fileAdress))
{
Logger::GetInstance()->logError("No such file exists.", __LINE__, __FILE__);
return nullptr;
}
std::ifstream model(fileAdress);
std::vector<glm::fvec3> vertices = std::vector<glm::fvec3>();
std::vector<glm::fvec2> textures = std::vector<glm::fvec2>();
std::vector<glm::fvec3> normals = std::vector<glm::fvec3>();
std::vector<GLuint> indices;
std::vector<GLfloat> verticesArray = std::vector<GLfloat>();
std::vector<GLfloat> normalsArray = std::vector<GLfloat>();
std::vector<GLfloat> textureArray = std::vector<GLfloat>();
std::string line;
while (getline(model, line))
{
std::vector<std::string> currentLine = splitStringDel(line, ' ');
if (line.find("v ") == 0)
{
vertices.push_back(glm::fvec3(atof(currentLine.at(1).c_str()), atof(currentLine.at(2).c_str()), atof(currentLine.at(3).c_str())));
}
else if (line.find("vt ") == 0)
{
textures.push_back(glm::fvec2(atof(currentLine.at(1).c_str()), atof(currentLine.at(2).c_str())));
}
else if (line.find("vn ") == 0)
{
normals.push_back(glm::fvec3(atof(currentLine.at(1).c_str()), atof(currentLine.at(2).c_str()), atof(currentLine.at(3).c_str())));
}
else if (line.find("f ") == 0)
{
textureArray.resize(vertices.size() * 2);
normalsArray.resize(vertices.size() * 3);
break;
}
}
model.clear();
model.seekg(0, std::ios::beg);
while (getline(model, line))
{
if (line.find("f ") == 0)
{
std::vector<std::string> currentLine = splitStringDel(line, ' ');
std::vector<std::string> vertex1 = splitStringDel(currentLine.at(1), '/');
std::vector<std::string> vertex2 = splitStringDel(currentLine.at(2), '/');
std::vector<std::string> vertex3 = splitStringDel(currentLine.at(3), '/');
processVertex(vertex1, indices, textures, normals, textureArray, normalsArray);
processVertex(vertex2, indices, textures, normals, textureArray, normalsArray);
processVertex(vertex3, indices, textures, normals, textureArray, normalsArray);
}
}
model.close();
verticesArray.resize(vertices.size() * 3);
int vertexPointer = 0;
for (glm::fvec3 Vertex : vertices)
{
verticesArray[vertexPointer++] = Vertex.x;
verticesArray[vertexPointer++] = Vertex.y;
verticesArray[vertexPointer++] = Vertex.z;
}
return ModelManager::GetInstance()->loadModelToVao(verticesArray, indices, textureArray);
}
void ObjFormatLoader::processVertex(const std::vector<std::string> &t_vertex_data, std::vector<GLuint> &t_indices_data, const std::vector<glm::vec2> &t_texture_data, const std::vector<glm::vec3> &t_normals_data, std::vector<GLfloat> &t_texture_array, std::vector<GLfloat> &t_normals_data_array)
{
int currentVertex = atoi(t_vertex_data.at(0).c_str()) - 1;
t_indices_data.push_back(currentVertex);
glm::vec2 currentTex = t_texture_data.at(atoi(t_vertex_data.at(1).c_str()) - 1);
t_texture_array[currentVertex * 2] = currentTex.x;
t_texture_array[currentVertex * 2 + 1] = 1.0f - currentTex.y;
glm::vec3 currentNormal = t_normals_data.at(atoi(t_vertex_data.at(2).c_str()) - 1);
t_normals_data_array[currentVertex * 3] = currentNormal.x;
t_normals_data_array[currentVertex * 3 + 1] = currentNormal.y;
t_normals_data_array[currentVertex * 3 + 2] = currentNormal.z;
}
std::vector<std::string> ObjFormatLoader::splitStringDel(std::string const& original, char separator)
{
std::vector<std::string> results;
std::string::const_iterator start = original.begin();
std::string::const_iterator end = original.end();
std::string::const_iterator next = std::find(start, end, separator);
while (next != end) {
results.push_back(std::string(start, next));
start = next + 1;
next = std::find(start, end, separator);
}
results.push_back(std::string(start, next));
return results;
}
Drawing
void RenderManager::renderEntity(BaseEntity * t_entity, StandardShader* t_shader)
{
auto model = t_entity->getModel();
auto modelData = model->getModelData();
auto textureData = model->getTextureData();
glBindVertexArray(modelData->getVaoId());
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
auto transformationMatrix = MathHelper::createTransformationMatrix(
t_entity->getPosition(),
t_entity->getRotation(),
t_entity->getScale()
);
t_shader->loadTransformationMatrix(transformationMatrix);
t_shader->loadProjectionMatrix(MathHelper::createProjectionMatrix());
t_shader->loadViewMatrix(MathHelper::createViewMatrix(WindowManager::GetInstance()->getCamera()));
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureData->getTextureId());
glDrawElements(GL_TRIANGLES, modelData->getVertexCount(), GL_UNSIGNED_INT, BUFFER_OFFSET(0));
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindVertexArray(0);
}
Texture loading
TextureData* ModelManager::loadTexture(std::string t_filename)
{
GLint texture;
std::string FileName = "..\\Assets\\Textures\\" + t_filename;
texture = SOIL_load_OGL_texture
(
FileName.c_str(),
SOIL_LOAD_AUTO,
SOIL_CREATE_NEW_ID,
SOIL_FLAG_MIPMAPS | SOIL_FLAG_TEXTURE_REPEATS
);
m_texture_vector.push_back(texture);
return new TextureData(texture);
}
Fragment shader
#version 400 core
in vec2 pass_textureCoords;
out vec4 out_Color;
uniform sampler2D tex0;
void main()
{
out_Color = texture(tex0, pass_textureCoords);
}
Vertex shader
#version 400 core
in vec3 position;
in vec2 textureCoords;
out vec2 pass_textureCoords;
uniform mat4 projectionMatrix;
uniform mat4 transformationMatrix;
uniform mat4 viewMatrix;
void main()
{
vec4 worldPosition = transformationMatrix * vec4(position, 1.0);
gl_Position = projectionMatrix * viewMatrix * worldPosition;
pass_textureCoords = textureCoords;
}
Cube.obj
# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
# File Created: 02.05.2019 21:59:07
#
# object Box002
#
v -2.5000 -2.5000 0.0000
v -2.5000 2.5000 0.0000
v 2.5000 2.5000 0.0000
v 2.5000 -2.5000 0.0000
v -2.5000 -2.5000 5.0000
v 2.5000 -2.5000 5.0000
v 2.5000 2.5000 5.0000
v -2.5000 2.5000 5.0000
# 8 vertices
vn 0.0000 0.0000 -1.0000
vn 0.0000 0.0000 1.0000
vn 0.0000 -1.0000 0.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 1.0000 0.0000
vn -1.0000 0.0000 0.0000
# 6 vertex normals
vt 0.6677 0.3323 0.0000
vt 0.6677 0.0005 0.0000
vt 0.9995 0.0005 0.0000
vt 0.9995 0.3323 0.0000
vt 0.3341 0.0005 0.0000
vt 0.6659 0.0005 0.0000
vt 0.6659 0.3323 0.0000
vt 0.3341 0.3323 0.0000
vt 0.0005 0.6677 0.0000
vt 0.3323 0.6677 0.0000
vt 0.3323 0.9995 0.0000
vt 0.0005 0.9995 0.0000
vt 0.3341 0.3422 0.0000
vt 0.6659 0.3422 0.0000
vt 0.6659 0.6740 0.0000
vt 0.3341 0.6740 0.0000
vt 0.0005 0.3341 0.0000
vt 0.3323 0.3341 0.0000
vt 0.3323 0.6659 0.0000
vt 0.0005 0.6659 0.0000
vt 0.0005 0.0005 0.0000
vt 0.3323 0.0005 0.0000
vt 0.3323 0.3323 0.0000
vt 0.0005 0.3323 0.0000
# 24 texture coords
o Box002
g Box002
s 2
f 1/1/1 2/2/1 3/3/1
f 3/3/1 4/4/1 1/1/1
s 4
f 5/5/2 6/6/2 7/7/2
f 7/7/2 8/8/2 5/5/2
s 8
f 1/9/3 4/10/3 6/11/3
f 6/11/3 5/12/3 1/9/3
s 16
f 4/13/4 3/14/4 7/15/4
f 7/15/4 6/16/4 4/13/4
s 32
f 3/17/5 2/18/5 8/19/5
f 8/19/5 7/20/5 3/17/5
s 64
f 2/21/6 1/22/6 5/23/6
f 5/23/6 8/24/6 2/21/6
# 12 faces
If any other code is needed, please let me know.