opengl-deferred/types/geometry/geometry.go

220 lines
4.6 KiB
Go

package geometry
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"github.com/go-gl/gl/v4.6-core/gl"
)
// attributes:
// position 3 * float
// normal 3 * float
// texture coordinates 2 * float
type Geometry struct {
vao uint32
vbo uint32
ebo uint32
size int32
}
func (g Geometry) Draw() {
gl.BindVertexArray(g.vao)
gl.BindBuffer(gl.ARRAY_BUFFER, g.vbo)
gl.DrawElementsWithOffset(gl.TRIANGLES, g.size, gl.UNSIGNED_INT, 0)
}
func (g Geometry) Delete() {
gl.DeleteVertexArrays(1, &g.vao)
gl.DeleteBuffers(1, &g.ebo)
gl.DeleteBuffers(1, &g.vbo)
}
type vec struct {
Values []float32
}
type point struct {
Vertex int
Normal int
Uv int
}
type face struct {
Points [3]point
}
func new(vertices []vec, normals []vec, uvs []vec, faces []face) (Geometry, error) {
fmt.Println(len(vertices), len(normals), len(uvs), len(faces))
i := 0
points := make(map[point]int)
indicies := []uint{}
for _, face := range faces {
for _, point := range face.Points {
if val, ok := points[point]; !ok {
points[point] = i
indicies = append(indicies, uint(i))
i++
} else {
indicies = append(indicies, uint(val))
}
}
}
rPoints := make(map[int]point)
for k, v := range points {
rPoints[v] = k
}
buffer := make([]float32, len(rPoints)*8)
for i := 0; i < len(rPoints); i++ {
v := rPoints[i]
buffer[i*8+0] = vertices[v.Vertex].Values[0]
buffer[i*8+1] = vertices[v.Vertex].Values[1]
buffer[i*8+2] = vertices[v.Vertex].Values[2]
buffer[i*8+3] = normals[v.Normal].Values[0]
buffer[i*8+4] = normals[v.Normal].Values[1]
buffer[i*8+5] = normals[v.Normal].Values[2]
buffer[i*8+6] = uvs[v.Uv].Values[0]
buffer[i*8+7] = uvs[v.Uv].Values[1]
}
var geometry Geometry
// vao
gl.GenVertexArrays(1, &geometry.vao)
gl.BindVertexArray(geometry.vao)
// vbo
gl.GenBuffers(1, &geometry.vbo)
gl.BindBuffer(gl.ARRAY_BUFFER, geometry.vbo)
gl.BufferData(gl.ARRAY_BUFFER, len(buffer)*4, gl.Ptr(buffer), gl.STATIC_DRAW)
// attributes
gl.VertexAttribPointerWithOffset(0, 3, gl.FLOAT, false, 8*4, 0)
gl.EnableVertexAttribArray(0)
gl.VertexAttribPointerWithOffset(1, 3, gl.FLOAT, false, 8*4, 3*4)
gl.EnableVertexAttribArray(1)
gl.VertexAttribPointerWithOffset(2, 2, gl.FLOAT, false, 8*4, 6*4)
gl.EnableVertexAttribArray(2)
// ebo
gl.GenBuffers(1, &geometry.ebo)
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, geometry.ebo)
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(indicies)*4, gl.Ptr(indicies), gl.STATIC_DRAW)
geometry.size = int32(len(indicies))
return geometry, nil
}
func LoadOBJ(path string) (geometries []Geometry, err error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
var vertices []vec
var normals []vec
var uvs []vec
var faces []face
scanner := bufio.NewScanner(file)
for scanner.Scan() {
switch scanner.Text()[0:2] {
case "# ":
continue
case "mt":
fmt.Println("material ignored")
continue
case "o ":
if len(vertices) != 0 {
geometry, err := new(vertices, normals, uvs, faces)
if err != nil {
return nil, err
}
geometries = append(geometries, geometry)
}
case "v ":
parts := strings.Split(scanner.Text(), " ")
vertex := vec{}
for i := 1; i < 4; i++ {
value, err := strconv.ParseFloat(parts[i], 32)
if err != nil {
return nil, err
}
vertex.Values = append(vertex.Values, float32(value))
}
vertices = append(vertices, vertex)
case "vn":
parts := strings.Split(scanner.Text(), " ")
normal := vec{}
for i := 1; i < 4; i++ {
value, err := strconv.ParseFloat(parts[i], 32)
if err != nil {
return nil, err
}
normal.Values = append(normal.Values, float32(value))
}
normals = append(normals, normal)
case "vt":
parts := strings.Split(scanner.Text(), " ")
uv := vec{}
for i := 1; i < 3; i++ {
value, err := strconv.ParseFloat(parts[i], 32)
if err != nil {
return nil, err
}
uv.Values = append(uv.Values, float32(value))
}
uvs = append(uvs, uv)
case "f ":
parts := strings.Split(scanner.Text(), " ")
face := face{}
for i, part := range parts[1:] {
bits := strings.Split(part, "/")
v, err := strconv.Atoi(bits[0])
if err != nil {
return nil, err
}
t, err := strconv.Atoi(bits[1])
if err != nil {
return nil, err
}
n, err := strconv.Atoi(bits[2])
if err != nil {
return nil, err
}
face.Points[i].Vertex = v - 1
face.Points[i].Normal = n - 1
face.Points[i].Uv = t - 1
}
faces = append(faces, face)
}
}
geometry, err := new(vertices, normals, uvs, faces)
if err != nil {
return nil, err
}
geometries = append(geometries, geometry)
return geometries, err
}