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:
#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!
Your vertex positions and uv coords are indexed the same way, but that's not how you are setting up your buffers.

Specifically: this:

Code:

        for(auto pt : vertices){
            vboData.push_back(pt.x);
            vboData.push_back(pt.y);
            vboData.push_back(pt.z);
        }


Sets up your vertex positions to be indexed correctly. You need to do the same thing with your uvs. These are indexed the same exact way:


Code:

        for(auto uv : texCoords){
            vboTexCoords.push_back(uv.x);
            vboTexCoords.push_back(uv.y);
        }


Then remove the per face stuff you are doing with the tex coords. As the face lookup in the vboTexCoords buffer is entirely determined by the index.

Cheers!
so are you saying that the index buffer object can handle both the vertex buffer object and the texture buffer object? if so, how would I set that up. As is, removing the code and adding that doesn't solve the problem... but I'm sure I'm missing something
When you index, say, index 2 from your vertex shader, it is going to look up the third xyz vertex position from vboData, and the third uv texture coordinated from vboTexCoords, yes. Your current code is not setting up vboTexCoords in this way.
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement