Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add reader api #28

Merged
merged 4 commits into from
Oct 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func main() {
doc := &gltf.Document{
Accessors: []*gltf.Accessor{
{BufferView: gltf.Index(0), ComponentType: gltf.ComponentUShort, Count: 36, Type: gltf.AccessorScalar},
{BufferView: gltf.Index(1), ComponentType: gltf.ComponentFloat, Count: 24, Max: []float64{0.5, 0.5, 0.5}, Min: []float64{-0.5, -0. -0.5}, Type: gltf.AccessorVec3},
{BufferView: gltf.Index(1), ComponentType: gltf.ComponentFloat, Count: 24, Max: []float32{0.5, 0.5, 0.5}, Min: []float32{-0.5, -0. -0.5}, Type: gltf.AccessorVec3},
{BufferView: gltf.Index(2), ComponentType: gltf.ComponentFloat, Count: 24, Type: gltf.AccessorVec3},
{BufferView: gltf.Index(3), ComponentType: gltf.ComponentFloat, Count: 24, Type: gltf.AccessorVec4},
{BufferView: gltf.Index(4), ComponentType: gltf.ComponentFloat, Count: 24, Type: gltf.AccessorVec2},
Expand All @@ -148,7 +148,7 @@ func main() {
Buffers: []*gltf.Buffer{{ByteLength: 1224, URI: bufferData}},
Materials: []*gltf.Material{{
Name: "Default", AlphaMode: gltf.AlphaOpaque, AlphaCutoff: gltf.Float64(0.5),
PBRMetallicRoughness: &gltf.PBRMetallicRoughness{BaseColorFactor: &gltf.RGBA{R: 0.8, G: 0.8, B: 0.8, A: 1}, MetallicFactor: gltf.Float64(0.1), RoughnessFactor: gltf.Float64(0.99)},
PBRMetallicRoughness: &gltf.PBRMetallicRoughness{BaseColorFactor: &[4]float32{0.8, 0.8, 0.8, 0.5}, MetallicFactor: gltf.Float64(0.1), RoughnessFactor: gltf.Float64(0.99)},
}},
Meshes: []*gltf.Mesh{{Name: "Cube", Primitives: []*gltf.Primitive{{Indices: gltf.Index(0), Material: gltf.Index(0), Mode: gltf.PrimitiveTriangles, Attributes: map[string]uint32{"POSITION": 1, "COLOR_0": 3, "NORMAL": 2, "TEXCOORD_0": 4}}}}},
Nodes: []*gltf.Node{
Expand Down
49 changes: 21 additions & 28 deletions binary/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,25 @@ import (
"encoding/binary"
"image/color"
"io"

"github.com/qmuntal/gltf"
)

// Read reads structured binary data from b into data.
// Data should be a slice of glTF predefined fixed-size types,
// else it fallbacks to `encoding/binary.Read`.
// byteStride can be zero for non-interleaved buffer views.
//
// Data should be a slice of glTF predefined fixed-size types.
// If data length is greater than the length of b, Read returns io.ErrShortBuffer.
func Read(b []byte, data interface{}) error {
func Read(b []byte, byteStride uint32, data interface{}) error {
c, t, n := Type(data)
if n == 0 {
return binary.Read(bytes.NewReader(b), binary.LittleEndian, data)
size := SizeOfElement(c, t)
if byteStride == 0 {
byteStride = size
}
e := int(byteStride)
high := int(n) * e
if byteStride != size {
high -= int(size)
}
e := int(SizeOfElement(c, t))
if len(b) < int(n)*e {
if len(b) < high {
return io.ErrShortBuffer
}
switch data := data.(type) {
Expand All @@ -34,16 +37,6 @@ func Read(b []byte, data interface{}) error {
c := Ushort.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 @@ -73,7 +66,13 @@ func Read(b []byte, data interface{}) error {
data[i] = Byte.Mat4(b[e*i:])
}
case []uint8:
copy(data, b)
if byteStride != 1 {
copy(data, b)
} else {
for i := range data {
data[i] = Ubyte.Scalar(b[e*i:])
}
}
case [][2]uint8:
for i := range data {
data[i] = Ubyte.Vec2(b[e*i:])
Expand Down Expand Up @@ -210,6 +209,8 @@ func Read(b []byte, data interface{}) error {
for i := range data {
data[i] = Uint.Mat4(b[e*i:])
}
default:
panic("unsupported type")
}
return nil
}
Expand Down Expand Up @@ -240,14 +241,6 @@ func Write(b []byte, stride uint32, data interface{}) error {
for i, x := range data {
Ushort.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[e*i] = byte(x)
Expand Down
10 changes: 1 addition & 9 deletions binary/encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import (
"math"
"reflect"
"testing"

"github.com/qmuntal/gltf"
)

func buildBuffer1(n int, empty ...int) []byte {
Expand Down Expand Up @@ -59,7 +57,6 @@ func TestRead(t *testing.T) {
want interface{}
wantErr bool
}{
{"not suported", args{make([]byte, 2), 1}, nil, true},
{"small", args{[]byte{0, 0}, []int8{1, 2, 3}}, []int8{0, 0}, true},
{"empty", args{make([]byte, 0), []int8{}}, []int8{}, false},
{"int8", args{buildBuffer1(4), make([]int8, 4)}, []int8{1, 2, 3, 4}, false},
Expand Down Expand Up @@ -166,12 +163,10 @@ func TestRead(t *testing.T) {
}, 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) {
if err := Read(tt.args.b, tt.args.data); (err != nil) != tt.wantErr {
if err := Read(tt.args.b, 0, tt.args.data); (err != nil) != tt.wantErr {
t.Errorf("Read() error = %v, wantErr %v", err, tt.wantErr)
}
if !tt.wantErr && !reflect.DeepEqual(tt.args.data, tt.want) {
Expand All @@ -192,7 +187,6 @@ func TestWrite(t *testing.T) {
want []byte
wantErr bool
}{
{"not suported", args{2, 1}, nil, true},
{"small", args{2, []int8{1, 2, 3}}, []byte{1, 2, 3}, true},
{"empty", args{0, []int8{}}, []byte{}, false},
{"int8", args{4, []int8{1, 2, 3, 4}}, buildBuffer1(4), false},
Expand Down Expand Up @@ -299,8 +293,6 @@ func TestWrite(t *testing.T) {
}}, 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
4 changes: 2 additions & 2 deletions binary/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ func ExampleRead() {
sizeIndices := uint32(len(indices)) * binary.SizeOfElement(gltf.ComponentUbyte, gltf.AccessorScalar)

// Write
binary.Read(b, indices)
binary.Read(b[sizeIndices:], vertices)
binary.Read(b, 0, indices)
binary.Read(b[sizeIndices:], 0, vertices)

fmt.Println(indices)
fmt.Println(vertices)
Expand Down
135 changes: 86 additions & 49 deletions binary/size.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package binary

import (
"fmt"
"image/color"
"reflect"

Expand Down Expand Up @@ -28,102 +29,138 @@ func SizeOfElement(c gltf.ComponentType, t gltf.AccessorType) uint32 {
return c.ByteSize() * t.Components()
}

// MakeSlice returns the slice type associated with c and t and with the given element count.
// For example, if c is gltf.ComponentFloat and t is gltf.AccessorVec3
// then MakeSlice(c, t, 5) is equivalent to make([][3]float32, 5).
func MakeSlice(c gltf.ComponentType, t gltf.AccessorType, count uint32) interface{} {
var tp reflect.Type
switch c {
case gltf.ComponentUbyte:
tp = reflect.TypeOf((*uint8)(nil))
case gltf.ComponentByte:
tp = reflect.TypeOf((*int8)(nil))
case gltf.ComponentUshort:
tp = reflect.TypeOf((*uint16)(nil))
case gltf.ComponentShort:
tp = reflect.TypeOf((*int16)(nil))
case gltf.ComponentUint:
tp = reflect.TypeOf((*uint32)(nil))
case gltf.ComponentFloat:
tp = reflect.TypeOf((*float32)(nil))
}
tp = tp.Elem()
switch t {
case gltf.AccessorVec2:
tp = reflect.ArrayOf(2, tp)
case gltf.AccessorVec3:
tp = reflect.ArrayOf(3, tp)
case gltf.AccessorVec4:
tp = reflect.ArrayOf(4, tp)
case gltf.AccessorMat2:
tp = reflect.ArrayOf(2, reflect.ArrayOf(2, tp))
case gltf.AccessorMat3:
tp = reflect.ArrayOf(3, reflect.ArrayOf(3, tp))
case gltf.AccessorMat4:
tp = reflect.ArrayOf(4, reflect.ArrayOf(4, tp))
}
return reflect.MakeSlice(reflect.SliceOf(tp), int(count), int(count)).Interface()
}

// Type returns the associated glTF type data.
// If data is an slice, it also returns the length of the slice.
// If data does not have an associated glTF type length will be 0.
func Type(data interface{}) (c gltf.ComponentType, t gltf.AccessorType, length uint32) {
// It panics if data is not an slice.
func Type(data interface{}) (c gltf.ComponentType, t gltf.AccessorType, count uint32) {
v := reflect.ValueOf(data)
switch v.Kind() {
case reflect.Slice:
length = uint32(v.Len())
if v.Kind() != reflect.Slice {
panic(fmt.Sprintf("go3mf: binary.Type expecting a slice but got %s", v.Kind()))
}
count = uint32(v.Len())
switch data.(type) {
case []int8, int8:
case []int8:
c, t = gltf.ComponentByte, gltf.AccessorScalar
case [][2]int8, [2]int8:
case [][2]int8:
c, t = gltf.ComponentByte, gltf.AccessorVec2
case [][3]int8, [3]int8:
case [][3]int8:
c, t = gltf.ComponentByte, gltf.AccessorVec3
case [][4]int8, [4]int8:
case [][4]int8:
c, t = gltf.ComponentByte, gltf.AccessorVec4
case [][2][2]int8, [2][2]int8:
case [][2][2]int8:
c, t = gltf.ComponentByte, gltf.AccessorMat2
case [][3][3]int8, [3][3]int8:
case [][3][3]int8:
c, t = gltf.ComponentByte, gltf.AccessorMat3
case [][4][4]int8, [4][4]int8:
case [][4][4]int8:
c, t = gltf.ComponentByte, gltf.AccessorMat4
case []uint8, uint8:
case []uint8:
c, t = gltf.ComponentUbyte, gltf.AccessorScalar
case [][2]uint8, [2]uint8:
case [][2]uint8:
c, t = gltf.ComponentUbyte, gltf.AccessorVec2
case [][3]uint8, [3]uint8:
case [][3]uint8:
c, t = gltf.ComponentUbyte, gltf.AccessorVec3
case []color.RGBA, color.RGBA, [][4]uint8, [4]uint8:
case []color.RGBA, [][4]uint8:
c, t = gltf.ComponentUbyte, gltf.AccessorVec4
case [][2][2]uint8, [2][2]uint8:
case [][2][2]uint8:
c, t = gltf.ComponentUbyte, gltf.AccessorMat2
case [][3][3]uint8, [3][3]uint8:
case [][3][3]uint8:
c, t = gltf.ComponentUbyte, gltf.AccessorMat3
case [][4][4]uint8, [4][4]uint8:
case [][4][4]uint8:
c, t = gltf.ComponentUbyte, gltf.AccessorMat4
case []int16, int16:
case []int16:
c, t = gltf.ComponentShort, gltf.AccessorScalar
case [][2]int16, [2]int16:
case [][2]int16:
c, t = gltf.ComponentShort, gltf.AccessorVec2
case [][3]int16, [3]int16:
case [][3]int16:
c, t = gltf.ComponentShort, gltf.AccessorVec3
case [][4]int16, [4]int16:
case [][4]int16:
c, t = gltf.ComponentShort, gltf.AccessorVec4
case [][2][2]int16, [2][2]int16:
case [][2][2]int16:
c, t = gltf.ComponentShort, gltf.AccessorMat2
case [][3][3]int16, [3][3]int16:
case [][3][3]int16:
c, t = gltf.ComponentShort, gltf.AccessorMat3
case [][4][4]int16, [4][4]int16:
case [][4][4]int16:
c, t = gltf.ComponentShort, gltf.AccessorMat4
case []uint16, uint16:
case []uint16:
c, t = gltf.ComponentUshort, gltf.AccessorScalar
case [][2]uint16, [2]uint16:
case [][2]uint16:
c, t = gltf.ComponentUshort, gltf.AccessorVec2
case [][3]uint16, [3]uint16:
case [][3]uint16:
c, t = gltf.ComponentUshort, gltf.AccessorVec3
case []color.RGBA64, color.RGBA64, [][4]uint16, [4]uint16:
case []color.RGBA64, [][4]uint16:
c, t = gltf.ComponentUshort, gltf.AccessorVec4
case [][2][2]uint16, [2][2]uint16:
case [][2][2]uint16:
c, t = gltf.ComponentUshort, gltf.AccessorMat2
case [][3][3]uint16, [3][3]uint16:
case [][3][3]uint16:
c, t = gltf.ComponentUshort, gltf.AccessorMat3
case [][4][4]uint16, [4][4]uint16:
case [][4][4]uint16:
c, t = gltf.ComponentUshort, gltf.AccessorMat4
case []uint32, uint32:
case []uint32:
c, t = gltf.ComponentUint, gltf.AccessorScalar
case [][2]uint32, [2]uint32:
case [][2]uint32:
c, t = gltf.ComponentUint, gltf.AccessorVec2
case [][3]uint32, [3]uint32:
case [][3]uint32:
c, t = gltf.ComponentUint, gltf.AccessorVec3
case [][4]uint32, [4]uint32:
case [][4]uint32:
c, t = gltf.ComponentUint, gltf.AccessorVec4
case [][2][2]uint32, [2][2]uint32:
case [][2][2]uint32:
c, t = gltf.ComponentUint, gltf.AccessorMat2
case [][3][3]uint32, [3][3]uint32:
case [][3][3]uint32:
c, t = gltf.ComponentUint, gltf.AccessorMat3
case [][4][4]uint32, [4][4]uint32:
case [][4][4]uint32:
c, t = gltf.ComponentUint, gltf.AccessorMat4
case []float32, float32:
case []float32:
c, t = gltf.ComponentFloat, gltf.AccessorScalar
case [][2]float32, [2]float32:
case [][2]float32:
c, t = gltf.ComponentFloat, gltf.AccessorVec2
case []gltf.RGB, gltf.RGB, [][3]float32, [3]float32:
case [][3]float32:
c, t = gltf.ComponentFloat, gltf.AccessorVec3
case []gltf.RGBA, gltf.RGBA, [][4]float32, [4]float32:
case [][4]float32:
c, t = gltf.ComponentFloat, gltf.AccessorVec4
case [][2][2]float32, [2][2]float32:
case [][2][2]float32:
c, t = gltf.ComponentFloat, gltf.AccessorMat2
case [][3][3]float32, [3][3]float32:
case [][3][3]float32:
c, t = gltf.ComponentFloat, gltf.AccessorMat3
case [][4][4]float32, [4][4]float32:
case [][4][4]float32:
c, t = gltf.ComponentFloat, gltf.AccessorMat4
default:
length = 0
panic(fmt.Sprintf("go3mf: binary.Type expecting a glTF supported type but got %s", v.Kind()))
}
return
}