私は何か間違ったことをしていると確信しています.OBJ形式の3Dモデルを解析し、jsonオブジェクトを出力するGoプログラムを持っています. ゴルーチンを追加せずに実行すると、次の出力が得られます。
$ go run objParser.go ak47.obj extincteur_obj.obj
--Creating ak47.json3d from ak47.obj
--Exported 85772 faces with 89088 verticies
--Creating extincteur_obj.json3d from extincteur_obj.obj
--Exported 150316 faces with 151425 verticies
Parsed 2 files in 8.4963s
次に、ゴルーチンを追加すると、次の出力が得られます。
$ go run objParser.go ak47.obj extincteur_obj.obj
--Creating ak47.json3d from ak47.obj
--Creating extincteur_obj.json3d from extincteur_obj.obj
--Exported 85772 faces with 89088 verticies
--Exported 150316 faces with 151425 verticies
Parsed 2 files in 10.23137s
印刷される順序は、解析のインターレースを考慮して予想したものですが、実際に時間がかかる理由はわかりません! コードはかなり長いです。可能な限り省略しましたが、それでもかなり長いです。申し訳ありません!
package main
func parseFile(name string, finished chan int) {
var Verts []*Vertex
var Texs []*TexCoord
var Faces []*Face
var objFile, mtlFile, jsonFile *os.File
var parseMaterial bool
// Set up files and i/o
inName := name
outName := strings.Replace(inName, ".obj", ".json3d", -1)
parseMaterial = false
fmt.Printf("--"+FgGreen+"Creating"+Reset+" %s from %s\n", outName, inName)
var err error
var part []byte
var prefix bool
if objFile, err = os.Open(inName); err != nil {
fmt.Println(FgRed+"!!Failed to open input file!!"+Reset)
return
}
if jsonFile, err = os.Create(outName); err != nil {
fmt.Println(FgRed+"!!Failed to create output file!!"+Reset)
return
}
reader := bufio.NewReader(objFile)
writer := bufio.NewWriter(jsonFile)
buffer := bytes.NewBuffer(make([]byte, 1024))
// Read the file in and parse out what we need
for {
if part, prefix, err = reader.ReadLine(); err != nil {
break
}
buffer.Write(part)
if !prefix {
line := buffer.String()
if(strings.Contains(line, "v ")) {
Verts = append(Verts, parseVertex(line))
} else if(strings.Contains(line, "vt ")) {
Texs = append(Texs, parseTexCoord(line))
} else if(strings.Contains(line, "f ")) {
Faces = append(Faces, parseFace(line, Verts, Texs))
} else if(strings.Contains(line, "mtllib ")) {
mtlName := strings.Split(line, " ")[1]
if mtlFile, err = os.Open(mtlName); err != nil {
fmt.Printf("--"+FgRed+"Failed to find material file: %s\n"+Reset, mtlName)
parseMaterial = false
} else {
parseMaterial = true
}
}
buffer.Reset()
}
}
if err == io.EOF {
err = nil
}
objFile.Close()
// Write out the data
writer.WriteString("{\"obj\":[\n");
// Write out the verts
writer.WriteString("{\"vrt\":[\n");
for i, vert := range Verts {
writer.WriteString(vert.String())
if i < len(Verts) - 1 { writer.WriteString(",") }
writer.WriteString("\n")
}
// Write out the faces
writer.WriteString("],\"fac\":[\n")
for i, face := range Faces {
writer.WriteString(face.String(true))
if i < len(Faces) - 1 { writer.WriteString(",") }
writer.WriteString("\n")
}
// Write out the normals
writer.WriteString("],\"nrm\":[")
for i, face := range Faces {
writer.WriteString("[")
for j, vert := range face.verts {
length := math.Sqrt((vert.X * vert.X) + (vert.Y * vert.Y) + (vert.Z * vert.Z))
x := vert.X / length
y := vert.Y / length
z := vert.Z / length
normal := fmt.Sprintf("[%f,%f,%f]", x, y, z)
writer.WriteString(normal)
if(j < len(face.verts)-1) { writer.WriteString(",") }
}
writer.WriteString("]")
//writer.WriteString("[0, 1, 0]")
if i < len(Faces) - 1 { writer.WriteString(",") }
writer.WriteString("\n")
}
// Write out the tex coords
writer.WriteString("],\"tex\":[")
for i, face := range Faces {
writer.WriteString("[")
writer.WriteString(face.tex[0].String())
writer.WriteString(",")
writer.WriteString(face.tex[1].String())
writer.WriteString(",")
writer.WriteString(face.tex[2].String())
writer.WriteString("]")
if i < len(Faces) - 1 { writer.WriteString(",") }
writer.WriteString("\n")
}
// Close obj block
writer.WriteString("]}]");
if parseMaterial {
writer.WriteString(",mat:[{");
reader := bufio.NewReader(mtlFile)
// Read the file in and parse out what we need
for {
if part, prefix, err = reader.ReadLine(); err != nil {
break
}
buffer.Write(part)
if !prefix {
line := buffer.String()
if(strings.Contains(line, "map_Kd ")) {
parts := strings.Split(line, " ")
entry := fmt.Sprintf("\"t\":\"%s\",", parts[1])
writer.WriteString(entry)
width, height := 256, 256
var imageFile *os.File
if imageFile, err = os.Open(parts[1]); err != nil {
fmt.Printf("--"+FgRed+"Failed to find %s, defaulting to 256x256"+Reset+"\n", parts[1])
return
} else {
var config image.Config
imageReader := bufio.NewReader(imageFile)
config, err = jpeg.DecodeConfig(imageReader)
width, height = config.Width, config.Height
fmt.Printf("--"+FgGreen+"Verifing"+Reset+" that %s is %dpx x %dpx\n", parts[1], width, height)
}
size := fmt.Sprintf("\"w\":%d,\"h\":%d,", width, height)
writer.WriteString(size)
} else if(strings.Contains(line, "Kd ")) {
parts := strings.Split(line, " ")
entry := fmt.Sprintf("\"r\":%s, \"g\":%s, \"b\":%s,", parts[1], parts[2], parts[3])
writer.WriteString(entry)
}
buffer.Reset()
}
}
if err == io.EOF {
err = nil
}
writer.WriteString("\"res\":100,\"uv\":true}]");
}
// Close json
writer.WriteString("}");
writer.Flush()
jsonFile.Close()
fmt.Printf("--"+FgGreen+"Exported"+Reset+" %d faces with %d verticies\n", len(Faces), len(Verts))
finished <- -1
}
func main(){
// Verify we were called correctly
if len(os.Args) < 2 {
fmt.Println("Usage: go run objParser.go <OBJ File>");
return
}
files := len(os.Args)
finished := make(chan int)
now := time.Now()
for i := 1; i < files; i++ {
go parseFile(os.Args[i], finished)
}
for i := 1; i < files; i++ {
<- finished
}
fmt.Printf("Parsed %d files in %s\n", files-1, time.Since(now))
}