Skip to content

Commit

Permalink
Merge f1a6fdb into 30dd10c
Browse files Browse the repository at this point in the history
  • Loading branch information
qmuntal committed Nov 26, 2019
2 parents 30dd10c + f1a6fdb commit d3f64f8
Show file tree
Hide file tree
Showing 16 changed files with 2,022 additions and 939 deletions.
1 change: 0 additions & 1 deletion .sonarcloud.properties

This file was deleted.

38 changes: 37 additions & 1 deletion README.md
Expand Up @@ -17,6 +17,7 @@ A Go package for simple, efficient, and robust serialization/deserialization of
* [x] ASCII glTF
* [x] Binary glTF(GLB)
* [x] PBR material description
* [x] Modeler package
* glTF validaton
* [ ] Validate against schemas
* [ ] Validate coherence
Expand Down Expand Up @@ -82,7 +83,42 @@ if err != nil {
fmt.Print(doc.Asset)
```

### Save
### Create a glb using gltf/modeler

```go
package main

import (
"github.com/qmuntal/gltf"
"github.com/qmuntal/gltf/modeler"
)

func main() {
m := modeler.NewModeler()
positionAccessor := m.AddPosition(0, [][3]float32{{43, 43, 0}, {83, 43, 0}, {63, 63, 40}, {43, 83, 0}, {83, 83, 0}})
indicesAccessor := m.AddIndices(0, []uint8{0, 1, 2, 3, 1, 0, 0, 2, 3, 1, 4, 2, 4, 3, 2, 4, 1, 3})
colorIndices := m.AddColor(0, [][3]uint8{{50, 155, 255}, {0, 100, 200}, {255, 155, 50}, {155, 155, 155}, {25, 25, 25}})
m.Document.Meshes = []gltf.Mesh{{
Name: "Pyramid",
Primitives: []gltf.Primitive{
{
Indices: gltf.Index(indicesAccessor),
Attributes: map[string]uint32{
"POSITION": positionAccessor,
"COLOR_0": colorIndices,
},
},
},
}}
m.Nodes = []gltf.Node{{Name: "Root", Mesh: gltf.Index(0)}}
m.Scenes[0].Nodes = append(m.Scenes[0].Nodes, 0)
if err := gltf.SaveBinary(m.Document, "./example.glb"); err != nil {
panic(err)
}
}
```

### Create a glb using raw data

The following example generates a 3D box with colors per vertex.

Expand Down
82 changes: 41 additions & 41 deletions binary/bench_test.go
@@ -1,41 +1,41 @@
package binary

import (
"bytes"
"encoding/binary"
"testing"

"github.com/qmuntal/gltf"
)

func BenchmarkNative(b *testing.B) {
s := 1000
bs := make([]byte, s*SizeOfElement(gltf.Float, gltf.Vec3))
data := make([][3]float32, s)
b.ResetTimer()
for n := 0; n < b.N; n++ {
for i := range data {
Float.PutVec3(bs[4*i:], data[i])
}
}
}

func BenchmarkWrite(b *testing.B) {
s := 1000
bs := make([]byte, s*SizeOfElement(gltf.Float, gltf.Vec3))
data := make([][3]float32, s)
b.ResetTimer()
for n := 0; n < b.N; n++ {
Write(bs, data)
}
}

func BenchmarkWrite_builtint(b *testing.B) {
s := 1000
bs := bytes.NewBuffer(make([]byte, s*SizeOfElement(gltf.Float, gltf.Vec3)))
data := make([][3]float32, s)
b.ResetTimer()
for n := 0; n < b.N; n++ {
binary.Write(bs, binary.LittleEndian, data)
}
}
package binary

import (
"bytes"
"encoding/binary"
"testing"

"github.com/qmuntal/gltf"
)

func BenchmarkNative(b *testing.B) {
s := 1000
bs := make([]byte, s*SizeOfElement(gltf.Float, gltf.Vec3))
data := make([][3]float32, s)
b.ResetTimer()
for n := 0; n < b.N; n++ {
for i := range data {
Float.PutVec3(bs[4*i:], data[i])
}
}
}

func BenchmarkWrite(b *testing.B) {
s := 1000
bs := make([]byte, s*SizeOfElement(gltf.Float, gltf.Vec3))
data := make([][3]float32, s)
b.ResetTimer()
for n := 0; n < b.N; n++ {
Write(bs, data)
}
}

func BenchmarkWrite_builtint(b *testing.B) {
s := 1000
bs := bytes.NewBuffer(make([]byte, s*SizeOfElement(gltf.Float, gltf.Vec3)))
data := make([][3]float32, s)
b.ResetTimer()
for n := 0; n < b.N; n++ {
binary.Write(bs, binary.LittleEndian, data)
}
}
49 changes: 45 additions & 4 deletions binary/encode.go
Expand Up @@ -3,7 +3,10 @@ package binary
import (
"bytes"
"encoding/binary"
"image/color"
"io"

"github.com/qmuntal/gltf"
)

// Read reads structured binary data from r into data.
Expand All @@ -12,14 +15,35 @@ import (
//
// If data length is greater than the length of b, Read returns io.ErrShortBuffer.
func Read(b []byte, data interface{}) error {
e, n := intDataSize(data)
if e == 0 {
c, t, n := Type(data)
if n <= 0 {
return binary.Read(bytes.NewReader(b), binary.LittleEndian, data)
}
e := SizeOfElement(c, t)
if len(b) < n*e {
return io.ErrShortBuffer
}
switch data := data.(type) {
case []color.RGBA:
for i := range data {
c := UnsignedByte.Vec4(b[e*i:])
data[i] = color.RGBA{R: c[0], G: c[1], B: c[2], A: c[3]}
}
case []color.RGBA64:
for i := range data {
c := UnsignedShort.Vec4(b[e*i:])
data[i] = color.RGBA64{R: c[0], G: c[1], B: c[2], A: c[3]}
}
case []gltf.RGBA:
for i := range data {
c := Float.Vec4(b[e*i:])
data[i] = gltf.RGBA{R: float64(c[0]), G: float64(c[1]), B: float64(c[2]), A: float64(c[3])}
}
case []gltf.RGB:
for i := range data {
c := Float.Vec3(b[e*i:])
data[i] = gltf.RGB{R: float64(c[0]), G: float64(c[1]), B: float64(c[2])}
}
case []int8:
for i, x := range b {
data[i] = int8(x)
Expand Down Expand Up @@ -194,14 +218,31 @@ func Read(b []byte, data interface{}) error {
// Data must be a slice of glTF predefined fixed-size types,
// else it fallbacks to `encoding/binary.Write`.
func Write(b []byte, data interface{}) error {
e, n := intDataSize(data)
if e == 0 {
c, t, n := Type(data)
if n <= 0 {
return binary.Write(bytes.NewBuffer(b), binary.LittleEndian, data)
}
e := SizeOfElement(c, t)
if len(b) < e*n {
return io.ErrShortBuffer
}
switch data := data.(type) {
case []color.RGBA:
for i, x := range data {
UnsignedByte.PutVec4(b[e*i:], [4]uint8{x.R, x.G, x.B, x.A})
}
case []color.RGBA64:
for i, x := range data {
UnsignedShort.PutVec4(b[e*i:], [4]uint16{x.R, x.G, x.B, x.A})
}
case []gltf.RGBA:
for i, x := range data {
Float.PutVec4(b[e*i:], [4]float32{float32(x.R), float32(x.G), float32(x.B), float32(x.A)})
}
case []gltf.RGB:
for i, x := range data {
Float.PutVec3(b[e*i:], [3]float32{float32(x.R), float32(x.G), float32(x.B)})
}
case []int8:
for i, x := range data {
b[i] = byte(x)
Expand Down
35 changes: 23 additions & 12 deletions binary/encode_test.go
Expand Up @@ -2,9 +2,12 @@ package binary

import (
"encoding/binary"
"image/color"
"math"
"reflect"
"testing"

"github.com/qmuntal/gltf"
)

func buildBuffer1(n int, empty ...int) []byte {
Expand Down Expand Up @@ -61,8 +64,8 @@ func TestRead(t *testing.T) {
{"empty", args{make([]byte, 0), []int8{}}, []int8{}, false},
{"int8", args{buildBuffer1(4), make([]int8, 4)}, []int8{1, 2, 3, 4}, false},
{"int8-FE", args{[]byte{0xFE}, make([]int8, 1)}, []int8{-2}, false},
{"[2]int8", args{buildBuffer1(2 * 2), make([][2]int8, 2)}, [][2]int8{{1, 2}, {3, 4}}, false},
{"[3]int8", args{buildBuffer1(2 * 3), make([][3]int8, 2)}, [][3]int8{{1, 2, 3}, {4, 5, 6}}, false},
{"[2]int8", args{[]byte{1, 2, 0, 0, 3, 4, 0, 0}, make([][2]int8, 2)}, [][2]int8{{1, 2}, {3, 4}}, false},
{"[3]int8", args{[]byte{1, 2, 3, 0, 4, 5, 6, 0}, make([][3]int8, 2)}, [][3]int8{{1, 2, 3}, {4, 5, 6}}, false},
{"[4]int8", args{buildBuffer1(2 * 4), make([][4]int8, 2)}, [][4]int8{{1, 2, 3, 4}, {5, 6, 7, 8}}, false},
{"[2][2]int8", args{buildBuffer1(16, 2, 3, 6, 7, 10, 11, 14, 15), make([][2][2]int8, 2)}, [][2][2]int8{
{{1, 5}, {2, 6}},
Expand All @@ -78,8 +81,8 @@ func TestRead(t *testing.T) {
}, false},
{"uint8", args{buildBuffer1(4), make([]uint8, 4)}, []uint8{1, 2, 3, 4}, false},
{"uint8-FE", args{[]byte{0xFE}, make([]uint8, 1)}, []uint8{254}, false},
{"[2]uint8", args{buildBuffer1(2 * 2), make([][2]uint8, 2)}, [][2]uint8{{1, 2}, {3, 4}}, false},
{"[3]uint8", args{buildBuffer1(2 * 3), make([][3]uint8, 2)}, [][3]uint8{{1, 2, 3}, {4, 5, 6}}, false},
{"[2]uint8", args{[]byte{1, 2, 0, 0, 3, 4, 0, 0}, make([][2]uint8, 2)}, [][2]uint8{{1, 2}, {3, 4}}, false},
{"[3]uint8", args{[]byte{1, 2, 3, 0, 4, 5, 6, 0}, make([][3]uint8, 2)}, [][3]uint8{{1, 2, 3}, {4, 5, 6}}, false},
{"[4]uint8", args{buildBuffer1(2 * 4), make([][4]uint8, 2)}, [][4]uint8{{1, 2, 3, 4}, {5, 6, 7, 8}}, false},
{"[2][2]uint8", args{buildBuffer1(16, 2, 3, 6, 7, 10, 11), make([][2][2]uint8, 2)}, [][2][2]uint8{
{{1, 5}, {2, 6}},
Expand All @@ -96,7 +99,7 @@ func TestRead(t *testing.T) {
{"int16", args{buildBuffer2(4), make([]int16, 4)}, []int16{1, 2, 3, 4}, false},
{"int16-FE", args{[]byte{0xFE, 0xFF}, make([]int16, 1)}, []int16{-2}, false},
{"[2]int16", args{buildBuffer2(2 * 2), make([][2]int16, 2)}, [][2]int16{{1, 2}, {3, 4}}, false},
{"[3]int16", args{buildBuffer2(2 * 3), make([][3]int16, 2)}, [][3]int16{{1, 2, 3}, {4, 5, 6}}, false},
{"[3]int16", args{[]byte{1, 0, 2, 0, 3, 0, 0, 0, 4, 0, 5, 0, 6, 0, 0, 0}, make([][3]int16, 2)}, [][3]int16{{1, 2, 3}, {4, 5, 6}}, false},
{"[4]int16", args{buildBuffer2(2 * 4), make([][4]int16, 2)}, [][4]int16{{1, 2, 3, 4}, {5, 6, 7, 8}}, false},
{"[2][2]int16", args{buildBuffer2(2 * 2 * 2), make([][2][2]int16, 2)}, [][2][2]int16{
{{1, 3}, {2, 4}},
Expand All @@ -113,7 +116,7 @@ func TestRead(t *testing.T) {
{"uint16", args{buildBuffer2(4), make([]uint16, 4)}, []uint16{1, 2, 3, 4}, false},
{"uint16-FE", args{[]byte{0xFE, 0xFF}, make([]uint16, 1)}, []uint16{65534}, false},
{"[2]uint16", args{buildBuffer2(2 * 2), make([][2]uint16, 2)}, [][2]uint16{{1, 2}, {3, 4}}, false},
{"[3]uint16", args{buildBuffer2(2 * 3), make([][3]uint16, 2)}, [][3]uint16{{1, 2, 3}, {4, 5, 6}}, false},
{"[3]uint16", args{[]byte{1, 0, 2, 0, 3, 0, 0, 0, 4, 0, 5, 0, 6, 0, 0, 0}, make([][3]uint16, 2)}, [][3]uint16{{1, 2, 3}, {4, 5, 6}}, false},
{"[4]uint16", args{buildBuffer2(2 * 4), make([][4]uint16, 2)}, [][4]uint16{{1, 2, 3, 4}, {5, 6, 7, 8}}, false},
{"[2][2]uint16", args{buildBuffer2(2 * 2 * 2), make([][2][2]uint16, 2)}, [][2][2]uint16{
{{1, 3}, {2, 4}},
Expand Down Expand Up @@ -161,6 +164,10 @@ func TestRead(t *testing.T) {
{{1, 5, 9, 13}, {2, 6, 10, 14}, {3, 7, 11, 15}, {4, 8, 12, 16}},
{{17, 21, 25, 29}, {18, 22, 26, 30}, {19, 23, 27, 31}, {20, 24, 28, 32}},
}, false},
{"color.RGBA", args{buildBuffer1(2 * 4), make([]color.RGBA, 2)}, []color.RGBA{{1, 2, 3, 4}, {5, 6, 7, 8}}, false},
{"color.RGBA64", args{buildBuffer2(2 * 4), make([]color.RGBA64, 2)}, []color.RGBA64{{1, 2, 3, 4}, {5, 6, 7, 8}}, false},
{"gltf.RGBA", args{buildBufferF(2 * 4), make([]gltf.RGBA, 2)}, []gltf.RGBA{{R: 1, G: 2, B: 3, A: 4}, {R: 5, G: 6, B: 7, A: 8}}, false},
{"gltf.RGB", args{buildBufferF(2 * 3), make([]gltf.RGB, 2)}, []gltf.RGB{{R: 1, G: 2, B: 3}, {R: 4, G: 5, B: 6}}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -190,8 +197,8 @@ func TestWrite(t *testing.T) {
{"empty", args{0, []int8{}}, []byte{}, false},
{"int8", args{4, []int8{1, 2, 3, 4}}, buildBuffer1(4), false},
{"int8-FE", args{1, []int8{-2}}, []byte{0xFE}, false},
{"[2]int8", args{4, [][2]int8{{1, 2}, {3, 4}}}, buildBuffer1(2 * 2), false},
{"[3]int8", args{6, [][3]int8{{1, 2, 3}, {4, 5, 6}}}, buildBuffer1(2 * 3), false},
{"[2]int8", args{8, [][2]int8{{1, 2}, {3, 4}}}, []byte{1, 2, 0, 0, 3, 4, 0, 0}, false},
{"[3]int8", args{8, [][3]int8{{1, 2, 3}, {4, 5, 6}}}, []byte{1, 2, 3, 0, 4, 5, 6, 0}, false},
{"[4]int8", args{8, [][4]int8{{1, 2, 3, 4}, {5, 6, 7, 8}}}, buildBuffer1(2 * 4), false},
{"[2][2]int8", args{16, [][2][2]int8{
{{1, 5}, {2, 6}},
Expand All @@ -207,8 +214,8 @@ func TestWrite(t *testing.T) {
}}, buildBuffer1(2 * 4 * 4), false},
{"uint8", args{4, []uint8{1, 2, 3, 4}}, buildBuffer1(4), false},
{"uint8-FE", args{1, []uint8{254}}, []byte{0xFE}, false},
{"[2]uint8", args{4, [][2]uint8{{1, 2}, {3, 4}}}, buildBuffer1(2 * 2), false},
{"[3]uint8", args{6, [][3]uint8{{1, 2, 3}, {4, 5, 6}}}, buildBuffer1(2 * 3), false},
{"[2]uint8", args{8, [][2]uint8{{1, 2}, {3, 4}}}, []byte{1, 2, 0, 0, 3, 4, 0, 0}, false},
{"[3]uint8", args{8, [][3]uint8{{1, 2, 3}, {4, 5, 6}}}, []byte{1, 2, 3, 0, 4, 5, 6, 0}, false},
{"[4]uint8", args{8, [][4]uint8{{1, 2, 3, 4}, {5, 6, 7, 8}}}, buildBuffer1(2 * 4), false},
{"[2][2]uint8", args{16, [][2][2]uint8{
{{1, 5}, {2, 6}},
Expand All @@ -225,7 +232,7 @@ func TestWrite(t *testing.T) {
{"int16", args{8, []int16{1, 2, 3, 4}}, buildBuffer2(4), false},
{"int16-FE", args{2, []int16{-2}}, []byte{0xFE, 0xFF}, false},
{"[2]int16", args{8, [][2]int16{{1, 2}, {3, 4}}}, buildBuffer2(2 * 2), false},
{"[3]int16", args{12, [][3]int16{{1, 2, 3}, {4, 5, 6}}}, buildBuffer2(2 * 3), false},
{"[3]int16", args{16, [][3]int16{{1, 2, 3}, {4, 5, 6}}}, []byte{1, 0, 2, 0, 3, 0, 0, 0, 4, 0, 5, 0, 6, 0, 0, 0}, false},
{"[4]int16", args{16, [][4]int16{{1, 2, 3, 4}, {5, 6, 7, 8}}}, buildBuffer2(2 * 4), false},
{"[2][2]int16", args{16, [][2][2]int16{
{{1, 3}, {2, 4}},
Expand All @@ -242,7 +249,7 @@ func TestWrite(t *testing.T) {
{"uint16", args{8, []uint16{1, 2, 3, 4}}, buildBuffer2(4), false},
{"uint16-FE", args{2, []uint16{65534}}, []byte{0xFE, 0xFF}, false},
{"[2]uint16", args{8, [][2]uint16{{1, 2}, {3, 4}}}, buildBuffer2(2 * 2), false},
{"[3]uint16", args{12, [][3]uint16{{1, 2, 3}, {4, 5, 6}}}, buildBuffer2(2 * 3), false},
{"[3]uint16", args{16, [][3]uint16{{1, 2, 3}, {4, 5, 6}}}, []byte{1, 0, 2, 0, 3, 0, 0, 0, 4, 0, 5, 0, 6, 0, 0, 0}, false},
{"[4]uint16", args{16, [][4]uint16{{1, 2, 3, 4}, {5, 6, 7, 8}}}, buildBuffer2(2 * 4), false},
{"[2][2]uint16", args{16, [][2][2]uint16{
{{1, 3}, {2, 4}},
Expand Down Expand Up @@ -290,6 +297,10 @@ func TestWrite(t *testing.T) {
{{1, 5, 9, 13}, {2, 6, 10, 14}, {3, 7, 11, 15}, {4, 8, 12, 16}},
{{17, 21, 25, 29}, {18, 22, 26, 30}, {19, 23, 27, 31}, {20, 24, 28, 32}},
}}, buildBufferF(32), false},
{"color.RGBA", args{8, []color.RGBA{{1, 2, 3, 4}, {5, 6, 7, 8}}}, buildBuffer1(2 * 4), false},
{"color.RGBA64", args{16, []color.RGBA64{{1, 2, 3, 4}, {5, 6, 7, 8}}}, buildBuffer2(2 * 4), false},
{"gltf.RGBA", args{32, []gltf.RGBA{{R: 1, G: 2, B: 3, A: 4}, {R: 5, G: 6, B: 7, A: 8}}}, buildBufferF(2 * 4), false},
{"gltf.RGB", args{24, []gltf.RGB{{R: 1, G: 2, B: 3}, {R: 4, G: 5, B: 6}}}, buildBufferF(2 * 3), false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down

0 comments on commit d3f64f8

Please sign in to comment.