I've written my own wavefront obj loader, and for the most part it works. The obj itself loads perfectly, but the texture doesn't. It seems that the texture coords received from my obj loader are incorrect somehow. How do I properly load the texture coords from a .obj file for use with opengl?
here's my obj loader
Code:
here's my main.cpp file-
Code:
here's my vertex shader-
Code:
and here's my fragment shader-
Code:
here's my duke.obj file (a simple cube)-
Code:
as you can see, the texture loads correctly, but is not wrapped around the cube as desired... In blender I placed the texture with the dogs face on each face of the cube.
any help is much appreciated!
here's my obj loader
Code:
#pragma once
#include <fstream>
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <regex>
#include <GL/glew.h>
#include <glm/glm.hpp>
struct face{
std::vector<int> v;
std::vector<int> vt;
std::vector<int> vn;
};
enum {
vertex,
texture,
normal
};
class obj{
public:
obj(){};
obj(const char* objLoc){
std::ifstream file(objLoc);
std::string currLine = "";
inputType = new bool[3];
getInputType(objLoc);
while(std::getline(file, currLine)){
float x, y, z;
if(currLine.compare(0, 2, "v ") == 0){
currLine.erase(0, 2);
std::stringstream ss(currLine);
ss >> x;
ss >> y;
ss >> z;
vertices.push_back(glm::vec3(x, y, z));
}
else if(currLine.compare(0, 2, "vt") == 0){
currLine.erase(0, 3);
std::stringstream ss(currLine);
ss >> x;
ss >> y;
texCoords.push_back(glm::vec2(x, y));
}
else if(currLine.compare(0, 2, "vn") == 0){
currLine.erase(0, 3);
std::stringstream ss(currLine);
ss >> x;
ss >> y;
ss >> z;
normals.push_back(glm::vec3(x, y, z));
}
else if(currLine.compare(0, 2, "f ") == 0){
currLine.erase(0, 2);
face currentFace;
int vI, vtI, vnI;
std::stringstream currLineStream(currLine);
std::string currPt;
while(std::getline(currLineStream, currPt, ' ')){
currPt = std::regex_replace(currPt, std::regex("/"), " ");
std::stringstream currPtStream(currPt);
if(inputType[vertex]) {
currPtStream >> vI;
currentFace.v.push_back(vI - 1);
}
if(inputType[texture]) {
currPtStream >> vtI;
currentFace.vt.push_back(vtI - 1);
}
if(inputType[normal]) {
currPtStream >> vnI;
currentFace.vn.push_back(vnI - 1);
}
}
faces.push_back(currentFace);
}
}
for(auto pt : vertices){
vboData.push_back(pt.x);
vboData.push_back(pt.y);
vboData.push_back(pt.z);
}
for(auto f : faces){
if(f.v.size() == 4){
vaoData.push_back(f.v[0]);
vaoData.push_back(f.v[1]);
vaoData.push_back(f.v[2]);
vaoData.push_back(f.v[0]);
vaoData.push_back(f.v[2]);
vaoData.push_back(f.v[3]);
}
else if(f.v.size() == 3){
vaoData.push_back(f.v[0]);
vaoData.push_back(f.v[1]);
vaoData.push_back(f.v[2]);
}
if(f.vt.size() == 4){
vboTexCoords.push_back(texCoords[f.vt[0]].x);
vboTexCoords.push_back(texCoords[f.vt[0]].y);
vboTexCoords.push_back(texCoords[f.vt[1]].x);
vboTexCoords.push_back(texCoords[f.vt[1]].y);
vboTexCoords.push_back(texCoords[f.vt[2]].x);
vboTexCoords.push_back(texCoords[f.vt[2]].y);
vboTexCoords.push_back(texCoords[f.vt[0]].x);
vboTexCoords.push_back(texCoords[f.vt[0]].y);
vboTexCoords.push_back(texCoords[f.vt[2]].x);
vboTexCoords.push_back(texCoords[f.vt[2]].y);
vboTexCoords.push_back(texCoords[f.vt[3]].x);
vboTexCoords.push_back(texCoords[f.vt[3]].y);
}
else if(f.vt.size() == 3){
vboTexCoords.push_back(texCoords[f.vt[0]].x);
vboTexCoords.push_back(texCoords[f.vt[0]].y);
vboTexCoords.push_back(texCoords[f.vt[1]].x);
vboTexCoords.push_back(texCoords[f.vt[1]].y);
vboTexCoords.push_back(texCoords[f.vt[2]].x);
vboTexCoords.push_back(texCoords[f.vt[2]].y);
}
}
}
std::vector<float> getVertexBufferData(){ return vboData; }
std::vector<int> getIndexBufferObjectData(){ return vaoData; }
std::vector<float> getTexCoords(){ return vboTexCoords; }
std::vector<float> getNormals(){ return vboNormals; }
private:
bool *inputType;
//values from obj file
std::vector<glm::vec3> vertices;
std::vector<glm::vec2> texCoords;
std::vector<glm::vec3> normals;
std::vector<face> faces;
//values for gl
std::vector<float> vboData;
std::vector<int> vaoData;
std::vector<float> vboTexCoords;
std::vector<float> vboNormals;
void getInputType(const char* objLoc){
std::ifstream file(objLoc);
std::string currLine = "";
while(getline(file, currLine)){
if(currLine.compare(0, 2, "f ") == 0){
currLine.erase(0, 2);
std::stringstream currLineStream(currLine);
std::string in_between;
int count;
for(count = 1; std::getline(currLineStream, in_between, '/') && in_between.find(" ") == std::string::npos; count++){
std::cout << in_between << std::endl;
if(in_between == ""){
inputType[vertex] = true;
inputType[texture] = false;
inputType[normal] = true;
return;
}
}
if(count == 1){
inputType[vertex] = true;
inputType[texture] = false;
inputType[normal] = false;
}
else if(count == 2){
inputType[vertex] = true;
inputType[texture] = true;
inputType[normal] = false;
}
else if(count == 3){
inputType[vertex] = true;
inputType[texture] = true;
inputType[normal] = true;
}
return;
}
}
}
};
here's my main.cpp file-
Code:
#include "shader.h"
#include "texture.h"
#include "objects.h"
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
void init(GLFWwindow* window);
void display(GLFWwindow* window, double currentTime);
glm::vec3 cameraLoc;
GLuint program;
GLuint vMatLoc, projMatLoc, mMatLoc;
GLuint vbo[3];
GLuint vao[1];
int w, h;
float aspect;
glm::mat4 pMat, vMat;
obj o;
GLuint tex;
int main(int, char**) {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
GLFWwindow* window = glfwCreateWindow(600, 600, "glfw", NULL, NULL);
glfwMakeContextCurrent(window);
glewInit();
glfwSwapInterval(1);
init(window);
while(!glfwWindowShouldClose(window)){
glfwPollEvents();
display(window, glfwGetTime());
glfwSwapBuffers(window);
}
glfwDestroyWindow(window);
glfwTerminate();
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods){
if(key == GLFW_KEY_D){
cameraLoc.x++;
}
else if(key == GLFW_KEY_A){
cameraLoc.x--;
}
else if(key == GLFW_KEY_W){
cameraLoc.z--;
}
else if(key == GLFW_KEY_S){
cameraLoc.z++;
}
else if(key == GLFW_KEY_SPACE){
cameraLoc.y++;
}
else if(key == GLFW_KEY_LEFT_SHIFT){
cameraLoc.y--;
}
}
void drawModel(glm::mat4 model){
glUniformMatrix4fv(mMatLoc, 1, GL_FALSE, glm::value_ptr(model));
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
glDrawElements(GL_TRIANGLES, o.getIndexBufferObjectData().size(), GL_UNSIGNED_INT, 0);
}
void display(GLFWwindow* window, double currentTime){
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glUseProgram(program);
vMat = glm::translate(glm::mat4(1.0), -cameraLoc);
glUniformMatrix4fv(projMatLoc, 1, GL_FALSE, glm::value_ptr(pMat));
glUniformMatrix4fv(vMatLoc, 1, GL_FALSE, glm::value_ptr(vMat));
drawModel(glm::translate(glm::mat4(1), glm::vec3(0, 0, 0)));
}
void resizeCallback(GLFWwindow* window, int width, int height){
glfwSetWindowSize(window, width, height);
glfwGetFramebufferSize(window, &w, &h);
aspect = (float)w/(float)h;
pMat = glm::perspective(glm::radians(60.0f), aspect, 0.1f, 1000.0f);
}
void init(GLFWwindow* window){
glfwSetKeyCallback(window, key_callback);
glfwSetWindowSizeCallback(window, resizeCallback);
resizeCallback(window, 600, 600);
program = createProgram("shaders/vert.glsl", "shaders/frag.glsl");
tex = loadTexture("duke.jpg");
o = obj("duke.obj");
glGenVertexArrays(1, vao);
glBindVertexArray(vao[0]);
//gen vertex buffer object
glGenBuffers(3, vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, o.getVertexBufferData().size() * sizeof(float), &o.getVertexBufferData()[0], GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
//gen texture buffer object
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, o.getTexCoords().size() * sizeof(float), &o.getTexCoords()[0], GL_STATIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
//gen index buffer object
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[2]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, o.getIndexBufferObjectData().size() * sizeof(int), &o.getIndexBufferObjectData()[0], GL_STATIC_DRAW);
vMatLoc = glGetUniformLocation(program, "v_matrix");
projMatLoc = glGetUniformLocation(program, "proj_matrix");
mMatLoc = glGetUniformLocation(program, "m_matrix");
cameraLoc = glm::vec3(0.0f, 0.0f, 2.0f);
}
here's my vertex shader-
Code:
#version 430
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoord;
out vec2 tc;
layout (binding = 0) uniform sampler2D samp;
uniform mat4 m_matrix;
uniform mat4 v_matrix;
uniform mat4 proj_matrix;
void main(void){
mat4 mv_matrix = v_matrix * m_matrix;
gl_Position = proj_matrix * mv_matrix * vec4(position, 1.0);
tc = texCoord;
}
and here's my fragment shader-
Code:
#version 430
in vec2 tc;
out vec4 color;
layout (binding = 0) uniform sampler2D samp;
uniform mat4 m_matrix;
uniform mat4 v_matrix;
uniform mat4 proj_matrix;
void main(void){
color = texture(samp, tc);
}
here's my duke.obj file (a simple cube)-
Code:
# Blender v2.90.1 OBJ File: ''
# www.blender.org
mtllib duke.mtl
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.618850 0.505208
vt 0.368850 0.755208
vt 0.368850 0.505208
vt 0.625000 0.500000
vt 0.375000 0.750000
vt 0.375000 0.500000
vt 0.618752 0.505208
vt 0.368752 0.755208
vt 0.368752 0.505208
vt 0.624902 0.505208
vt 0.374902 0.755208
vt 0.374902 0.505208
vt 0.625000 0.500000
vt 0.375000 0.750000
vt 0.375000 0.500000
vt 0.621876 0.505208
vt 0.371876 0.755208
vt 0.371876 0.505208
vt 0.618850 0.755208
vt 0.625000 0.750000
vt 0.618752 0.755208
vt 0.624902 0.755208
vt 0.625000 0.750000
vt 0.621876 0.755208
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
usemtl Material
s off
f 5/1/1 3/2/1 1/3/1
f 3/4/2 8/5/2 4/6/2
f 7/7/3 6/8/3 8/9/3
f 2/10/4 8/11/4 6/12/4
f 1/13/5 4/14/5 2/15/5
f 5/16/6 2/17/6 6/18/6
f 5/1/1 7/19/1 3/2/1
f 3/4/2 7/20/2 8/5/2
f 7/7/3 5/21/3 6/8/3
f 2/10/4 4/22/4 8/11/4
f 1/13/5 3/23/5 4/14/5
f 5/16/6 1/24/6 2/17/6
as you can see, the texture loads correctly, but is not wrapped around the cube as desired... In blender I placed the texture with the dogs face on each face of the cube.
any help is much appreciated!