forked from qmuntal/gltf
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathencode.go
More file actions
141 lines (126 loc) · 3.59 KB
/
Copy pathencode.go
File metadata and controls
141 lines (126 loc) · 3.59 KB
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
131
132
133
134
135
136
137
138
139
140
141
package gltf
import (
"encoding/binary"
"encoding/json"
"io"
"os"
"path/filepath"
"unsafe"
)
// WriteHandler is the interface that wraps the Write method.
//
// WriteResource should behaves as io.Write in terms of reading the writing resource.
type WriteHandler interface {
WriteResource(uri string, data []byte) error
}
// Save will save a document as a glTF with the specified by name.
func Save(doc *Document, name string) error {
return save(doc, name, false)
}
// SaveBinary will save a document as a GLB file with the specified by name.
func SaveBinary(doc *Document, name string) error {
return save(doc, name, true)
}
func save(doc *Document, name string, asBinary bool) error {
f, err := os.Create(name)
if err != nil {
return err
}
e := NewEncoder(f).WithWriteHandler(&RelativeFileHandler{Dir: filepath.Dir(name)})
e.AsBinary = asBinary
if err := e.Encode(doc); err != nil {
f.Close()
return err
}
return f.Close()
}
// An Encoder writes a GLTF to an output stream
// with relative external buffers support.
type Encoder struct {
AsBinary bool
WriteHandler WriteHandler
w io.Writer
}
// NewEncoder returns a new encoder that writes to w as a normal glTF file.
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{
AsBinary: true,
WriteHandler: new(RelativeFileHandler),
w: w,
}
}
// WithWriteHandler sets the WriteHandler.
func (e *Encoder) WithWriteHandler(h WriteHandler) *Encoder {
e.WriteHandler = h
return e
}
// Encode writes the encoding of doc to the stream.
func (e *Encoder) Encode(doc *Document) error {
if doc.Asset.Version == "" {
doc.Asset.Version = "2.0"
}
var err error
var externalBufferIndex = 0
if e.AsBinary {
err = e.encodeBinary(doc)
externalBufferIndex = 1
} else {
err = json.NewEncoder(e.w).Encode(doc)
}
if err != nil {
return err
}
for i := externalBufferIndex; i < len(doc.Buffers); i++ {
buffer := &doc.Buffers[i]
if len(buffer.Data) == 0 || buffer.IsEmbeddedResource() {
continue
}
if err = e.encodeBuffer(buffer); err != nil {
return err
}
}
return err
}
func (e *Encoder) encodeBuffer(buffer *Buffer) error {
if err := validateBufferURI(buffer.URI); err != nil {
return err
}
return e.WriteHandler.WriteResource(buffer.URI, buffer.Data)
}
func (e *Encoder) encodeBinary(doc *Document) error {
jsonText, err := json.Marshal(doc)
if err != nil {
return err
}
header := glbHeader{Magic: glbHeaderMagic, Version: 2, Length: 0, JSONHeader: chunkHeader{Length: 0, Type: glbChunkJSON}}
binHeader := chunkHeader{Length: 0, Type: glbChunkBIN}
var binBufferLength uint32
var binBuffer *Buffer
if len(doc.Buffers) > 0 {
binBuffer = &doc.Buffers[0]
binBufferLength = binBuffer.ByteLength
}
binPaddedLength := ((binBufferLength + 3) / 4) * 4
binPadding := make([]byte, binPaddedLength-binBufferLength)
binHeader.Length = binPaddedLength
header.JSONHeader.Length = uint32(((len(jsonText) + 3) / 4) * 4)
header.Length = uint32(unsafe.Sizeof(header)+unsafe.Sizeof(binHeader)) + header.JSONHeader.Length + binHeader.Length
headerPadding := make([]byte, header.JSONHeader.Length-uint32(len(jsonText)))
for i := range headerPadding {
headerPadding[i] = ' '
}
for i := range binPadding {
binPadding[i] = 0
}
err = binary.Write(e.w, binary.LittleEndian, &header)
if err != nil {
return err
}
binary.Write(e.w, binary.LittleEndian, jsonText)
binary.Write(e.w, binary.LittleEndian, headerPadding)
binary.Write(e.w, binary.LittleEndian, &binHeader)
if binBuffer != nil {
binary.Write(e.w, binary.LittleEndian, binBuffer.Data)
}
return binary.Write(e.w, binary.LittleEndian, binPadding)
}