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 }