tofu

Making something with OpenGL in Go
Log | Files | Refs

object.go (6813B)


      1 package tofu
      2 
      3 import (
      4 	"bufio"
      5 	"fmt"
      6 	"log"
      7 	"math"
      8 	"os"
      9 	"strconv"
     10 	"strings"
     11 
     12 	"github.com/go-gl/gl/v3.3-core/gl"
     13 )
     14 
     15 type Object struct {
     16 	Vertices  []Vec3
     17 	TexCoords []Vec2
     18 	Normals   []Vec3
     19 	Faces     []Face
     20 	vao       *VAO
     21 }
     22 
     23 func DecodeObject(filename string) (*Object, error) {
     24 	f, err := os.Open(filename)
     25 	if err != nil {
     26 		return nil, fmt.Errorf("open file %s: %v", filename, err)
     27 	}
     28 	defer f.Close()
     29 	obj := new(Object)
     30 	s := bufio.NewScanner(f)
     31 	nl := 1
     32 	for s.Scan() {
     33 		if err := decodeLine(obj, s.Text()); err != nil {
     34 			return nil, fmt.Errorf("%s:%d %s", filename, nl, err)
     35 		}
     36 		nl++
     37 	}
     38 	if err := s.Err(); err != nil {
     39 		return nil, err
     40 	}
     41 	return obj, nil
     42 }
     43 
     44 func decodeLine(obj *Object, line string) error {
     45 	if len(line) == 0 || line[0] == '#' {
     46 		return nil
     47 	}
     48 	sf := strings.Fields(line)
     49 	if len(sf) == 0 {
     50 		return nil
     51 	}
     52 	switch t := sf[0]; t {
     53 	case "v":
     54 		if err := obj.addVertex(sf[1:]); err != nil {
     55 			return fmt.Errorf("addVertex: %v", err)
     56 		}
     57 	case "vt":
     58 		if err := obj.addTexCoord(sf[1:]); err != nil {
     59 			return fmt.Errorf("addTexCoords: %v", err)
     60 		}
     61 	case "vn":
     62 		if err := obj.addNormal(sf[1:]); err != nil {
     63 			return fmt.Errorf("addNormal: %v", err)
     64 		}
     65 	case "f":
     66 		if err := obj.addFace(sf[1:]); err != nil {
     67 			return fmt.Errorf("addFace: %v", err)
     68 		}
     69 	default:
     70 		log.Printf("unsupported: %s, ignoring", t)
     71 	}
     72 	return nil
     73 }
     74 
     75 func (obj *Object) addVertex(s []string) error {
     76 	if len(s) != 3 {
     77 		return fmt.Errorf("want 3 coordinates, got %d", len(s))
     78 	}
     79 	var v Vec3
     80 	for i := 0; i < 3; i++ {
     81 		f, err := strconv.ParseFloat(s[i], 32)
     82 		if err != nil {
     83 			return err
     84 		}
     85 		v[i] = float32(f)
     86 	}
     87 	obj.Vertices = append(obj.Vertices, v)
     88 	return nil
     89 }
     90 
     91 func (obj *Object) addTexCoord(s []string) error {
     92 	if len(s) != 2 {
     93 		log.Println("only 2D texture is supported, ignoring the third element")
     94 	}
     95 	var t Vec2
     96 	for i := 0; i < 2; i++ {
     97 		f, err := strconv.ParseFloat(s[i], 32)
     98 		if err != nil {
     99 			return err
    100 		}
    101 		t[i] = float32(f)
    102 	}
    103 	obj.TexCoords = append(obj.TexCoords, t)
    104 	return nil
    105 }
    106 
    107 func (obj *Object) addNormal(s []string) error {
    108 	if len(s) != 3 {
    109 		return fmt.Errorf("want 3 coordinates, got %d", len(s))
    110 	}
    111 	var n Vec3
    112 	for i := 0; i < 3; i++ {
    113 		f, err := strconv.ParseFloat(s[i], 32)
    114 		if err != nil {
    115 			return err
    116 		}
    117 		n[i] = float32(f)
    118 	}
    119 	obj.Normals = append(obj.Normals, n)
    120 	return nil
    121 }
    122 
    123 func (obj *Object) addFace(s []string) error {
    124 	if len(s) != 3 {
    125 		return fmt.Errorf("want 3 pairs of indices, got %d", len(s))
    126 	}
    127 	var f Face
    128 	for i := 0; i < 3; i++ {
    129 		tuple := strings.Split(s[i], "/")
    130 		if len(tuple) == 0 || len(tuple) > 3 {
    131 			return fmt.Errorf("invalid face: %q", s[i])
    132 		}
    133 		v, err := strconv.Atoi(tuple[0])
    134 		if err != nil {
    135 			return fmt.Errorf("invalid index: %s", tuple[0])
    136 		}
    137 		f[i].V = v - 1
    138 		if len(tuple) > 1 && tuple[1] != "" {
    139 			t, err := strconv.Atoi(tuple[1])
    140 			if err != nil {
    141 				return fmt.Errorf("invalid index: %s", tuple[1])
    142 			}
    143 			f[i].T = t - 1
    144 		} else {
    145 			f[i].T = NoIndex
    146 		}
    147 		if len(tuple) == 3 {
    148 			n, err := strconv.Atoi(tuple[2])
    149 			if err != nil {
    150 				return fmt.Errorf("invalid index: %s", tuple[2])
    151 			}
    152 			f[i].N = n - 1
    153 		} else {
    154 			f[i].N = NoIndex
    155 		}
    156 	}
    157 	obj.Faces = append(obj.Faces, f)
    158 	return nil
    159 }
    160 
    161 var NoIndex = math.MinInt
    162 
    163 type VertexIndex struct {
    164 	V, N, T int
    165 }
    166 
    167 type Face [3]VertexIndex
    168 
    169 func (obj Object) stride() int {
    170 	if len(obj.Faces) < 1 {
    171 		return 0
    172 	}
    173 	var stride int = 3
    174 	if obj.Faces[0][0].N != NoIndex {
    175 		stride += 3
    176 	}
    177 	if obj.Faces[0][0].T != NoIndex {
    178 		stride += 2
    179 	}
    180 	return stride
    181 }
    182 
    183 func (obj Object) data() []float32 {
    184 	stride := obj.stride()
    185 	if stride == 0 {
    186 		return nil
    187 	}
    188 	data := make([]float32, stride*3*len(obj.Faces))
    189 	for i, f := range obj.Faces {
    190 		for j := 0; j < 3; j++ {
    191 			data[stride*(3*i+j)] = obj.Vertices[f[j].V][0]
    192 			data[stride*(3*i+j)+1] = obj.Vertices[f[j].V][1]
    193 			data[stride*(3*i+j)+2] = obj.Vertices[f[j].V][2]
    194 			switch stride {
    195 			case 5:
    196 				data[stride*(3*i+j)+3] = obj.TexCoords[f[j].T][0]
    197 				data[stride*(3*i+j)+4] = obj.TexCoords[f[j].T][1]
    198 			case 6:
    199 				data[stride*(3*i+j)+3] = obj.Normals[f[j].N][0]
    200 				data[stride*(3*i+j)+4] = obj.Normals[f[j].N][1]
    201 				data[stride*(3*i+j)+5] = obj.Normals[f[j].N][2]
    202 			case 8:
    203 				data[stride*(3*i+j)+3] = obj.TexCoords[f[j].T][0]
    204 				data[stride*(3*i+j)+4] = obj.TexCoords[f[j].T][1]
    205 				data[stride*(3*i+j)+5] = obj.Normals[f[j].N][0]
    206 				data[stride*(3*i+j)+6] = obj.Normals[f[j].N][1]
    207 				data[stride*(3*i+j)+7] = obj.Normals[f[j].N][2]
    208 			}
    209 		}
    210 	}
    211 	return data
    212 }
    213 
    214 func (obj Object) faceData() []uint32 {
    215 	fdata := make([]uint32, 3*len(obj.Faces))
    216 	for i := 0; i < 3*len(obj.Faces); i++ {
    217 		fdata[i] = uint32(i)
    218 	}
    219 	return fdata
    220 }
    221 
    222 func (obj *Object) SetNormals() {
    223 	obj.Normals = make([]Vec3, len(obj.Faces))
    224 	for i, f := range obj.Faces {
    225 		v := obj.Vertices[f[0].V].Sub(obj.Vertices[f[1].V])
    226 		w := obj.Vertices[f[0].V].Sub(obj.Vertices[f[2].V])
    227 		n := v.Cross(w).Normalize()
    228 		obj.Normals[i] = n
    229 		obj.Faces[i][0].N = i
    230 		obj.Faces[i][1].N = i
    231 		obj.Faces[i][2].N = i
    232 	}
    233 }
    234 
    235 // Load copies object data into the GPU.
    236 func (obj *Object) Load() {
    237 	obj.vao = newVAO(obj)
    238 }
    239 
    240 func (obj *Object) Draw(prog *Program) {
    241 	// TODO: performance?
    242 	obj.vao.bind()
    243 	gl.DrawElements(gl.TRIANGLES,
    244 		int32(obj.stride()*3*len(obj.Faces)),
    245 		gl.UNSIGNED_INT, nil)
    246 }
    247 
    248 type VAO struct {
    249 	id  uint32
    250 	vbo *Buffer
    251 	ebo *Buffer
    252 }
    253 
    254 const (
    255 	UniformVertex = iota
    256 	UniformColor
    257 	UniformTexCoords
    258 	UniformNormal
    259 )
    260 
    261 func newVAO(obj *Object) *VAO {
    262 	stride := obj.stride()
    263 	if stride == 0 {
    264 		return nil
    265 	}
    266 	var id uint32
    267 	gl.GenVertexArrays(1, &id)
    268 	vao := &VAO{id: id, vbo: newBuffer(), ebo: newBuffer()}
    269 	vao.bind()
    270 	data := obj.data()
    271 	vao.setData(data)
    272 	vao.setAttribute(UniformVertex, 3, stride, 0)
    273 	if stride == 5 {
    274 		vao.setAttribute(UniformTexCoords, 2, stride, 3)
    275 	} else if stride == 6 {
    276 		vao.setAttribute(UniformNormal, 3, stride, 3)
    277 	} else if stride == 8 {
    278 		vao.setAttribute(UniformTexCoords, 2, stride, 3)
    279 		vao.setAttribute(UniformNormal, 3, stride, 5)
    280 	}
    281 	fdata := obj.faceData()
    282 	vao.setFaces(fdata)
    283 	return vao
    284 }
    285 
    286 func (vao *VAO) bind() {
    287 	gl.BindVertexArray(vao.id)
    288 }
    289 
    290 func (vao *VAO) setAttribute(uniformNum uint32, size int, stride int, offset int) {
    291 	gl.VertexAttribPointerWithOffset(uniformNum, int32(size), gl.FLOAT,
    292 		false, int32(stride)*4, uintptr(offset)*4)
    293 	gl.EnableVertexAttribArray(uniformNum)
    294 }
    295 
    296 func (vao *VAO) setData(data []float32) {
    297 	gl.BindBuffer(gl.ARRAY_BUFFER, vao.vbo.id)
    298 	gl.BufferData(gl.ARRAY_BUFFER, len(data)*4,
    299 		gl.Ptr(data), gl.STATIC_DRAW)
    300 }
    301 
    302 func (vao *VAO) setFaces(data []uint32) {
    303 	gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, vao.ebo.id)
    304 	gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(data)*4,
    305 		gl.Ptr(data), gl.STATIC_DRAW)
    306 }
    307 
    308 type Buffer struct {
    309 	id uint32
    310 }
    311 
    312 func newBuffer() *Buffer {
    313 	var id uint32
    314 	gl.GenBuffers(1, &id)
    315 	return &Buffer{id: id}
    316 }