2

I wrote an obj loader in GoLang and am attempting to render a cube. The cube is already pre triangulated and I have made sure the UV's are set correctly in blender before exporting it. The issue is the texture is not rendering correctly on the cube and I'm not sure what's causing it. I do sort the texture coords and normals by the indices first. What I currently get: Cube Side Cube Front

What I expect to get: Expected

Code (Here's a Gist link to if that makes it)

package obj

import (
    "bytes"
    "fmt"
    "github.com/go-gl/mathgl/mgl32"
    "io"
    "kami/render/models"
    "kami/util"
)

func LoadModel(file string) models.Model {
    objData := util.ReadAsset(file)
    objReader := bytes.NewReader(objData)
    modelPart := models.ModelPart{}

    var x, y, z float32
    var textureCoords []mgl32.Vec2
    var normals []mgl32.Vec3

    for {
        var lineType string
        _, err := fmt.Fscanf(objReader, "%s", &lineType)

        if err != nil {
            if err == io.EOF {
                break
            }
        }

        switch lineType {
        // VERTICES.
        case "v":
            fmt.Fscanf(objReader, "%f %f %f\n", &x, &y, &z)
            modelPart.Vertices = append(modelPart.Vertices, x, y, z)

        // NORMALS.
        case "vn":
            fmt.Fscanf(objReader, "%f %f %f\n", &x, &y, &z)
            normals = append(normals, mgl32.Vec3{x, y, z})

        // TEXTURE VERTICES.
        case "vt":
            fmt.Fscanf(objReader, "%f %f\n", &x, &y)
            textureCoords = append(textureCoords, mgl32.Vec2{x, y})

        // INDICES.
        case "f":
            norm := make([]float32, 4)
            indices := make([]float32, 4)
            uv := make([]float32, 4)
            matches, _ := fmt.Fscanf(objReader, "%f/%f/%f %f/%f/%f %f/%f/%f %f/%f/%f\n", &indices[0], &uv[0], &norm[0], &indices[1], &uv[1], &norm[1], &indices[2], &uv[2], &norm[2], &indices[3], &uv[3], &norm[3])

            if (matches != 9 && matches != 12) || textureCoords == nil || normals == nil {
                panic("Cannot read OBJ file")
            }

            modelPart.Indices = append(modelPart.Indices, uint32(indices[0]-1))
            modelPart.Indices = append(modelPart.Indices, uint32(indices[1]-1))
            modelPart.Indices = append(modelPart.Indices, uint32(indices[2]-1))

            modelPart.TextureCoords = append(modelPart.TextureCoords, textureCoords[int(uv[0]) -1].X(), 1 - textureCoords[int(uv[0]) -1].Y())
            modelPart.TextureCoords = append(modelPart.TextureCoords, textureCoords[int(uv[1]) -1].X(), 1 - textureCoords[int(uv[1]) -1].Y())
            modelPart.TextureCoords = append(modelPart.TextureCoords, textureCoords[int(uv[2]) -1].X(), 1 - textureCoords[int(uv[2]) -1].Y())

            modelPart.Normals = append(modelPart.Normals, normals[int(norm[0]) - 1].X(), normals[int(norm[0]) - 1].Y(), normals[int(norm[0]) - 1].Z())
            modelPart.Normals = append(modelPart.Normals, normals[int(norm[1]) - 1].X(), normals[int(norm[1]) - 1].Y(), normals[int(norm[1]) - 1].Z())
            modelPart.Normals = append(modelPart.Normals, normals[int(norm[2]) - 1].X(), normals[int(norm[2]) - 1].Y(), normals[int(norm[2]) - 1].Z())

            //Triangulate if face is a Quad
            if matches == 12 {
                modelPart.Indices = append(modelPart.Indices, uint32(indices[0]-1), uint32(indices[2]-1), uint32(indices[3]-1))

                modelPart.TextureCoords = append(modelPart.TextureCoords, textureCoords[int(uv[0]) -1].X(), 1 - textureCoords[int(uv[0]) -1].Y())
                modelPart.TextureCoords = append(modelPart.TextureCoords, textureCoords[int(uv[2]) -1].X(), 1 - textureCoords[int(uv[2]) -1].Y())
                modelPart.TextureCoords = append(modelPart.TextureCoords, textureCoords[int(uv[3]) -1].X(), 1 - textureCoords[int(uv[3]) -1].Y())

                modelPart.Normals = append(modelPart.Normals, normals[int(norm[0]) - 1].X(), normals[int(norm[0]) - 1].Y(), normals[int(norm[0]) - 1].Z())
                modelPart.Normals = append(modelPart.Normals, normals[int(norm[2]) - 1].X(), normals[int(norm[2]) - 1].Y(), normals[int(norm[2]) - 1].Z())
                modelPart.Normals = append(modelPart.Normals, normals[int(norm[3]) - 1].X(), normals[int(norm[3]) - 1].Y(), normals[int(norm[3]) - 1].Z())
            }
        }
    }

    modelPart.GenerateModelVAO()
    return models.Model{Parts:[]models.ModelPart{modelPart}}
}

For actually rendering the model, I Simple bind the texture, create a transformation matrix, bind the VAO i generated earlier, and then draw the elements

gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, texture)
rotation := mgl32.AnglesToQuat(0, 0, 0, mgl32.XYZ)
transformMatrix := render.CreateTransformMatrix(mgl32.Vec3{0, 0, -10}, rotation, 1)

for _, element := range cubeModel.Parts {
    element.Vao.Bind()
    gl.UniformMatrix4fv(transformationMatrixUniform, 1, false, &transformMatrix[0])
    gl.DrawElements(gl.TRIANGLES, int32(len(element.Indices)), gl.UNSIGNED_INT, gl.Ptr(element.Indices))
}

1 Answers1

1

Turns out the issue was that I was sorting the Texture Coordinates when reading the face, and OpenGL was trying to render the UV based on its vertex indices. Fixing it was as simple as sorting the vertices as well and then creating a new Indices array that started from 0 to the length of vertices since all the data was sorted properly now.