Repository: qmuntal/gltf
Pull request #19 (2019-11-18 17:47:28)

Binary package
Package binary implements simple translation between numbers and byte sequences.


Patch:
diff --git a/README.md b/README.md
index 6670dc9..9919c6d 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,7 @@ A Go package for simple, efficient, and robust serialization/deserialization of
 * Buffers
   * [x] Parse BASE64 encoded embedded buffer data(DataURI)
   * [x] Load .bin file
+  * [x] Binary package
 * Read from io.Reader
   * [x] Boilerplate for disk loading
   * [x] Custom callback handlers
@@ -62,7 +63,7 @@ func ExampleExension() {
 }
 ```
 
-It is not necessary to call `gltf.RegisterExtension` for built-in extensions, as these auto-register themselves on `init()`.
+To support custom extensions you should use the `gltf.RegisterExtension` method.
 
 ## Perfomance
 
diff --git a/binary/binary.go b/binary/binary.go
new file mode 100644
index 0000000..8be232a
--- /dev/null
+++ b/binary/binary.go
@@ -0,0 +1,363 @@
+// Package binary implements simple translation between numbers and byte sequences.
+//
+// Numbers are translated by reading and writing fixed-size values.
+// A fixed-size value is either a fixed-size arithmetic
+// type (bool, int8, uint8, int16, float32, complex64, ...)
+// or an array or struct containing only fixed-size values.
+//
+// Even though the defined structs don't implement a common interface,
+// they all follow the same `generic` interface, and the only difference is the
+// type of the value:
+//
+// type Component interface {
+//   Scalar([]byte) interface{}
+//   Vec2([]byte) [2]interface{}
+//   Vec3([]byte) [2]interface{}
+//   Vec4([]byte) [2]interface{}
+//   Mat2([]byte) [2][2]interface{}
+//   Mat3([]byte) [3][3]interface{}
+//   Mat4([]byte) [4][4]interface{}
+//   PutScalar([]byte, interface{})
+//   PutVec2([]byte, [2]interface{})
+//   PutVec3([]byte, [3]interface{})
+//   PutVec4([]byte, [4]interface{})
+//   PutMat2([]byte, [2][2]interface{})
+//   PutMat3([]byte, [3][3]interface{})
+//   PutMat4([]byte, [4][4]interface{})
+// }
+//
+// This package favors simplicity and compliance over efficiency,
+// but it is still an order of magnitude more performant than using the built-int `binary` package,
+// as it implements fast paths for basic glTF types and slices.
+package binary
+
+import "encoding/binary"
+
+// Byte is the byte implementation of Component.
+var Byte byteComponent
+
+// UnsignedByte is the usnigned byte implementation of Component.
+var UnsignedByte ubyteComponent
+
+// Short is the byte short of Component.
+var Short shortComponent
+
+// UnsignedShort is the usnigned short implementation of Component.
+var UnsignedShort ushortComponent
+
+// UnsignedInt is the unsigned int implementation of Component.
+var UnsignedInt uintComponent
+
+// Float is the float implementation of Component.
+var Float floatComponent
+
+type ubyteComponent struct{}
+type ushortComponent struct{}
+type uintComponent struct{}
+
+func (ubyteComponent) Scalar(b []byte) uint8 {
+	return b[0]
+}
+
+func (ubyteComponent) PutScalar(b []byte, v uint8) {
+	b[0] = v
+}
+
+func (ubyteComponent) Vec2(b []byte) [2]uint8 {
+	return [2]uint8{b[0], b[1]}
+}
+
+func (ubyteComponent) PutVec2(b []byte, v [2]uint8) {
+	b[0] = v[0]
+	b[1] = v[1]
+}
+
+func (ubyteComponent) Vec3(b []byte) [3]uint8 {
+	return [3]uint8{b[0], b[1], b[2]}
+}
+
+func (ubyteComponent) PutVec3(b []byte, v [3]uint8) {
+	b[0] = v[0]
+	b[1] = v[1]
+	b[2] = v[2]
+}
+
+func (ubyteComponent) Vec4(b []byte) [4]uint8 {
+	return [4]uint8{b[0], b[1], b[2], b[3]}
+}
+
+func (ubyteComponent) PutVec4(b []byte, v [4]uint8) {
+	b[0] = v[0]
+	b[1] = v[1]
+	b[2] = v[2]
+	b[3] = v[3]
+}
+
+func (ubyteComponent) Mat2(b []byte) [2][2]uint8 {
+	return [2][2]uint8{
+		{b[0], b[4]},
+		{b[1], b[5]},
+	}
+}
+
+func (ubyteComponent) PutMat2(b []byte, v [2][2]uint8) {
+	b[0] = v[0][0]
+	b[1] = v[1][0]
+	b[4] = v[0][1]
+	b[5] = v[1][1]
+}
+
+func (ubyteComponent) Mat3(b []byte) [3][3]uint8 {
+	return [3][3]uint8{
+		{b[0], b[4], b[8]},
+		{b[1], b[5], b[9]},
+		{b[2], b[6], b[10]},
+	}
+}
+
+func (ubyteComponent) PutMat3(b []byte, v [3][3]uint8) {
+	b[0] = v[0][0]
+	b[1] = v[1][0]
+	b[2] = v[2][0]
+	b[4] = v[0][1]
+	b[5] = v[1][1]
+	b[6] = v[2][1]
+	b[8] = v[0][2]
+	b[9] = v[1][2]
+	b[10] = v[2][2]
+}
+func (ubyteComponent) Mat4(b []byte) [4][4]uint8 {
+	return [4][4]uint8{
+		{b[0], b[4], b[8], b[12]},
+		{b[1], b[5], b[9], b[13]},
+		{b[2], b[6], b[10], b[14]},
+		{b[3], b[7], b[11], b[15]},
+	}
+}
+
+func (ubyteComponent) PutMat4(b []byte, v [4][4]uint8) {
+	b[0] = v[0][0]
+	b[1] = v[1][0]
+	b[2] = v[2][0]
+	b[3] = v[3][0]
+	b[4] = v[0][1]
+	b[5] = v[1][1]
+	b[6] = v[2][1]
+	b[7] = v[3][1]
+	b[8] = v[0][2]
+	b[9] = v[1][2]
+	b[10] = v[2][2]
+	b[11] = v[3][2]
+	b[12] = v[0][3]
+	b[13] = v[1][3]
+	b[14] = v[2][3]
+	b[15] = v[3][3]
+}
+
+func getUint16(b []byte) uint16 {
+	return binary.LittleEndian.Uint16(b)
+}
+
+func (ushortComponent) Scalar(b []byte) uint16 {
+	return getUint16(b)
+}
+
+func (ushortComponent) PutScalar(b []byte, v uint16) {
+	binary.LittleEndian.PutUint16(b, v)
+}
+
+func (ushortComponent) Vec2(b []byte) [2]uint16 {
+	return [2]uint16{getUint16(b), getUint16(b[2:])}
+}
+
+func (ushortComponent) PutVec2(b []byte, v [2]uint16) {
+	binary.LittleEndian.PutUint16(b, v[0])
+	binary.LittleEndian.PutUint16(b[2:], v[1])
+}
+
+func (ushortComponent) Vec3(b []byte) [3]uint16 {
+	return [3]uint16{getUint16(b), getUint16(b[2:]), getUint16(b[4:])}
+}
+
+func (ushortComponent) PutVec3(b []byte, v [3]uint16) {
+	binary.LittleEndian.PutUint16(b, v[0])
+	binary.LittleEndian.PutUint16(b[2:], v[1])
+	binary.LittleEndian.PutUint16(b[4:], v[2])
+}
+
+func (ushortComponent) Vec4(b []byte) [4]uint16 {
+	return [4]uint16{getUint16(b), getUint16(b[2:]), getUint16(b[4:]), getUint16(b[6:])}
+}
+
+func (ushortComponent) PutVec4(b []byte, v [4]uint16) {
+	binary.LittleEndian.PutUint16(b, v[0])
+	binary.LittleEndian.PutUint16(b[2:], v[1])
+	binary.LittleEndian.PutUint16(b[4:], v[2])
+	binary.LittleEndian.PutUint16(b[6:], v[3])
+}
+
+func (ushortComponent) Mat2(b []byte) [2][2]uint16 {
+	return [2][2]uint16{
+		{getUint16(b), getUint16(b[4:])},
+		{getUint16(b[2:]), getUint16(b[6:])},
+	}
+}
+
+func (ushortComponent) PutMat2(b []byte, v [2][2]uint16) {
+	binary.LittleEndian.PutUint16(b, v[0][0])
+	binary.LittleEndian.PutUint16(b[2:], v[1][0])
+	binary.LittleEndian.PutUint16(b[4:], v[0][1])
+	binary.LittleEndian.PutUint16(b[6:], v[1][1])
+}
+
+func (ushortComponent) Mat3(b []byte) [3][3]uint16 {
+	return [3][3]uint16{
+		{getUint16(b), getUint16(b[8:]), getUint16(b[16:])},
+		{getUint16(b[2:]), getUint16(b[10:]), getUint16(b[18:])},
+		{getUint16(b[4:]), getUint16(b[12:]), getUint16(b[20:])},
+	}
+}
+
+func (ushortComponent) PutMat3(b []byte, v [3][3]uint16) {
+	binary.LittleEndian.PutUint16(b, v[0][0])
+	binary.LittleEndian.PutUint16(b[2:], v[1][0])
+	binary.LittleEndian.PutUint16(b[4:], v[2][0])
+	binary.LittleEndian.PutUint16(b[8:], v[0][1])
+	binary.LittleEndian.PutUint16(b[10:], v[1][1])
+	binary.LittleEndian.PutUint16(b[12:], v[2][1])
+	binary.LittleEndian.PutUint16(b[16:], v[0][2])
+	binary.LittleEndian.PutUint16(b[18:], v[1][2])
+	binary.LittleEndian.PutUint16(b[20:], v[2][2])
+}
+
+func (ushortComponent) Mat4(b []byte) [4][4]uint16 {
+	return [4][4]uint16{
+		{getUint16(b), getUint16(b[8:]), getUint16(b[16:]), getUint16(b[24:])},
+		{getUint16(b[2:]), getUint16(b[10:]), getUint16(b[18:]), getUint16(b[26:])},
+		{getUint16(b[4:]), getUint16(b[12:]), getUint16(b[20:]), getUint16(b[28:])},
+		{getUint16(b[6:]), getUint16(b[14:]), getUint16(b[22:]), getUint16(b[30:])},
+	}
+}
+
+func (ushortComponent) PutMat4(b []byte, v [4][4]uint16) {
+	binary.LittleEndian.PutUint16(b, v[0][0])
+	binary.LittleEndian.PutUint16(b[2:], v[1][0])
+	binary.LittleEndian.PutUint16(b[4:], v[2][0])
+	binary.LittleEndian.PutUint16(b[6:], v[3][0])
+	binary.LittleEndian.PutUint16(b[8:], v[0][1])
+	binary.LittleEndian.PutUint16(b[10:], v[1][1])
+	binary.LittleEndian.PutUint16(b[12:], v[2][1])
+	binary.LittleEndian.PutUint16(b[14:], v[3][1])
+	binary.LittleEndian.PutUint16(b[16:], v[0][2])
+	binary.LittleEndian.PutUint16(b[18:], v[1][2])
+	binary.LittleEndian.PutUint16(b[20:], v[2][2])
+	binary.LittleEndian.PutUint16(b[22:], v[3][2])
+	binary.LittleEndian.PutUint16(b[24:], v[0][3])
+	binary.LittleEndian.PutUint16(b[26:], v[1][3])
+	binary.LittleEndian.PutUint16(b[28:], v[2][3])
+	binary.LittleEndian.PutUint16(b[30:], v[3][3])
+}
+
+func getUint32(b []byte) uint32 {
+	return binary.LittleEndian.Uint32(b)
+}
+
+func (uintComponent) Scalar(b []byte) uint32 {
+	return getUint32(b)
+}
+
+func (uintComponent) PutScalar(b []byte, v uint32) {
+	binary.LittleEndian.PutUint32(b, v)
+}
+
+func (uintComponent) Vec2(b []byte) [2]uint32 {
+	return [2]uint32{getUint32(b), getUint32(b[4:])}
+}
+
+func (uintComponent) PutVec2(b []byte, v [2]uint32) {
+	binary.LittleEndian.PutUint32(b, v[0])
+	binary.LittleEndian.PutUint32(b[4:], v[1])
+}
+
+func (uintComponent) Vec3(b []byte) [3]uint32 {
+	return [3]uint32{getUint32(b), getUint32(b[4:]), getUint32(b[8:])}
+}
+
+func (uintComponent) PutVec3(b []byte, v [3]uint32) {
+	binary.LittleEndian.PutUint32(b, v[0])
+	binary.LittleEndian.PutUint32(b[4:], v[1])
+	binary.LittleEndian.PutUint32(b[8:], v[2])
+}
+
+func (uintComponent) Vec4(b []byte) [4]uint32 {
+	return [4]uint32{getUint32(b), getUint32(b[4:]), getUint32(b[8:]), getUint32(b[12:])}
+}
+
+func (uintComponent) PutVec4(b []byte, v [4]uint32) {
+	binary.LittleEndian.PutUint32(b, v[0])
+	binary.LittleEndian.PutUint32(b[4:], v[1])
+	binary.LittleEndian.PutUint32(b[8:], v[2])
+	binary.LittleEndian.PutUint32(b[12:], v[3])
+}
+
+func (uintComponent) Mat2(b []byte) [2][2]uint32 {
+	return [2][2]uint32{
+		{getUint32(b), getUint32(b[8:])},
+		{getUint32(b[4:]), getUint32(b[12:])},
+	}
+}
+
+func (uintComponent) PutMat2(b []byte, v [2][2]uint32) {
+	binary.LittleEndian.PutUint32(b, v[0][0])
+	binary.LittleEndian.PutUint32(b[4:], v[1][0])
+	binary.LittleEndian.PutUint32(b[8:], v[0][1])
+	binary.LittleEndian.PutUint32(b[12:], v[1][1])
+}
+
+func (uintComponent) Mat3(b []byte) [3][3]uint32 {
+	return [3][3]uint32{
+		{getUint32(b), getUint32(b[12:]), getUint32(b[24:])},
+		{getUint32(b[4:]), getUint32(b[16:]), getUint32(b[28:])},
+		{getUint32(b[8:]), getUint32(b[20:]), getUint32(b[32:])},
+	}
+}
+
+func (uintComponent) PutMat3(b []byte, v [3][3]uint32) {
+	binary.LittleEndian.PutUint32(b, v[0][0])
+	binary.LittleEndian.PutUint32(b[4:], v[1][0])
+	binary.LittleEndian.PutUint32(b[8:], v[2][0])
+	binary.LittleEndian.PutUint32(b[12:], v[0][1])
+	binary.LittleEndian.PutUint32(b[16:], v[1][1])
+	binary.LittleEndian.PutUint32(b[20:], v[2][1])
+	binary.LittleEndian.PutUint32(b[24:], v[0][2])
+	binary.LittleEndian.PutUint32(b[28:], v[1][2])
+	binary.LittleEndian.PutUint32(b[32:], v[2][2])
+}
+
+func (uintComponent) Mat4(b []byte) [4][4]uint32 {
+	return [4][4]uint32{
+		{getUint32(b), getUint32(b[16:]), getUint32(b[32:]), getUint32(b[48:])},
+		{getUint32(b[4:]), getUint32(b[20:]), getUint32(b[36:]), getUint32(b[52:])},
+		{getUint32(b[8:]), getUint32(b[24:]), getUint32(b[40:]), getUint32(b[56:])},
+		{getUint32(b[12:]), getUint32(b[28:]), getUint32(b[44:]), getUint32(b[60:])},
+	}
+}
+
+func (uintComponent) PutMat4(b []byte, v [4][4]uint32) {
+	binary.LittleEndian.PutUint32(b, v[0][0])
+	binary.LittleEndian.PutUint32(b[4:], v[1][0])
+	binary.LittleEndian.PutUint32(b[8:], v[2][0])
+	binary.LittleEndian.PutUint32(b[12:], v[3][0])
+	binary.LittleEndian.PutUint32(b[16:], v[0][1])
+	binary.LittleEndian.PutUint32(b[20:], v[1][1])
+	binary.LittleEndian.PutUint32(b[24:], v[2][1])
+	binary.LittleEndian.PutUint32(b[28:], v[3][1])
+	binary.LittleEndian.PutUint32(b[32:], v[0][2])
+	binary.LittleEndian.PutUint32(b[36:], v[1][2])
+	binary.LittleEndian.PutUint32(b[40:], v[2][2])
+	binary.LittleEndian.PutUint32(b[44:], v[3][2])
+	binary.LittleEndian.PutUint32(b[48:], v[0][3])
+	binary.LittleEndian.PutUint32(b[52:], v[1][3])
+	binary.LittleEndian.PutUint32(b[56:], v[2][3])
+	binary.LittleEndian.PutUint32(b[60:], v[3][3])
+}
diff --git a/binary/encode.go b/binary/encode.go
new file mode 100644
index 0000000..fdc1459
--- /dev/null
+++ b/binary/encode.go
@@ -0,0 +1,373 @@
+package binary
+
+import (
+	"bytes"
+	"encoding/binary"
+	"io"
+)
+
+// Read reads structured binary data from r into data.
+// Data should be a slice of glTF predefined fixed-size types,
+// else it fallbacks to `encoding/binary.Read`.
+//
+// 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 {
+		return binary.Read(bytes.NewReader(b), binary.LittleEndian, data)
+	}
+	if len(b) < n*e {
+		return io.ErrShortBuffer
+	}
+	switch data := data.(type) {
+	case []int8:
+		for i, x := range b {
+			data[i] = int8(x)
+		}
+	case [][2]int8:
+		for i := range data {
+			data[i] = Byte.Vec2(b[e*i:])
+		}
+	case [][3]int8:
+		for i := range data {
+			data[i] = Byte.Vec3(b[e*i:])
+		}
+	case [][4]int8:
+		for i := range data {
+			data[i] = Byte.Vec4(b[e*i:])
+		}
+	case [][2][2]int8:
+		for i := range data {
+			data[i] = Byte.Mat2(b[e*i:])
+		}
+	case [][3][3]int8:
+		for i := range data {
+			data[i] = Byte.Mat3(b[e*i:])
+		}
+	case [][4][4]int8:
+		for i := range data {
+			data[i] = Byte.Mat4(b[e*i:])
+		}
+	case []uint8:
+		copy(data, b)
+	case [][2]uint8:
+		for i := range data {
+			data[i] = UnsignedByte.Vec2(b[e*i:])
+		}
+	case [][3]uint8:
+		for i := range data {
+			data[i] = UnsignedByte.Vec3(b[e*i:])
+		}
+	case [][4]uint8:
+		for i := range data {
+			data[i] = UnsignedByte.Vec4(b[e*i:])
+		}
+	case [][2][2]uint8:
+		for i := range data {
+			data[i] = UnsignedByte.Mat2(b[e*i:])
+		}
+	case [][3][3]uint8:
+		for i := range data {
+			data[i] = UnsignedByte.Mat3(b[e*i:])
+		}
+	case [][4][4]uint8:
+		for i := range data {
+			data[i] = UnsignedByte.Mat4(b[e*i:])
+		}
+	case []int16:
+		for i := range data {
+			data[i] = Short.Scalar(b[e*i:])
+		}
+	case [][2]int16:
+		for i := range data {
+			data[i] = Short.Vec2(b[e*i:])
+		}
+	case [][3]int16:
+		for i := range data {
+			data[i] = Short.Vec3(b[e*i:])
+		}
+	case [][4]int16:
+		for i := range data {
+			data[i] = Short.Vec4(b[e*i:])
+		}
+	case [][2][2]int16:
+		for i := range data {
+			data[i] = Short.Mat2(b[e*i:])
+		}
+	case [][3][3]int16:
+		for i := range data {
+			data[i] = Short.Mat3(b[e*i:])
+		}
+	case [][4][4]int16:
+		for i := range data {
+			data[i] = Short.Mat4(b[e*i:])
+		}
+	case []uint16:
+		for i := range data {
+			data[i] = UnsignedShort.Scalar(b[e*i:])
+		}
+	case [][2]uint16:
+		for i := range data {
+			data[i] = UnsignedShort.Vec2(b[e*i:])
+		}
+	case [][3]uint16:
+		for i := range data {
+			data[i] = UnsignedShort.Vec3(b[e*i:])
+		}
+	case [][4]uint16:
+		for i := range data {
+			data[i] = UnsignedShort.Vec4(b[e*i:])
+		}
+	case [][2][2]uint16:
+		for i := range data {
+			data[i] = UnsignedShort.Mat2(b[e*i:])
+		}
+	case [][3][3]uint16:
+		for i := range data {
+			data[i] = UnsignedShort.Mat3(b[e*i:])
+		}
+	case [][4][4]uint16:
+		for i := range data {
+			data[i] = UnsignedShort.Mat4(b[e*i:])
+		}
+	case []float32:
+		for i := range data {
+			data[i] = Float.Scalar(b[e*i:])
+		}
+	case [][2]float32:
+		for i := range data {
+			data[i] = Float.Vec2(b[e*i:])
+		}
+	case [][3]float32:
+		for i := range data {
+			data[i] = Float.Vec3(b[e*i:])
+		}
+	case [][4]float32:
+		for i := range data {
+			data[i] = Float.Vec4(b[e*i:])
+		}
+	case [][2][2]float32:
+		for i := range data {
+			data[i] = Float.Mat2(b[e*i:])
+		}
+	case [][3][3]float32:
+		for i := range data {
+			data[i] = Float.Mat3(b[e*i:])
+		}
+	case [][4][4]float32:
+		for i := range data {
+			data[i] = Float.Mat4(b[e*i:])
+		}
+	case []uint32:
+		for i := range data {
+			data[i] = UnsignedInt.Scalar(b[e*i:])
+		}
+	case [][2]uint32:
+		for i := range data {
+			data[i] = UnsignedInt.Vec2(b[e*i:])
+		}
+	case [][3]uint32:
+		for i := range data {
+			data[i] = UnsignedInt.Vec3(b[e*i:])
+		}
+	case [][4]uint32:
+		for i := range data {
+			data[i] = UnsignedInt.Vec4(b[e*i:])
+		}
+	case [][2][2]uint32:
+		for i := range data {
+			data[i] = UnsignedInt.Mat2(b[e*i:])
+		}
+	case [][3][3]uint32:
+		for i := range data {
+			data[i] = UnsignedInt.Mat3(b[e*i:])
+		}
+	case [][4][4]uint32:
+		for i := range data {
+			data[i] = UnsignedInt.Mat4(b[e*i:])
+		}
+	}
+	return nil
+}
+
+// Write writes the binary representation of data into b.
+// 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 {
+		return binary.Write(bytes.NewBuffer(b), binary.LittleEndian, data)
+	}
+	if len(b) < e*n {
+		return io.ErrShortBuffer
+	}
+	switch data := data.(type) {
+	case []int8:
+		for i, x := range data {
+			b[i] = byte(x)
+		}
+	case [][2]int8:
+		for i := range data {
+			Byte.PutVec2(b[e*i:], data[i])
+		}
+	case [][3]int8:
+		for i := range data {
+			Byte.PutVec3(b[e*i:], data[i])
+		}
+	case [][4]int8:
+		for i := range data {
+			Byte.PutVec4(b[e*i:], data[i])
+		}
+	case [][2][2]int8:
+		for i := range data {
+			Byte.PutMat2(b[e*i:], data[i])
+		}
+	case [][3][3]int8:
+		for i := range data {
+			Byte.PutMat3(b[e*i:], data[i])
+		}
+	case [][4][4]int8:
+		for i := range data {
+			Byte.PutMat4(b[e*i:], data[i])
+		}
+	case []uint8:
+		copy(b, data)
+	case [][2]uint8:
+		for i := range data {
+			UnsignedByte.PutVec2(b[e*i:], data[i])
+		}
+	case [][3]uint8:
+		for i := range data {
+			UnsignedByte.PutVec3(b[e*i:], data[i])
+		}
+	case [][4]uint8:
+		for i := range data {
+			UnsignedByte.PutVec4(b[e*i:], data[i])
+		}
+	case [][2][2]uint8:
+		for i := range data {
+			UnsignedByte.PutMat2(b[e*i:], data[i])
+		}
+	case [][3][3]uint8:
+		for i := range data {
+			UnsignedByte.PutMat3(b[e*i:], data[i])
+		}
+	case [][4][4]uint8:
+		for i := range data {
+			UnsignedByte.PutMat4(b[e*i:], data[i])
+		}
+	case []int16:
+		for i := range data {
+			Short.PutScalar(b[e*i:], data[i])
+		}
+	case [][2]int16:
+		for i := range data {
+			Short.PutVec2(b[e*i:], data[i])
+		}
+	case [][3]int16:
+		for i := range data {
+			Short.PutVec3(b[e*i:], data[i])
+		}
+	case [][4]int16:
+		for i := range data {
+			Short.PutVec4(b[e*i:], data[i])
+		}
+	case [][2][2]int16:
+		for i := range data {
+			Short.PutMat2(b[e*i:], data[i])
+		}
+	case [][3][3]int16:
+		for i := range data {
+			Short.PutMat3(b[e*i:], data[i])
+		}
+	case [][4][4]int16:
+		for i := range data {
+			Short.PutMat4(b[e*i:], data[i])
+		}
+	case []uint16:
+		for i := range data {
+			UnsignedShort.PutScalar(b[e*i:], data[i])
+		}
+	case [][2]uint16:
+		for i := range data {
+			UnsignedShort.PutVec2(b[e*i:], data[i])
+		}
+	case [][3]uint16:
+		for i := range data {
+			UnsignedShort.PutVec3(b[e*i:], data[i])
+		}
+	case [][4]uint16:
+		for i := range data {
+			UnsignedShort.PutVec4(b[e*i:], data[i])
+		}
+	case [][2][2]uint16:
+		for i := range data {
+			UnsignedShort.PutMat2(b[e*i:], data[i])
+		}
+	case [][3][3]uint16:
+		for i := range data {
+			UnsignedShort.PutMat3(b[e*i:], data[i])
+		}
+	case [][4][4]uint16:
+		for i := range data {
+			UnsignedShort.PutMat4(b[e*i:], data[i])
+		}
+	case []float32:
+		for i := range data {
+			Float.PutScalar(b[e*i:], data[i])
+		}
+	case [][2]float32:
+		for i := range data {
+			Float.PutVec2(b[e*i:], data[i])
+		}
+	case [][3]float32:
+		for i := range data {
+			Float.PutVec3(b[e*i:], data[i])
+		}
+	case [][4]float32:
+		for i := range data {
+			Float.PutVec4(b[e*i:], data[i])
+		}
+	case [][2][2]float32:
+		for i := range data {
+			Float.PutMat2(b[e*i:], data[i])
+		}
+	case [][3][3]float32:
+		for i := range data {
+			Float.PutMat3(b[e*i:], data[i])
+		}
+	case [][4][4]float32:
+		for i := range data {
+			Float.PutMat4(b[e*i:], data[i])
+		}
+	case []uint32:
+		for i := range data {
+			UnsignedInt.PutScalar(b[e*i:], data[i])
+		}
+	case [][2]uint32:
+		for i := range data {
+			UnsignedInt.PutVec2(b[e*i:], data[i])
+		}
+	case [][3]uint32:
+		for i := range data {
+			UnsignedInt.PutVec3(b[e*i:], data[i])
+		}
+	case [][4]uint32:
+		for i := range data {
+			UnsignedInt.PutVec4(b[e*i:], data[i])
+		}
+	case [][2][2]uint32:
+		for i := range data {
+			UnsignedInt.PutMat2(b[e*i:], data[i])
+		}
+	case [][3][3]uint32:
+		for i := range data {
+			UnsignedInt.PutMat3(b[e*i:], data[i])
+		}
+	case [][4][4]uint32:
+		for i := range data {
+			UnsignedInt.PutMat4(b[e*i:], data[i])
+		}
+	}
+	return nil
+}
diff --git a/binary/size.go b/binary/size.go
new file mode 100644
index 0000000..1359eb3
--- /dev/null
+++ b/binary/size.go
@@ -0,0 +1,177 @@
+package binary
+
+import "github.com/qmuntal/gltf"
+
+// SizeOf returns the size, in bytes, of a component type.
+func SizeOf(c gltf.ComponentType) int {
+	return map[gltf.ComponentType]int{
+		gltf.Byte:          1,
+		gltf.UnsignedByte:  1,
+		gltf.Short:         2,
+		gltf.UnsignedShort: 2,
+		gltf.UnsignedInt:   4,
+		gltf.Float:         4,
+	}[c]
+}
+
+// ComponentsOf returns the number of components of an accessor type.
+func ComponentsOf(t gltf.AccessorType) int {
+	return map[gltf.AccessorType]int{
+		gltf.Scalar: 1,
+		gltf.Vec2:   2,
+		gltf.Vec3:   3,
+		gltf.Vec4:   4,
+		gltf.Mat2:   4,
+		gltf.Mat3:   9,
+		gltf.Mat4:   16,
+	}[t]
+}
+
+// SizeOfElement returns the size, in bytes, of an element.
+func SizeOfElement(c gltf.ComponentType, t gltf.AccessorType) int {
+	// special cases
+	switch {
+	case t == gltf.Mat2 && (c == gltf.Byte || c == gltf.UnsignedByte):
+		return 8
+	case t == gltf.Mat3 && (c == gltf.Byte || c == gltf.UnsignedByte):
+		return 12
+	case t == gltf.Mat3 && (c == gltf.Short || c == gltf.UnsignedShort):
+		return 24
+	}
+	return SizeOf(c) * ComponentsOf(t)
+}
+
+// intDataSize returns the size of the data required to represent the data when encoded.
+// It returns zero if the type cannot be implemented by the fast path in Read or Write.
+func intDataSize(data interface{}) (element int, total int) {
+	var length int
+	switch data := data.(type) {
+	case []int8:
+		element = SizeOfElement(gltf.Byte, gltf.Scalar)
+		length = len(data)
+	case [][2]int8:
+		element = SizeOfElement(gltf.Byte, gltf.Vec2)
+		length = len(data)
+	case [][3]int8:
+		element = SizeOfElement(gltf.Byte, gltf.Vec3)
+		length = len(data)
+	case [][4]int8:
+		element = SizeOfElement(gltf.Byte, gltf.Vec4)
+		length = len(data)
+	case [][2][2]int8:
+		element = SizeOfElement(gltf.Byte, gltf.Mat2)
+		length = len(data)
+	case [][3][3]int8:
+		element = SizeOfElement(gltf.Byte, gltf.Mat3)
+		length = len(data)
+	case [][4][4]int8:
+		element = SizeOfElement(gltf.Byte, gltf.Mat4)
+		length = len(data)
+	case []uint8:
+		element = SizeOfElement(gltf.UnsignedByte, gltf.Scalar)
+		length = len(data)
+	case [][2]uint8:
+		element = SizeOfElement(gltf.UnsignedByte, gltf.Vec2)
+		length = len(data)
+	case [][3]uint8:
+		element = SizeOfElement(gltf.UnsignedByte, gltf.Vec3)
+		length = len(data)
+	case [][4]uint8:
+		element = SizeOfElement(gltf.UnsignedByte, gltf.Vec4)
+		length = len(data)
+	case [][2][2]uint8:
+		element = SizeOfElement(gltf.UnsignedByte, gltf.Mat2)
+		length = len(data)
+	case [][3][3]uint8:
+		element = SizeOfElement(gltf.UnsignedByte, gltf.Mat3)
+		length = len(data)
+	case [][4][4]uint8:
+		element = SizeOfElement(gltf.UnsignedByte, gltf.Mat4)
+		length = len(data)
+	case []int16:
+		element = SizeOfElement(gltf.Short, gltf.Scalar)
+		length = len(data)
+	case [][2]int16:
+		element = SizeOfElement(gltf.Short, gltf.Vec2)
+		length = len(data)
+	case [][3]int16:
+		element = SizeOfElement(gltf.Short, gltf.Vec3)
+		length = len(data)
+	case [][4]int16:
+		element = SizeOfElement(gltf.Short, gltf.Vec4)
+		length = len(data)
+	case [][2][2]int16:
+		element = SizeOfElement(gltf.Short, gltf.Mat2)
+		length = len(data)
+	case [][3][3]int16:
+		element = SizeOfElement(gltf.Short, gltf.Mat3)
+		length = len(data)
+	case [][4][4]int16:
+		element = SizeOfElement(gltf.Short, gltf.Mat4)
+		length = len(data)
+	case []uint16:
+		element = SizeOfElement(gltf.UnsignedShort, gltf.Scalar)
+		length = len(data)
+	case [][2]uint16:
+		element = SizeOfElement(gltf.UnsignedShort, gltf.Vec2)
+		length = len(data)
+	case [][3]uint16:
+		element = SizeOfElement(gltf.UnsignedShort, gltf.Vec3)
+		length = len(data)
+	case [][4]uint16:
+		element = SizeOfElement(gltf.UnsignedShort, gltf.Vec4)
+		length = len(data)
+	case [][2][2]uint16:
+		element = SizeOfElement(gltf.UnsignedShort, gltf.Mat2)
+		length = len(data)
+	case [][3][3]uint16:
+		element = SizeOfElement(gltf.UnsignedShort, gltf.Mat3)
+		length = len(data)
+	case [][4][4]uint16:
+		element = SizeOfElement(gltf.UnsignedShort, gltf.Mat4)
+		length = len(data)
+	case []uint32:
+		element = SizeOfElement(gltf.UnsignedInt, gltf.Scalar)
+		length = len(data)
+	case [][2]uint32:
+		element = SizeOfElement(gltf.UnsignedInt, gltf.Vec2)
+		length = len(data)
+	case [][3]uint32:
+		element = SizeOfElement(gltf.UnsignedInt, gltf.Vec3)
+		length = len(data)
+	case [][4]uint32:
+		element = SizeOfElement(gltf.UnsignedInt, gltf.Vec4)
+		length = len(data)
+	case [][2][2]uint32:
+		element = SizeOfElement(gltf.UnsignedInt, gltf.Mat2)
+		length = len(data)
+	case [][3][3]uint32:
+		element = SizeOfElement(gltf.UnsignedInt, gltf.Mat3)
+		length = len(data)
+	case [][4][4]uint32:
+		element = SizeOfElement(gltf.UnsignedInt, gltf.Mat4)
+		length = len(data)
+	case []float32:
+		element = SizeOfElement(gltf.Float, gltf.Scalar)
+		length = len(data)
+	case [][2]float32:
+		element = SizeOfElement(gltf.Float, gltf.Vec2)
+		length = len(data)
+	case [][3]float32:
+		element = SizeOfElement(gltf.Float, gltf.Vec3)
+		length = len(data)
+	case [][4]float32:
+		element = SizeOfElement(gltf.Float, gltf.Vec4)
+		length = len(data)
+	case [][2][2]float32:
+		element = SizeOfElement(gltf.Float, gltf.Mat2)
+		length = len(data)
+	case [][3][3]float32:
+		element = SizeOfElement(gltf.Float, gltf.Mat3)
+		length = len(data)
+	case [][4][4]float32:
+		element = SizeOfElement(gltf.Float, gltf.Mat4)
+		length = len(data)
+	}
+	return element, length
+}
diff --git a/binary/unsafe.go b/binary/unsafe.go
new file mode 100644
index 0000000..9b6d6be
--- /dev/null
+++ b/binary/unsafe.go
@@ -0,0 +1,198 @@
+package binary
+
+import (
+	"unsafe"
+)
+
+type byteComponent struct{}
+type shortComponent struct{}
+type floatComponent struct{}
+
+func (byteComponent) Scalar(b []byte) int8 {
+	v := UnsignedByte.Scalar(b)
+	return *(*int8)(unsafe.Pointer(&v))
+}
+
+func (byteComponent) PutScalar(b []byte, v int8) {
+	UnsignedByte.PutScalar(b, *(*uint8)(unsafe.Pointer(&v)))
+}
+
+func (byteComponent) Vec2(b []byte) [2]int8 {
+	v := UnsignedByte.Vec2(b)
+	return *(*[2]int8)(unsafe.Pointer(&v))
+}
+
+func (byteComponent) PutVec2(b []byte, v [2]int8) {
+	UnsignedByte.PutVec2(b, *(*[2]uint8)(unsafe.Pointer(&v)))
+}
+
+func (byteComponent) Vec3(b []byte) [3]int8 {
+	v := UnsignedByte.Vec3(b)
+	return *(*[3]int8)(unsafe.Pointer(&v))
+}
+
+func (byteComponent) PutVec3(b []byte, v [3]int8) {
+	UnsignedByte.PutVec3(b, *(*[3]uint8)(unsafe.Pointer(&v)))
+}
+
+func (byteComponent) Vec4(b []byte) [4]int8 {
+	v := UnsignedByte.Vec4(b)
+	return *(*[4]int8)(unsafe.Pointer(&v))
+}
+
+func (byteComponent) PutVec4(b []byte, v [4]int8) {
+	UnsignedByte.PutVec4(b, *(*[4]uint8)(unsafe.Pointer(&v)))
+}
+
+func (byteComponent) Mat2(b []byte) [2][2]int8 {
+	v := UnsignedByte.Mat2(b)
+	return *(*[2][2]int8)(unsafe.Pointer(&v))
+}
+
+func (byteComponent) PutMat2(b []byte, v [2][2]int8) {
+	UnsignedByte.PutMat2(b, *(*[2][2]uint8)(unsafe.Pointer(&v)))
+}
+
+func (byteComponent) Mat3(b []byte) [3][3]int8 {
+	v := UnsignedByte.Mat3(b)
+	return *(*[3][3]int8)(unsafe.Pointer(&v))
+}
+
+func (byteComponent) PutMat3(b []byte, v [3][3]int8) {
+	UnsignedByte.PutMat3(b, *(*[3][3]uint8)(unsafe.Pointer(&v)))
+}
+
+func (byteComponent) Mat4(b []byte) [4][4]int8 {
+	v := UnsignedByte.Mat4(b)
+	return *(*[4][4]int8)(unsafe.Pointer(&v))
+}
+
+func (byteComponent) PutMat4(b []byte, v [4][4]int8) {
+	UnsignedByte.PutMat4(b, *(*[4][4]uint8)(unsafe.Pointer(&v)))
+}
+
+func (shortComponent) Scalar(b []byte) int16 {
+	v := UnsignedShort.Scalar(b)
+	return *(*int16)(unsafe.Pointer(&v))
+}
+
+func (shortComponent) PutScalar(b []byte, v int16) {
+	UnsignedShort.PutScalar(b, *(*uint16)(unsafe.Pointer(&v)))
+}
+
+func (shortComponent) Vec2(b []byte) [2]int16 {
+	v := UnsignedShort.Vec2(b)
+	return *(*[2]int16)(unsafe.Pointer(&v))
+}
+
+func (shortComponent) PutVec2(b []byte, v [2]int16) {
+	UnsignedShort.PutVec2(b, *(*[2]uint16)(unsafe.Pointer(&v)))
+}
+
+func (shortComponent) Vec3(b []byte) [3]int16 {
+	v := UnsignedShort.Vec3(b)
+	return *(*[3]int16)(unsafe.Pointer(&v))
+}
+
+func (shortComponent) PutVec3(b []byte, v [3]int16) {
+	UnsignedShort.PutVec3(b, *(*[3]uint16)(unsafe.Pointer(&v)))
+}
+
+func (shortComponent) Vec4(b []byte) [4]int16 {
+	v := UnsignedShort.Vec4(b)
+	return *(*[4]int16)(unsafe.Pointer(&v))
+}
+
+func (shortComponent) PutVec4(b []byte, v [4]int16) {
+	UnsignedShort.PutVec4(b, *(*[4]uint16)(unsafe.Pointer(&v)))
+}
+
+func (shortComponent) Mat2(b []byte) [2][2]int16 {
+	v := UnsignedShort.Mat2(b)
+	return *(*[2][2]int16)(unsafe.Pointer(&v))
+}
+
+func (shortComponent) PutMat2(b []byte, v [2][2]int16) {
+	UnsignedShort.PutMat2(b, *(*[2][2]uint16)(unsafe.Pointer(&v)))
+}
+
+func (shortComponent) Mat3(b []byte) [3][3]int16 {
+	v := UnsignedShort.Mat3(b)
+	return *(*[3][3]int16)(unsafe.Pointer(&v))
+}
+
+func (shortComponent) PutMat3(b []byte, v [3][3]int16) {
+	UnsignedShort.PutMat3(b, *(*[3][3]uint16)(unsafe.Pointer(&v)))
+}
+
+func (shortComponent) Mat4(b []byte) [4][4]int16 {
+	v := UnsignedShort.Mat4(b)
+	return *(*[4][4]int16)(unsafe.Pointer(&v))
+}
+
+func (shortComponent) PutMat4(b []byte, v [4][4]int16) {
+	UnsignedShort.PutMat4(b, *(*[4][4]uint16)(unsafe.Pointer(&v)))
+}
+
+func (floatComponent) Scalar(b []byte) float32 {
+	v := UnsignedInt.Scalar(b)
+	return *(*float32)(unsafe.Pointer(&v))
+}
+
+func (floatComponent) PutScalar(b []byte, v float32) {
+	UnsignedInt.PutScalar(b, *(*uint32)(unsafe.Pointer(&v)))
+}
+
+func (floatComponent) Vec2(b []byte) [2]float32 {
+	v := UnsignedInt.Vec2(b)
+	return *(*[2]float32)(unsafe.Pointer(&v))
+}
+
+func (floatComponent) PutVec2(b []byte, v [2]float32) {
+	UnsignedInt.PutVec2(b, *(*[2]uint32)(unsafe.Pointer(&v)))
+}
+
+func (floatComponent) Vec3(b []byte) [3]float32 {
+	v := UnsignedInt.Vec3(b)
+	return *(*[3]float32)(unsafe.Pointer(&v))
+}
+
+func (floatComponent) PutVec3(b []byte, v [3]float32) {
+	UnsignedInt.PutVec3(b, *(*[3]uint32)(unsafe.Pointer(&v)))
+}
+
+func (floatComponent) Vec4(b []byte) [4]float32 {
+	v := UnsignedInt.Vec4(b)
+	return *(*[4]float32)(unsafe.Pointer(&v))
+}
+
+func (floatComponent) PutVec4(b []byte, v [4]float32) {
+	UnsignedInt.PutVec4(b, *(*[4]uint32)(unsafe.Pointer(&v)))
+}
+
+func (floatComponent) Mat2(b []byte) [2][2]float32 {
+	v := UnsignedInt.Mat2(b)
+	return *(*[2][2]float32)(unsafe.Pointer(&v))
+}
+
+func (floatComponent) PutMat2(b []byte, v [2][2]float32) {
+	UnsignedInt.PutMat2(b, *(*[2][2]uint32)(unsafe.Pointer(&v)))
+}
+
+func (floatComponent) Mat3(b []byte) [3][3]float32 {
+	v := UnsignedInt.Mat3(b)
+	return *(*[3][3]float32)(unsafe.Pointer(&v))
+}
+
+func (floatComponent) PutMat3(b []byte, v [3][3]float32) {
+	UnsignedInt.PutMat3(b, *(*[3][3]uint32)(unsafe.Pointer(&v)))
+}
+
+func (floatComponent) Mat4(b []byte) [4][4]float32 {
+	v := UnsignedInt.Mat4(b)
+	return *(*[4][4]float32)(unsafe.Pointer(&v))
+}
+
+func (floatComponent) PutMat4(b []byte, v [4][4]float32) {
+	UnsignedInt.PutMat4(b, *(*[4][4]uint32)(unsafe.Pointer(&v)))
+}
diff --git a/struct.go b/struct.go
index 13b9ddb..4c742c0 100644
--- a/struct.go
+++ b/struct.go
@@ -3,6 +3,8 @@ package gltf
 import (
 	"encoding/base64"
 	"fmt"
+	"image/color"
+	"math"
 	"strings"
 )
 
@@ -320,6 +322,12 @@ func NewRGBA() *RGBA {
 	return &RGBA{1, 1, 1, 1}
 }
 
+// NewRGBAColor transform a RGB uint8 color (from 0 to 255) to its float represtation (from 0 to 1).
+func NewRGBAColor(c color.RGBA) *RGBA {
+	linear := NewRGBColor(c)
+	return &RGBA{R: linear.R, G: linear.G, B: linear.B, A: float64(c.A) / 255}
+}
+
 // The RGB components of a color.
 // Each element must be greater than or equal to 0 and less than or equal to 1.
 type RGB struct {
@@ -331,6 +339,20 @@ func NewRGB() *RGB {
 	return &RGB{1, 1, 1}
 }
 
+// NewRGBColor transform a RGB uint8 color (from 0 to 255) to its float represtation (from 0 to 1).
+func NewRGBColor(c color.RGBA) *RGB {
+	sRGB := [3]float64{float64(c.R) / 255, float64(c.G) / 255, float64(c.B) / 255}
+	var linear [3]float64
+	for i := 0; i < 3; i++ {
+		if sRGB[i] <= 0.04045 {
+			linear[i] = sRGB[i] / 12.92
+		} else {
+			linear[i] = math.Pow(((sRGB[i] + 0.055) / 1.055), 2.4)
+		}
+	}
+	return &RGB{R: linear[0], G: linear[1], B: linear[2]}
+}
+
 // PBRMetallicRoughness defines a set of parameter values that are used to define the metallic-roughness material model from Physically-Based Rendering (PBR) methodology.
 type PBRMetallicRoughness struct {
 	Extensions               Extensions   `json:"extensions,omitempty"`


Test patch:
diff --git a/binary/bench_test.go b/binary/bench_test.go
new file mode 100644
index 0000000..1d3a1b4
--- /dev/null
+++ b/binary/bench_test.go
@@ -0,0 +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)
+	}
+}
diff --git a/binary/encode_test.go b/binary/encode_test.go
new file mode 100644
index 0000000..c86599b
--- /dev/null
+++ b/binary/encode_test.go
@@ -0,0 +1,341 @@
+package binary
+
+import (
+	"encoding/binary"
+	"math"
+	"reflect"
+	"testing"
+)
+
+func buildBuffer1(n int, empty ...int) []byte {
+	b := make([]byte, n)
+	for i := 0; i < n; i++ {
+		b[i] = byte(i + 1)
+	}
+	for _, e := range empty {
+		b[e] = 0
+	}
+	return b
+}
+
+func buildBuffer2(n int, empty ...int) []byte {
+	b := make([]byte, 2*n)
+	for i := 0; i < n; i++ {
+		binary.LittleEndian.PutUint16(b[i*2:], uint16(i+1))
+	}
+	for _, e := range empty {
+		b[e] = 0
+	}
+	return b
+}
+
+func buildBuffer3(n int) []byte {
+	b := make([]byte, 4*n)
+	for i := 0; i < n; i++ {
+		binary.LittleEndian.PutUint32(b[i*4:], uint32(i+1))
+	}
+	return b
+}
+
+func buildBufferF(n int) []byte {
+	b := make([]byte, 4*n)
+	for i := 0; i < n; i++ {
+		binary.LittleEndian.PutUint32(b[i*4:], math.Float32bits(float32(i+1)))
+	}
+	return b
+}
+
+func TestRead(t *testing.T) {
+	type args struct {
+		b    []byte
+		data interface{}
+	}
+	tests := []struct {
+		name    string
+		args    args
+		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},
+		{"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},
+		{"[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}},
+			{{9, 13}, {10, 14}},
+		}, false},
+		{"[3][3]int8", args{buildBuffer1(24, 3, 7, 11, 15, 19, 23), make([][3][3]int8, 2)}, [][3][3]int8{
+			{{1, 5, 9}, {2, 6, 10}, {3, 7, 11}},
+			{{13, 17, 21}, {14, 18, 22}, {15, 19, 23}},
+		}, false},
+		{"[4][4]int8", args{buildBuffer1(2 * 4 * 4), make([][4][4]int8, 2)}, [][4][4]int8{
+			{{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},
+		{"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},
+		{"[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}},
+			{{9, 13}, {10, 14}},
+		}, false},
+		{"[3][3]uint8", args{buildBuffer1(32, 3, 7, 11, 15, 19, 23), make([][3][3]uint8, 2)}, [][3][3]uint8{
+			{{1, 5, 9}, {2, 6, 10}, {3, 7, 11}},
+			{{13, 17, 21}, {14, 18, 22}, {15, 19, 23}},
+		}, false},
+		{"[4][4]uint8", args{buildBuffer1(32), make([][4][4]uint8, 2)}, [][4][4]uint8{
+			{{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},
+		{"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},
+		{"[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}},
+			{{5, 7}, {6, 8}},
+		}, false},
+		{"[3][3]int16", args{buildBuffer2(32, 6, 7, 14, 15, 22, 23, 30, 31, 14, 15, 22, 23, 38, 39), make([][3][3]int16, 2)}, [][3][3]int16{
+			{{1, 5, 9}, {2, 6, 10}, {3, 7, 11}},
+			{{13, 17, 21}, {14, 18, 22}, {15, 19, 23}},
+		}, false},
+		{"[4][4]int16", args{buildBuffer2(2 * 4 * 4), make([][4][4]int16, 2)}, [][4][4]int16{
+			{{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},
+		{"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},
+		{"[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}},
+			{{5, 7}, {6, 8}},
+		}, false},
+		{"[3][3]uint16", args{buildBuffer2(32, 6, 7, 14, 15, 22, 23, 30, 31, 14, 15, 22, 23, 38, 39), make([][3][3]uint16, 2)}, [][3][3]uint16{
+			{{1, 5, 9}, {2, 6, 10}, {3, 7, 11}},
+			{{13, 17, 21}, {14, 18, 22}, {15, 19, 23}},
+		}, false},
+		{"[4][4]uint16", args{buildBuffer2(2 * 4 * 4), make([][4][4]uint16, 2)}, [][4][4]uint16{
+			{{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},
+		{"uint32", args{buildBuffer3(4), make([]uint32, 4)}, []uint32{1, 2, 3, 4}, false},
+		{"uint32-FE", args{[]byte{0xFE, 0xFF, 0xFF, 0xFF}, make([]uint32, 1)}, []uint32{4294967294}, false},
+		{"[2]uint32", args{buildBuffer3(2 * 2 * 4), make([][2]uint32, 2)}, [][2]uint32{{1, 2}, {3, 4}}, false},
+		{"[3]uint32", args{buildBuffer3(2 * 3 * 4), make([][3]uint32, 2)}, [][3]uint32{{1, 2, 3}, {4, 5, 6}}, false},
+		{"[4]uint32", args{buildBuffer3(2 * 4 * 4), make([][4]uint32, 2)}, [][4]uint32{{1, 2, 3, 4}, {5, 6, 7, 8}}, false},
+		{"[2][2]uint32", args{buildBuffer3(2 * 2 * 2 * 4), make([][2][2]uint32, 2)}, [][2][2]uint32{
+			{{1, 3}, {2, 4}},
+			{{5, 7}, {6, 8}},
+		}, false},
+		{"[3][3]uint32", args{buildBuffer3(64 * 2), make([][3][3]uint32, 2)}, [][3][3]uint32{
+			{{1, 4, 7}, {2, 5, 8}, {3, 6, 9}},
+			{{10, 13, 16}, {11, 14, 17}, {12, 15, 18}},
+		}, false},
+		{"[4][4]uint32", args{buildBuffer3(2 * 4 * 4 * 4), make([][4][4]uint32, 2)}, [][4][4]uint32{
+			{{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},
+		{"float32", args{buildBufferF(4), make([]float32, 4)}, []float32{1, 2, 3, 4}, false},
+		{"float32-FE", args{[]byte{0x00, 0x00, 0x00, 0xC0}, make([]float32, 1)}, []float32{-2}, false},
+		{"[2]float32", args{buildBufferF(2 * 2 * 4), make([][2]float32, 2)}, [][2]float32{{1, 2}, {3, 4}}, false},
+		{"[3]float32", args{buildBufferF(2 * 3 * 4), make([][3]float32, 2)}, [][3]float32{{1, 2, 3}, {4, 5, 6}}, false},
+		{"[4]float32", args{buildBufferF(2 * 4 * 4), make([][4]float32, 2)}, [][4]float32{{1, 2, 3, 4}, {5, 6, 7, 8}}, false},
+		{"[2][2]float32", args{buildBufferF(2 * 2 * 2 * 4), make([][2][2]float32, 2)}, [][2][2]float32{
+			{{1, 3}, {2, 4}},
+			{{5, 7}, {6, 8}},
+		}, false},
+		{"[3][3]float32", args{buildBufferF(64 * 2), make([][3][3]float32, 2)}, [][3][3]float32{
+			{{1, 4, 7}, {2, 5, 8}, {3, 6, 9}},
+			{{10, 13, 16}, {11, 14, 17}, {12, 15, 18}},
+		}, false},
+		{"[4][4]float32", args{buildBufferF(2 * 4 * 4 * 4), make([][4][4]float32, 2)}, [][4][4]float32{
+			{{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},
+	}
+	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 {
+				t.Errorf("Read() error = %v, wantErr %v", err, tt.wantErr)
+			}
+			if !tt.wantErr && !reflect.DeepEqual(tt.args.data, tt.want) {
+				t.Errorf("Read() error = %v, want %v", tt.args.data, tt.want)
+			}
+		})
+	}
+}
+
+func TestWrite(t *testing.T) {
+	type args struct {
+		n    int
+		data interface{}
+	}
+	tests := []struct {
+		name    string
+		args    args
+		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},
+		{"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},
+		{"[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}},
+			{{9, 13}, {10, 14}},
+		}}, buildBuffer1(16, 2, 3, 6, 7, 10, 11, 14, 15), false},
+		{"[3][3]int8", args{24, [][3][3]int8{
+			{{1, 5, 9}, {2, 6, 10}, {3, 7, 11}},
+			{{13, 17, 21}, {14, 18, 22}, {15, 19, 23}},
+		}}, buildBuffer1(24, 3, 7, 11, 15, 19, 23), false},
+		{"[4][4]int8", args{32, [][4][4]int8{
+			{{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}},
+		}}, 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},
+		{"[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}},
+			{{9, 13}, {10, 14}},
+		}}, buildBuffer1(16, 2, 3, 6, 7, 10, 11, 14, 15), false},
+		{"[3][3]uint8", args{24, [][3][3]uint8{
+			{{1, 5, 9}, {2, 6, 10}, {3, 7, 11}},
+			{{13, 17, 21}, {14, 18, 22}, {15, 19, 23}},
+		}}, buildBuffer1(24, 3, 7, 11, 15, 19, 23), false},
+		{"[4][4]uint8", args{32, [][4][4]uint8{
+			{{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}},
+		}}, buildBuffer1(2 * 4 * 4), false},
+		{"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},
+		{"[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}},
+			{{5, 7}, {6, 8}},
+		}}, buildBuffer2(2 * 2 * 2), false},
+		{"[3][3]int16", args{48, [][3][3]int16{
+			{{1, 5, 9}, {2, 6, 10}, {3, 7, 11}},
+			{{13, 17, 21}, {14, 18, 22}, {15, 19, 23}},
+		}}, buildBuffer2(24, 6, 7, 14, 15, 22, 23, 30, 31, 14, 15, 22, 23, 38, 39, 46, 47), false},
+		{"[4][4]int16", args{64, [][4][4]int16{
+			{{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}},
+		}}, buildBuffer2(2 * 4 * 4), false},
+		{"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},
+		{"[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}},
+			{{5, 7}, {6, 8}},
+		}}, buildBuffer2(2 * 2 * 2), false},
+		{"[3][3]uint16", args{48, [][3][3]uint16{
+			{{1, 5, 9}, {2, 6, 10}, {3, 7, 11}},
+			{{13, 17, 21}, {14, 18, 22}, {15, 19, 23}},
+		}}, buildBuffer2(24, 6, 7, 14, 15, 22, 23, 30, 31, 14, 15, 22, 23, 38, 39, 46, 47), false},
+		{"[4][4]uint16", args{64, [][4][4]uint16{
+			{{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}},
+		}}, buildBuffer2(2 * 4 * 4), false},
+		{"uint32", args{16, []uint32{1, 2, 3, 4}}, buildBuffer3(4), false},
+		{"uint32-FE", args{4, []uint32{4294967294}}, []byte{0xFE, 0xFF, 0xFF, 0xFF}, false},
+		{"[2]uint32", args{16, [][2]uint32{{1, 2}, {3, 4}}}, buildBuffer3(4), false},
+		{"[3]uint32", args{24, [][3]uint32{{1, 2, 3}, {4, 5, 6}}}, buildBuffer3(6), false},
+		{"[4]uint32", args{32, [][4]uint32{{1, 2, 3, 4}, {5, 6, 7, 8}}}, buildBuffer3(8), false},
+		{"[2][2]uint32", args{32, [][2][2]uint32{
+			{{1, 3}, {2, 4}},
+			{{5, 7}, {6, 8}},
+		}}, buildBuffer3(8), false},
+		{"[3][3]uint32", args{72, [][3][3]uint32{
+			{{1, 4, 7}, {2, 5, 8}, {3, 6, 9}},
+			{{10, 13, 16}, {11, 14, 17}, {12, 15, 18}},
+		}}, buildBuffer3(18), false},
+		{"[4][4]uint32", args{128, [][4][4]uint32{
+			{{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}},
+		}}, buildBuffer3(32), false},
+		{"float32", args{16, []float32{1, 2, 3, 4}}, buildBufferF(4), false},
+		{"float32-FE", args{4, []float32{-2}}, []byte{0x00, 0x00, 0x00, 0xC0}, false},
+		{"[2]float32", args{16, [][2]float32{{1, 2}, {3, 4}}}, buildBufferF(4), false},
+		{"[3]float32", args{24, [][3]float32{{1, 2, 3}, {4, 5, 6}}}, buildBufferF(6), false},
+		{"[4]float32", args{32, [][4]float32{{1, 2, 3, 4}, {5, 6, 7, 8}}}, buildBufferF(8), false},
+		{"[2][2]float32", args{32, [][2][2]float32{
+			{{1, 3}, {2, 4}},
+			{{5, 7}, {6, 8}},
+		}}, buildBufferF(8), false},
+		{"[3][3]float32", args{72, [][3][3]float32{
+			{{1, 4, 7}, {2, 5, 8}, {3, 6, 9}},
+			{{10, 13, 16}, {11, 14, 17}, {12, 15, 18}},
+		}}, buildBufferF(18), false},
+		{"[4][4]float32", args{128, [][4][4]float32{
+			{{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},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			b := make([]byte, tt.args.n)
+			if err := Write(b, tt.args.data); (err != nil) != tt.wantErr {
+				t.Errorf("Write() error = %v, wantErr %v", err, tt.wantErr)
+			}
+			if !tt.wantErr && !reflect.DeepEqual(b, tt.want) {
+				t.Errorf("Write() error = %v, want %v", b, tt.want)
+			}
+		})
+	}
+}
+
+func Test_ubyteComponent_Scalar(t *testing.T) {
+	tests := []struct {
+		name string
+		want uint8
+	}{
+		{"base", 1},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			b := make([]byte, 1)
+			UnsignedByte.PutScalar(b, tt.want)
+			if got := UnsignedByte.Scalar(b); got != tt.want {
+				t.Errorf("ubyteComponent.Scalar() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_byteComponent_Scalar(t *testing.T) {
+	tests := []struct {
+		name string
+		want int8
+	}{
+		{"base", 1},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			b := make([]byte, 1)
+			Byte.PutScalar(b, tt.want)
+			if got := Byte.Scalar(b); got != tt.want {
+				t.Errorf("byteComponent.Scalar() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/binary/example_test.go b/binary/example_test.go
new file mode 100644
index 0000000..f2110b2
--- /dev/null
+++ b/binary/example_test.go
@@ -0,0 +1,46 @@
+package binary_test
+
+import (
+	"fmt"
+	"github.com/qmuntal/gltf"
+	"github.com/qmuntal/gltf/binary"
+)
+
+func ExampleWrite() {
+	// Define data
+	indices := []uint8{0, 1, 2, 3, 1, 0, 0, 2, 3, 1, 4, 2, 4, 3, 2, 4, 1, 3}
+	vertices := [][3]float32{{43, 43, 0}, {83, 43, 0}, {63, 63, 40}, {43, 83, 0}, {83, 83, 0}}
+
+	// Allocate buffer
+	sizeIndices := len(indices) * binary.SizeOfElement(gltf.UnsignedByte, gltf.Scalar)
+	sizeVertices := len(vertices) * binary.SizeOfElement(gltf.Float, gltf.Vec3)
+	b := make([]byte, sizeIndices+sizeVertices)
+
+	// Write
+	binary.Write(b, indices)
+	binary.Write(b[sizeIndices:], vertices)
+
+	fmt.Print(b)
+	// Output:
+	// [0 1 2 3 1 0 0 2 3 1 4 2 4 3 2 4 1 3 0 0 44 66 0 0 44 66 0 0 0 0 0 0 166 66 0 0 44 66 0 0 0 0 0 0 124 66 0 0 124 66 0 0 32 66 0 0 44 66 0 0 166 66 0 0 0 0 0 0 166 66 0 0 166 66 0 0 0 0]
+}
+
+func ExampleRead() {
+	// Allocate data
+	indices := make([]uint8, 18)
+	vertices := make([][3]float32, 5)
+
+	// Define buffer
+	b := []byte{0, 1, 2, 3, 1, 0, 0, 2, 3, 1, 4, 2, 4, 3, 2, 4, 1, 3, 0, 0, 44, 66, 0, 0, 44, 66, 0, 0, 0, 0, 0, 0, 166, 66, 0, 0, 44, 66, 0, 0, 0, 0, 0, 0, 124, 66, 0, 0, 124, 66, 0, 0, 32, 66, 0, 0, 44, 66, 0, 0, 166, 66, 0, 0, 0, 0, 0, 0, 166, 66, 0, 0, 166, 66, 0, 0, 0, 0}
+	sizeIndices := len(indices) * binary.SizeOfElement(gltf.UnsignedByte, gltf.Scalar)
+
+	// Write
+	binary.Read(b, indices)
+	binary.Read(b[sizeIndices:], vertices)
+
+	fmt.Println(indices)
+	fmt.Println(vertices)
+	// Output:
+	// [0 1 2 3 1 0 0 2 3 1 4 2 4 3 2 4 1 3]
+	// [[43 43 0] [83 43 0] [63 63 40] [43 83 0] [83 83 0]]
+}
diff --git a/struct_test.go b/struct_test.go
index d61cbda..568fe2b 100644
--- a/struct_test.go
+++ b/struct_test.go
@@ -1,8 +1,11 @@
 package gltf
 
 import (
+	"image/color"
 	"reflect"
 	"testing"
+
+	"github.com/go-test/deep"
 )
 
 func TestBuffer_IsEmbeddedResource(t *testing.T) {
@@ -269,3 +272,69 @@ func TestPBRMetallicRoughness_RoughnessFactorOrDefault(t *testing.T) {
 		})
 	}
 }
+
+func TestNewRGBColor(t *testing.T) {
+	deep.FloatPrecision = 6
+	type args struct {
+		c color.RGBA
+	}
+	tests := []struct {
+		name string
+		args args
+		want *RGB
+	}{
+		{"empty", args{color.RGBA{}}, &RGB{}},
+		{"base", args{color.RGBA{R: 1, G: 1, B: 1}}, &RGB{R: 0.0003035, G: 0.0003035, B: 0.0003035}},
+		{"max", args{color.RGBA{R: 255, G: 255, B: 255}}, &RGB{R: 1, G: 1, B: 1}},
+		{"other", args{color.RGBA{R: 60, G: 120, B: 180}}, &RGB{R: 0.045186, G: 0.1878207, B: 0.4564110}},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := NewRGBColor(tt.args.c)
+			if diff := deep.Equal(got, tt.want); diff != nil {
+				t.Errorf("NewRGBColor() = %v", diff)
+			}
+		})
+	}
+}
+
+func TestNewRGB(t *testing.T) {
+	tests := []struct {
+		name string
+		want *RGB
+	}{
+		{"base", &RGB{1, 1, 1}},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := NewRGB(); !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("NewRGB() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestNewRGBAColor(t *testing.T) {
+	deep.FloatPrecision = 6
+	type args struct {
+		c color.RGBA
+	}
+	tests := []struct {
+		name string
+		args args
+		want *RGBA
+	}{
+		{"empty", args{color.RGBA{}}, &RGBA{}},
+		{"base", args{color.RGBA{R: 1, G: 1, B: 1, A: 1}}, &RGBA{R: 0.0003035, G: 0.0003035, B: 0.0003035, A: 0.00392156}},
+		{"max", args{color.RGBA{R: 255, G: 255, B: 255, A: 255}}, &RGBA{R: 1, G: 1, B: 1, A: 1}},
+		{"other", args{color.RGBA{R: 60, G: 120, B: 180, A: 220}}, &RGBA{R: 0.045186, G: 0.1878207, B: 0.4564110, A: 0.86274509}},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := NewRGBAColor(tt.args.c)
+			if diff := deep.Equal(got, tt.want); diff != nil {
+				t.Errorf("NewRGBAColor() = %v", diff)
+			}
+		})
+	}
+}