LGTM=nigeltao R=nigeltao, adg CC=davidday, golang-codereviews https://golang.org/cl/139510043
336 lines
6.6 KiB
Go
336 lines
6.6 KiB
Go
// 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
|
|
}
|