Skip to content

Commit

Permalink
support glb files without binary chunks
Browse files Browse the repository at this point in the history
  • Loading branch information
qmuntal committed Feb 25, 2021
1 parent 2b02742 commit 51b91b0
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 32 deletions.
64 changes: 38 additions & 26 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"io"
"os"
"path/filepath"
"unsafe"
)

// WriteHandler is the interface that wraps the Write method.
Expand Down Expand Up @@ -73,8 +72,11 @@ func (e *Encoder) Encode(doc *Document) error {
var err error
var externalBufferIndex = 0
if e.AsBinary {
err = e.encodeBinary(doc)
externalBufferIndex = 1
var hasBinChunk bool
hasBinChunk, err = e.encodeBinary(doc)
if hasBinChunk {
externalBufferIndex = 1
}
} else {
err = json.NewEncoder(e.w).Encode(doc)
}
Expand Down Expand Up @@ -103,44 +105,54 @@ func (e *Encoder) encodeBuffer(buffer *Buffer) error {
return e.WriteHandler.WriteResource(buffer.URI, buffer.Data)
}

func (e *Encoder) encodeBinary(doc *Document) error {
func (e *Encoder) encodeBinary(doc *Document) (bool, error) {
jsonText, err := json.Marshal(doc)
if err != nil {
return err
return false, 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
jsonHeader := chunkHeader{
Length: uint32(((len(jsonText) + 3) / 4) * 4),
Type: glbChunkJSON,
}
header := glbHeader{
Magic: glbHeaderMagic,
Version: 2,
Length: 12 + 8 + jsonHeader.Length, // 12-byte glb header + 8-byte json chunk header
JSONHeader: jsonHeader,
}
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
return false, err
}
e.w.Write(jsonText)
e.w.Write(headerPadding)
binary.Write(e.w, binary.LittleEndian, &binHeader)
if binBuffer != nil {

hasBinChunk := len(doc.Buffers) > 0 && doc.Buffers[0].URI == ""
if hasBinChunk {
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 := chunkHeader{Length: 0, Type: glbChunkBIN}
binHeader.Length = binPaddedLength
header.Length = uint32(8) + binHeader.Length
for i := range binPadding {
binPadding[i] = 0
}
binary.Write(e.w, binary.LittleEndian, &binHeader)
e.w.Write(binBuffer.Data)
_, err = e.w.Write(binPadding)
}
_, err = e.w.Write(binPadding)
return err

return hasBinChunk, err
}

// UnmarshalJSON unmarshal the node with the correct default values.
Expand Down
53 changes: 47 additions & 6 deletions encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"reflect"
"strings"
"testing"

"github.com/go-test/deep"
Expand Down Expand Up @@ -36,6 +37,52 @@ func saveMemory(doc *Document, asBinary bool) (*Decoder, error) {
return NewDecoder(buff).WithReadHandler(m), nil
}

func TestEncoder_Encode_AsBinary_WithoutBuffer(t *testing.T) {
doc := &Document{}
buff := new(bytes.Buffer)
e := NewEncoder(buff)
e.AsBinary = true
if err := e.Encode(doc); err != nil {
t.Errorf("Encoder.Encode() error = %v", err)
}
if strings.Contains(buff.String(), "BIN") {
t.Error("Encoder.Encode() as binary without bin buffer should not contain bin chunk")
}
}

func TestEncoder_Encode_AsBinary_WithoutBinChunk(t *testing.T) {
doc := &Document{Buffers: []*Buffer{
{Extras: 8.0, Name: "embedded", ByteLength: 2, URI: "data:application/octet-stream;base64,YW55ICsgb2xkICYgZGF0YQ==", Data: []byte("any + old & data")},
{Extras: 8.0, Name: "external", ByteLength: 4, URI: "b.bin", Data: []byte{4, 5, 6, 7}},
{Extras: 8.0, Name: "external", ByteLength: 4, URI: "a.drc", Data: []byte{0, 0, 0, 0}},
}}
buff := new(bytes.Buffer)
m := &mockChunkReadHandler{Chunks: make(map[string][]byte)}
e := NewEncoder(buff).WithWriteHandler(m)
e.AsBinary = true
if err := e.Encode(doc); err != nil {
t.Errorf("Encoder.Encode() error = %v", err)
}
if strings.Contains(buff.String(), "BIN") {
t.Error("Encoder.Encode() as binary without bin buffer should not contain bin chunk")
}
}

func TestEncoder_Encode_AsBinary_WithBinChunk(t *testing.T) {
doc := &Document{Buffers: []*Buffer{
{Extras: 8.0, Name: "binary", ByteLength: 3, Data: []byte{1, 2, 3}},
}}
buff := new(bytes.Buffer)
e := NewEncoder(buff)
e.AsBinary = true
if err := e.Encode(doc); err != nil {
t.Errorf("Encoder.Encode() error = %v", err)
}
if !strings.Contains(buff.String(), "BIN") {
t.Error("Encoder.Encode() as binary with bin buffer should contain bin chunk")
}
}

func TestEncoder_Encode(t *testing.T) {
type args struct {
doc *Document
Expand Down Expand Up @@ -72,12 +119,6 @@ func TestEncoder_Encode(t *testing.T) {
{Extras: 8.0, Input: Index(1), Output: Index(1), Interpolation: InterpolationCubicSpline},
}},
}}}, false},
{"withBuffer", args{&Document{Buffers: []*Buffer{
{Extras: 8.0, Name: "binary", ByteLength: 3, URI: "a.bin", Data: []byte{1, 2, 3}},
{Extras: 8.0, Name: "embedded", ByteLength: 2, URI: "data:application/octet-stream;base64,YW55ICsgb2xkICYgZGF0YQ==", Data: []byte("any + old & data")},
{Extras: 8.0, Name: "external", ByteLength: 4, URI: "b.bin", Data: []byte{4, 5, 6, 7}},
{Extras: 8.0, Name: "external", ByteLength: 4, URI: "a.drc", Data: []byte{0, 0, 0, 0}},
}}}, false},
{"withBufView", args{&Document{BufferViews: []*BufferView{
{Extras: 8.0, Buffer: 0, ByteOffset: 1, ByteLength: 2, ByteStride: 5, Target: TargetArrayBuffer},
{Buffer: 10, ByteOffset: 10, ByteLength: 20, ByteStride: 50, Target: TargetElementArrayBuffer},
Expand Down

0 comments on commit 51b91b0

Please sign in to comment.