go.mobile/example/gopher: parse wavefront obj file

LGTM=nigeltao
R=nigeltao, adg
CC=davidday, golang-codereviews
https://golang.org/cl/139510043
This commit is contained in:
David Crawshaw 2014-09-17 18:35:56 -04:00
parent 79cb3f5193
commit 400bc443a2
1 changed files with 335 additions and 0 deletions

335
example/gopher/gengopher.go Normal file
View File

@ -0,0 +1,335 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//+build ignore
// Gengopher processes a simplified wavefront object file into a set of triangles.
//
// Usage:
// go run gengopher.go -output gopher.go -input gopher.obj
//
// Normally used via go:generate.
package main
import (
"bufio"
"bytes"
"compress/flate"
"encoding/binary"
"flag"
"fmt"
"go/format"
"io"
"log"
"os"
"strconv"
"strings"
)
var (
output = flag.String("output", "gopher.go", "output file name")
input = flag.String("input", "gopher.obj", "input file name")
)
type vector struct {
x, y, z float32
}
type face struct {
// index values into the vertices, textures, and normals slices.
v [3]int // index into vertices
t [3]int // index into textures (not implemented)
n [3]int // index into normals
}
type obj struct {
name string
faces []face
}
var vertices []vector
var normals []vector
var objs []obj
func main() {
flag.Parse()
if err := parseAndWrite(); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(2)
}
}
func parseAndWrite() (err error) {
out, err := os.Create(*output)
if err != nil {
return err
}
defer func() {
outerr := out.Close()
if err == nil {
err = outerr
}
}()
in, err := os.Open(*input)
if err != nil {
return err
}
defer in.Close()
if err := parse(in); err != nil {
return err
}
buf := new(bytes.Buffer)
writeObjs(buf, objs)
b, err := format.Source(buf.Bytes())
if err != nil {
os.Stdout.Write(buf.Bytes()) // useful for debugging
return err
}
_, err = out.Write(b)
return err
}
func writeObjs(w io.Writer, objs []obj) {
fmt.Fprint(w, preamble)
for _, obj := range objs {
log.Printf("writing %s", obj.name)
vdata, ndata := obj.triangleBytes()
fmt.Fprintf(w, "var %s = []byte{", obj.name)
writeBytes(w, vdata)
fmt.Fprintf(w, "\n}\n\n")
fmt.Fprintf(w, "var %sNormals = []byte{", obj.name)
writeBytes(w, ndata)
fmt.Fprintf(w, "\n}\n\n")
}
}
func writeBytes(w io.Writer, data []byte) {
for i, b := range data {
if i%12 == 0 {
fmt.Fprintf(w, "\n\t")
}
fmt.Fprintf(w, "0x%02x, ", b)
}
}
var preamble = `// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Generated by gengopher.go, do not edit manually.
// Original data data has the copyright:
//
// The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/)
// The gopher 3D model was made by Takuya Ueda (http://u.hinoichi.net).
// Licensed under the Creative Commons 3.0 Attributions license.
//
// Model from http://github.com/golang-samples/gopher-3d
// Each variable contains gzipped 3-component float32 triangle data.
package main
`
func (obj obj) triangleBytes() (vdata, ndata []byte) {
var v, n []float32
for _, f := range obj.faces {
l := len(vertices)
if f.v[0] > l || f.v[1] > l || f.v[2] > l {
log.Printf("bad face %v, len(vertices)=%d", f, l)
continue
}
for _, ind := range f.v {
val := vertices[ind]
v = append(v, val.x, val.y, val.z)
}
if f.n[0] >= 0 {
for _, ind := range f.n {
val := normals[ind]
n = append(n, val.x, val.y, val.z)
}
}
}
var dn []byte
if len(n) > 0 {
dn = deflate(n)
}
return deflate(v), dn
}
func deflate(v []float32) []byte {
buf := new(bytes.Buffer)
w, err := flate.NewWriter(buf, 9)
if err != nil {
log.Fatal(err)
}
if err := binary.Write(w, binary.LittleEndian, v); err != nil {
log.Fatal(err)
}
if err := w.Close(); err != nil {
log.Fatal(err)
}
return buf.Bytes()
}
func parse(r io.Reader) (err error) {
line := 0
defer func() {
if err != nil {
err = fmt.Errorf("%d: %v", line, err)
}
}()
o := obj{}
done := func() {
if o.name == "" {
return
}
objs = append(objs, o)
fmt.Fprintf(os.Stderr, "%5d faces: %s\n", len(o.faces), o.name)
o = obj{}
}
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line++
b := scanner.Bytes()
if len(b) == 0 {
continue
}
switch {
case b[0] == '#':
// comment, ignore
case b[0] == 's', b[0] == 'l':
// TODO(crawshaw): what is this?
case b[0] == 'o':
done()
o.name = strings.Replace(string(b[2:]), ".", "_", -1)
case b[0] == 'f':
f, err := parseFace(b[2:])
if err != nil {
return err
}
o.faces = append(o.faces, f)
case b[0] == 'v':
switch b[1] {
case ' ':
v, err := parseVector(b[2:])
if err != nil {
return err
}
vertices = append(vertices, v)
case 't':
return fmt.Errorf("vt not supported")
case 'n':
v, err := parseVector(b[3:])
if err != nil {
return err
}
normals = append(normals, v)
case 'p':
return fmt.Errorf("vp not supported")
}
case bytes.HasPrefix(b, []byte("mtllib")):
// ignore
case bytes.HasPrefix(b, []byte("usemtl")):
// ignore
default:
return fmt.Errorf("unknown wavefront prefix: %q", b[0])
}
}
done()
return scanner.Err()
}
func parseVector(line []byte) (vector, error) {
var v vector
i := bytes.IndexRune(line, ' ')
if i == -1 {
return v, fmt.Errorf("expected three vector values, found one")
}
x, err := strconv.ParseFloat(string(line[:i]), 32)
if err != nil {
return v, fmt.Errorf("vector x: %s", err)
}
v.x = float32(x)
line = line[i+1:]
i = bytes.IndexRune(line, ' ')
if i == -1 {
return v, fmt.Errorf("expected three vector values, found two")
}
y, err := strconv.ParseFloat(string(line[:i]), 32)
if err != nil {
return v, fmt.Errorf("vector y: %s", err)
}
v.y = float32(y)
line = line[i+1:]
i = bytes.IndexRune(line, ' ')
if i != -1 {
return v, fmt.Errorf("expected three vector values, found more")
}
z, err := strconv.ParseFloat(string(line), 32)
if err != nil {
return v, fmt.Errorf("vector z: %s", err)
}
v.z = float32(z)
return v, nil
}
func parseFace(line []byte) (face, error) {
var f face
pos := 0
for i := 0; i < 3; i++ {
pos = bytes.IndexRune(line, ' ')
if i < 2 && pos == -1 {
return f, fmt.Errorf("expected three face values, found %d", i)
} else if i == 2 {
if pos != -1 {
return f, fmt.Errorf("expected three face values, found more")
}
pos = len(line)
}
var vals []int
for _, m := range bytes.Split(line[:pos], []byte{'/'}) {
if len(m) == 0 {
vals = append(vals, -1)
continue
}
v, err := strconv.ParseInt(string(m), 10, 32)
if err != nil {
return f, fmt.Errorf("face[%d]: %s", i, err)
}
// wavefront faces are 1-indexed.
vals = append(vals, int(v)-1)
}
f.v[i] = vals[0]
f.t[i] = vals[1]
f.n[i] = vals[2]
line = line[pos:]
if len(line) > 0 && line[0] == ' ' {
line = line[1:] // remove space
}
}
return f, nil
}