220 lines
4.6 KiB
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
|
||
|
}
|