Skip to content

Instantly share code, notes, and snippets.

@davemackintosh
Last active September 20, 2023 09:31
Show Gist options
  • Save davemackintosh/67959fa9dfd9018d79a4 to your computer and use it in GitHub Desktop.
Save davemackintosh/67959fa9dfd9018d79a4 to your computer and use it in GitHub Desktop.
Really simple OBJ loader in Golang. Done this loads in C++ and thought I'd see how easy it was in Go, turns out it's really easy. No license, feel free to whatever with this.
package model
import (
"fmt"
"io"
"os"
"github.com/go-gl/mathgl/mgl32"
)
// Model is a renderable collection of vecs.
type Model struct {
// For the v, vt and vn in the obj file.
Normals, Vecs []mgl32.Vec3
Uvs []mgl32.Vec2
// For the fun "f" in the obj file.
VecIndices, NormalIndices, UvIndices []float32
}
// NewModel will read an OBJ model file and create a Model from its contents
func NewModel(file string) Model {
// Open the file for reading and check for errors.
objFile, err := os.Open(file)
if err != nil {
panic(err)
}
// Don't forget to close the file reader.
defer objFile.Close()
// Create a model to store stuff.
model := Model{}
// Read the file and get it's contents.
for {
var lineType string
// Scan the type field.
_, err := fmt.Fscanf(objFile, "%s", &lineType)
// Check if it's the end of the file
// and break out of the loop.
if err != nil {
if err == io.EOF {
break
}
}
// Check the type.
switch lineType {
// VERTICES.
case "v":
// Create a vec to assign digits to.
vec := mgl32.Vec3{}
// Get the digits from the file.
fmt.Fscanf(objFile, "%f %f %f\n", &vec[0], &vec[1], &vec[2])
// Add the vector to the model.
model.Vecs = append(model.Vecs, vec)
// NORMALS.
case "vn":
// Create a vec to assign digits to.
vec := mgl32.Vec3{}
// Get the digits from the file.
fmt.Fscanf(objFile, "%f %f %f\n", &vec[0], &vec[1], &vec[2])
// Add the vector to the model.
model.Normals = append(model.Normals, vec)
// TEXTURE VERTICES.
case "vt":
// Create a Uv pair.
vec := mgl32.Vec2{}
// Get the digits from the file.
fmt.Fscanf(objFile, "%f %f\n", &vec[0], &vec[1])
// Add the uv to the model.
model.Uvs = append(model.Uvs, vec)
// INDICES.
case "f":
// Create a vec to assign digits to.
norm := make([]float32, 3)
vec := make([]float32, 3)
uv := make([]float32, 3)
// Get the digits from the file.
matches, _ := fmt.Fscanf(objFile, "%f/%f/%f %f/%f/%f %f/%f/%f\n", &vec[0], &uv[0], &norm[0], &vec[1], &uv[1], &norm[1], &vec[2], &uv[2], &norm[2])
if matches != 9 {
panic("Cannot read your file")
}
// Add the numbers to the model.
model.NormalIndices = append(model.NormalIndices, norm[0])
model.NormalIndices = append(model.NormalIndices, norm[1])
model.NormalIndices = append(model.NormalIndices, norm[2])
model.VecIndices = append(model.VecIndices, vec[0])
model.VecIndices = append(model.VecIndices, vec[1])
model.VecIndices = append(model.VecIndices, vec[2])
model.UvIndices = append(model.UvIndices, uv[0])
model.UvIndices = append(model.UvIndices, uv[1])
model.UvIndices = append(model.UvIndices, uv[2])
}
}
// Return the newly created Model.
return model
}
// GetRenderableVertices returns a slice of float32s
// formatted in X, Y, Z, U, V. That is, XYZ of the
// vertex and the texture position.
func (model Model) GetRenderableVertices() []float32 {
// Create a slice for the outward float32s.
var out []float32
// Loop over each vec3 in the indices property.
for _, position := range model.VecIndices {
index := int(position) - 1
vec := model.Vecs[index]
uv := model.Uvs[int(model.UvIndices[index])-1]
out = append(out, vec.X(), vec.Y(), vec.Z(), uv.X(), uv.Y())
}
// Return the array.
return out
}
@PPrinsloo
Copy link

Hi Dave, I'm busy learning OpenGL. Would you mind if I use this as a foundation to work from in model loading? I don't see a license so figured I would ask. I will probably be changing it quite a lot but it looks like a great base to work from.

@davemackintosh
Copy link
Author

davemackintosh commented Jul 4, 2020

Feel free to use this, I absolutely cannot say whether it still works or not but go ahead and use/attribute, honestly don't remember this code at all haha. Take care and stay safe.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment