-
Notifications
You must be signed in to change notification settings - Fork 16
/
obj-writer.go
130 lines (116 loc) · 3.42 KB
/
obj-writer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package main
import (
"compress/gzip"
"fmt"
"io"
"os"
"time"
"github.com/jonnenauha/obj-simplify/objectfile"
)
type Writer struct {
obj *objectfile.OBJ
}
func (wr *Writer) WriteFile(path string) (int, error) {
if fileExists(path) {
if err := os.Remove(path); err != nil {
return 0, err
}
}
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, os.ModePerm)
if err != nil {
return 0, err
}
linesWritten, errWrite := wr.WriteTo(f)
if cErr := f.Close(); cErr != nil && errWrite == nil {
errWrite = cErr
}
return linesWritten, errWrite
}
func (wr *Writer) WriteTo(writer io.Writer) (int, error) {
linesWritten := 0
w := writer
if StartParams.IsGzipEnabled() {
wGzip, errGzip := gzip.NewWriterLevel(writer, StartParams.Gzip)
if errGzip != nil {
return linesWritten, errGzip
}
defer wGzip.Close()
w = wGzip
}
ln := func() {
fmt.Fprint(w, "\n")
linesWritten++
}
writeLine := func(t objectfile.Type, value string, newline bool) {
fmt.Fprintf(w, "%s %s\n", t, value)
linesWritten++
if newline {
ln()
}
}
writeLines := func(t objectfile.Type, values []string, newline bool) {
for _, v := range values {
writeLine(t, v, false)
}
if newline && len(values) > 0 {
ln()
}
}
writeComments := func(t objectfile.Type, values []string, newline bool) {
if len(values) == 0 {
return
}
comments := make([]string, len(values))
copy(comments, values)
// remove empty lines from start and end.
// preserve empty lines in the center for long comments.
for len(comments) > 0 && comments[0] == "" {
comments = comments[1:]
}
for len(comments) > 0 && comments[len(comments)-1] == "" {
comments = comments[0 : len(comments)-1]
}
writeLines(t, comments, newline)
}
obj := wr.obj
// leave a comment that signifies this tool was ran on the file
writeLine(objectfile.Comment, fmt.Sprintf("Processed with %s %s | %s | %s", ApplicationName, getVersion(false), time.Now().UTC().Format(time.RFC3339), ApplicationURL), true)
// comments
writeComments(objectfile.Comment, obj.Comments, true)
// Materials (I think there is always just one, if this can change mid file, this needs to be adjusted and pos tracked during parsing)
writeLines(objectfile.MtlLib, obj.MaterialLibraries, true)
// geometry
for ti, t := range []objectfile.Type{objectfile.Vertex, objectfile.Normal, objectfile.UV, objectfile.Param} {
if slice := obj.Geometry.Get(t); len(slice) > 0 {
if ti > 0 {
ln()
}
writeLine(objectfile.Comment, fmt.Sprintf("%s [%d]", t.Name(), len(slice)), true)
for _, value := range slice {
writeLine(t, value.String(t), false)
}
}
}
ln()
// objects: preserves the parsing order of g/o
writeLine(objectfile.Comment, fmt.Sprintf("objects [%d]", len(obj.Objects)), true)
for _, child := range obj.Objects {
writeComments(objectfile.Comment, child.Comments, true)
writeLine(child.Type, child.Name, false)
// we dont skip writing material if it has already been declared as the
// last material, the file is easier to read for humans with write on each
// child, and this wont take many bytes in the total file size.
if len(child.Material) > 0 {
writeLine(objectfile.MtlUse, child.Material, false)
}
ln()
for _, vd := range child.VertexData {
if sgroup := vd.Meta(objectfile.SmoothingGroup); len(sgroup) > 0 {
writeLine(objectfile.SmoothingGroup, sgroup, false)
}
writeLine(vd.Type, vd.String(), false)
}
ln()
}
return linesWritten, nil
}