From 400bc443a2a41fec7b18d0abf2677fc59dd2a2ec Mon Sep 17 00:00:00 2001 From: David Crawshaw Date: Wed, 17 Sep 2014 18:35:56 -0400 Subject: [PATCH] go.mobile/example/gopher: parse wavefront obj file LGTM=nigeltao R=nigeltao, adg CC=davidday, golang-codereviews https://golang.org/cl/139510043 --- example/gopher/gengopher.go | 335 ++++++++++++++++++++++++++++++++++++ 1 file changed, 335 insertions(+) create mode 100644 example/gopher/gengopher.go diff --git a/example/gopher/gengopher.go b/example/gopher/gengopher.go new file mode 100644 index 0000000..1b156cf --- /dev/null +++ b/example/gopher/gengopher.go @@ -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 +}