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:
parent
79cb3f5193
commit
400bc443a2
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue