Repository: qmuntal/gltf
Pull request #8 (2019-03-26 06:20:22)

Improve examples and reduce code complexity


Patch:
diff --git a/README.md b/README.md
index 50b1bb8..7419e11 100644
--- a/README.md
+++ b/README.md
@@ -49,20 +49,8 @@ if err != nil {
 }
 fmt.Print(doc.Asset)
 ```
-### Write
-```go
-doc := &gltf.Document{
-  Scene: 0, 
-  Asset: gltf.Asset{Generator: "qmuntal/gltf"}, 
-  Scenes: []gltf.Scene{{Extras: 8.0, Extensions: gltf.Extensions{"a": "b"}, Name: "s_1"}}
-}
- 
-if err := gltf.Save(doc, "./a.gltf", true); err != nil {
-  panic(err)
-}
-```
 
-### Write complex
+### Save
 ```go
 doc := &gltf.Document{
   Accessors: []gltf.Accessor{
@@ -80,10 +68,11 @@ doc := &gltf.Document{
     {Buffer: 0, ByteLength: 384, ByteOffset: 648, Target: gltf.ArrayBuffer},
     {Buffer: 0, ByteLength: 192, ByteOffset: 1032, Target: gltf.ArrayBuffer},
   },
-  Buffers: []gltf.Buffer{{ByteLength: 1224, Data: readFile("testdata/BoxVertexColors/glTF-Binary/BoxVertexColors.glb")[1628+20+8:]}},
-  Materials: []gltf.Material{{Name: "Default", AlphaMode: gltf.Opaque, AlphaCutoff: gltf.Float64(0.5), 
-    PBRMetallicRoughness: &gltf.PBRMetallicRoughness{BaseColorFactor: [4]float64{0.8, 0.8, 0.8, 1}, MetallicFactor: gltf.Float64(0.1), RoughnessFactor: gltf.Float64(0.99)}}
-  },
+  Buffers: []gltf.Buffer{{ByteLength: 1224, Data: []uint8{97, 110, 121, 32, 99, 97, 114, 110, 97, 108, 32, 112, 108, 101, 97, 115}}},
+  Materials: []gltf.Material{{
+		Name: "Default", AlphaMode: gltf.Opaque, 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)},
+	}},
   Meshes: []gltf.Mesh{{Name: "Cube", Primitives: []gltf.Primitive{{Indices: gltf.Index(0), Material: gltf.Index(0), Mode: gltf.Triangles, Attributes: map[string]uint32{"POSITION": 1, "COLOR_0": 3, "NORMAL": 2, "TEXCOORD_0": 4}}}}},
   Nodes: []gltf.Node{
     {Name: "RootNode", Children: []uint32{1, 2, 3}},
diff --git a/const.go b/const.go
index 0f2e47d..be3d6af 100644
--- a/const.go
+++ b/const.go
@@ -254,12 +254,12 @@ func (m *MagFilter) MarshalJSON() ([]byte, error) {
 type MinFilter uint16
 
 const (
+	// MinLinear corresponds to a linear minification filter.
+	MinLinear MinFilter = iota
 	// MinNearestMipMapLinear corresponds to a nearest mipmap linear minification filter.
-	MinNearestMipMapLinear MinFilter = iota
+	MinNearestMipMapLinear
 	// MinNearest corresponds to a nearest minification filter.
 	MinNearest
-	// MinLinear corresponds to a linear minification filter.
-	MinLinear
 	// MinNearestMipMapNearest corresponds to a nearest mipmap nearest minification filter.
 	MinNearestMipMapNearest
 	// MinLinearMipMapNearest corresponds to a linear mipmap nearest minification filter.
diff --git a/decoder.go b/decoder.go
index f28cb41..3dd0673 100644
--- a/decoder.go
+++ b/decoder.go
@@ -90,12 +90,15 @@ func (d *Decoder) Decode(doc *Document) error {
 	return nil
 }
 
-func (d *Decoder) decodeDocument(doc *Document) (isBinary bool, err error) {
+func (d *Decoder) decodeDocument(doc *Document) (bool, error) {
 	glbHeader, err := d.readGLBHeader()
 	if err != nil {
-		return
+		return false, err
 	}
-	var jd *json.Decoder
+	var (
+		jd       *json.Decoder
+		isBinary bool
+	)
 	if glbHeader != nil {
 		jd = json.NewDecoder(&io.LimitedReader{R: d.r, N: int64(glbHeader.JSONHeader.Length)})
 		isBinary = true
@@ -105,7 +108,11 @@ func (d *Decoder) decodeDocument(doc *Document) (isBinary bool, err error) {
 	}
 
 	err = jd.Decode(doc)
-	return
+	if err == nil && len(doc.Buffers) > d.quotas.MaxBufferCount {
+		err = errors.New("gltf: Quota exceeded, number of buffer > MaxBufferCount")
+	}
+
+	return isBinary, err
 }
 
 func (d *Decoder) readGLBHeader() (*glbHeader, error) {
@@ -120,13 +127,17 @@ func (d *Decoder) readGLBHeader() (*glbHeader, error) {
 		return nil, nil
 	}
 	d.r.Read(chunk)
+	return &header, d.validateGLBHeader(&header)
+}
+
+func (d *Decoder) validateGLBHeader(header *glbHeader) error {
 	if int(header.Length) > d.quotas.MaxMemoryAllocation {
-		return nil, errors.New("gltf: Quota exceeded, bytes of glb buffer > MaxMemoryAllocation")
+		return errors.New("gltf: Quota exceeded, bytes of glb buffer > MaxMemoryAllocation")
 	}
 	if header.JSONHeader.Type != glbChunkJSON || (header.JSONHeader.Length+uint32(unsafe.Sizeof(header))) > header.Length {
-		return nil, errors.New("gltf: Invalid GLB JSON header")
+		return errors.New("gltf: Invalid GLB JSON header")
 	}
-	return &header, nil
+	return nil
 }
 
 func (d *Decoder) chunkHeader() (*chunkHeader, error) {
diff --git a/encode.go b/encode.go
index 989053e..b97e8db 100644
--- a/encode.go
+++ b/encode.go
@@ -63,7 +63,11 @@ func (e *Encoder) Encode(doc *Document) error {
 	}
 
 	for i := externalBufferIndex; i < len(doc.Buffers); i++ {
-		if err = e.encodeBuffer(&doc.Buffers[i]); err != nil {
+		buffer := &doc.Buffers[i]
+		if len(buffer.Data) == 0 || buffer.IsEmbeddedResource() {
+			continue
+		}
+		if err = e.encodeBuffer(buffer); err != nil {
 			return err
 		}
 	}
@@ -72,21 +76,19 @@ func (e *Encoder) Encode(doc *Document) error {
 }
 
 func (e *Encoder) encodeBuffer(buffer *Buffer) error {
-	if buffer.IsEmbeddedResource() {
-		return nil
-	}
-
-	if len(buffer.Data) == 0 {
+	if len(buffer.Data) == 0 || buffer.IsEmbeddedResource() {
 		return nil
 	}
 
 	if err := validateBufferURI(buffer.URI); err != nil {
 		return err
 	}
+
 	r, err := e.cb(buffer.URI, int(buffer.ByteLength))
 	if err != nil {
 		return err
 	}
+
 	_, err = r.Write(buffer.Data)
 	if err != nil {
 		return err
diff --git a/struct.go b/struct.go
index e4b0796..518b855 100644
--- a/struct.go
+++ b/struct.go
@@ -298,9 +298,9 @@ type Orthographic struct {
 type Perspective struct {
 	Extensions  Extensions  `json:"extensions,omitempty"`
 	Extras      interface{} `json:"extras,omitempty"`
-	AspectRatio float64     `json:"aspectRatio,omitempty"`
+	AspectRatio *float64    `json:"aspectRatio,omitempty"`
 	Yfov        float64     `json:"yfov"`           // The vertical field of view in radians.
-	Zfar        float64     `json:"zfar,omitempty"` // The distance to the far clipping plane.
+	Zfar        *float64    `json:"zfar,omitempty"` // The distance to the far clipping plane.
 	Znear       float64     `json:"znear"`          // The distance to the near clipping plane.
 }
 
@@ -581,7 +581,7 @@ func (p *PBRMetallicRoughness) MarshalJSON() ([]byte, error) {
 type TextureInfo struct {
 	Extensions Extensions  `json:"extensions,omitempty"`
 	Extras     interface{} `json:"extras,omitempty"`
-	Index      *uint32     `json:"index,omitempty"`
+	Index      uint32      `json:"index"`
 	TexCoord   uint32      `json:"texCoord,omitempty"` // The index of texture's TEXCOORD attribute used for texture coordinate mapping.
 }
 


Test patch:
diff --git a/decoder_test.go b/decoder_test.go
index d0d9758..f3bb33f 100644
--- a/decoder_test.go
+++ b/decoder_test.go
@@ -44,7 +44,7 @@ func TestOpen(t *testing.T) {
 			},
 			Buffers:   []Buffer{{ByteLength: 1800, URI: "Cube.bin", Data: readFile("testdata/Cube/glTF/Cube.bin")}},
 			Images:    []Image{{URI: "Cube_BaseColor.png"}, {URI: "Cube_MetallicRoughness.png"}},
-			Materials: []Material{{Name: "Cube", AlphaMode: Opaque, AlphaCutoff: Float64(0.5), PBRMetallicRoughness: &PBRMetallicRoughness{BaseColorFactor: NewRGBA(), MetallicFactor: Float64(1), RoughnessFactor: Float64(1), BaseColorTexture: &TextureInfo{Index: Index(0)}, MetallicRoughnessTexture: &TextureInfo{Index: Index(1)}}}},
+			Materials: []Material{{Name: "Cube", AlphaMode: Opaque, AlphaCutoff: Float64(0.5), PBRMetallicRoughness: &PBRMetallicRoughness{BaseColorFactor: NewRGBA(), MetallicFactor: Float64(1), RoughnessFactor: Float64(1), BaseColorTexture: &TextureInfo{Index: 0}, MetallicRoughnessTexture: &TextureInfo{Index: 1}}}},
 			Meshes:    []Mesh{{Name: "Cube", Primitives: []Primitive{{Indices: Index(0), Material: Index(0), Mode: Triangles, Attributes: map[string]uint32{"NORMAL": 2, "POSITION": 1, "TANGENT": 3, "TEXCOORD_0": 4}}}}},
 			Nodes:     []Node{{Mesh: Index(0), Name: "Cube", Matrix: [16]float64{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}, Rotation: [4]float64{0, 0, 0, 1}, Scale: [3]float64{1, 1, 1}}},
 			Samplers:  []Sampler{{WrapS: Repeat, WrapT: Repeat}},
@@ -66,7 +66,7 @@ func TestOpen(t *testing.T) {
 			},
 			Buffers: []Buffer{{ByteLength: 60, URI: "simpleSquare.bin", Data: readFile("testdata/Cameras/glTF/simpleSquare.bin")}},
 			Cameras: []Camera{
-				{Perspective: &Perspective{AspectRatio: 1.0, Yfov: 0.7, Zfar: 100, Znear: 0.01}},
+				{Perspective: &Perspective{AspectRatio: Float64(1.0), Yfov: 0.7, Zfar: Float64(100), Znear: 0.01}},
 				{Orthographic: &Orthographic{Xmag: 1.0, Ymag: 1.0, Zfar: 100, Znear: 0.01}},
 			},
 			Meshes: []Mesh{{Primitives: []Primitive{{Indices: Index(0), Mode: Triangles, Attributes: map[string]uint32{"POSITION": 1}}}}},
diff --git a/encode_test.go b/encode_test.go
index 1e86a30..3fce7ce 100644
--- a/encode_test.go
+++ b/encode_test.go
@@ -29,9 +29,8 @@ func saveMemory(doc *Document, asBinary bool) (*Decoder, error) {
 	rcb := func(uri string) (io.ReadCloser, error) {
 		if chunk, ok := chunks[uri]; ok {
 			return ioutil.NopCloser(chunk), nil
-		} else {
-			return nil, nil
 		}
+		return nil, nil
 	}
 	return NewDecoder(buff, rcb), nil
 }
@@ -84,7 +83,7 @@ func TestEncoder_Encode(t *testing.T) {
 		}}}, false},
 		{"withCameras", args{&Document{Cameras: []Camera{
 			{Extras: 8.0, Name: "cam_1", Orthographic: &Orthographic{Extras: 8.0, Xmag: 1, Ymag: 2, Zfar: 3, Znear: 4}},
-			{Extras: 8.0, Name: "cam_2", Perspective: &Perspective{Extras: 8.0, AspectRatio: 1, Yfov: 2, Zfar: 3, Znear: 4}},
+			{Extras: 8.0, Name: "cam_2", Perspective: &Perspective{Extras: 8.0, AspectRatio: Float64(1), Yfov: 2, Zfar: Float64(3), Znear: 4}},
 		}}}, false},
 		{"withImages", args{&Document{Images: []Image{
 			{Extras: 8.0, Name: "binary", BufferView: 1, MimeType: "data:image/png"},
@@ -96,8 +95,8 @@ func TestEncoder_Encode(t *testing.T) {
 			{Extras: 8.0, Name: "pbr", AlphaCutoff: Float64(0.5), AlphaMode: Opaque,
 				PBRMetallicRoughness: &PBRMetallicRoughness{
 					Extras: 8.0, MetallicFactor: Float64(1), RoughnessFactor: Float64(2), BaseColorFactor: &RGBA{R: 0.8, G: 0.8, B: 0.8, A: 1},
-					BaseColorTexture:         &TextureInfo{Extras: 8.0, Index: Index(1), TexCoord: 3},
-					MetallicRoughnessTexture: &TextureInfo{Extras: 8.0, Index: Index(6), TexCoord: 5},
+					BaseColorTexture:         &TextureInfo{Extras: 8.0, Index: 1, TexCoord: 3},
+					MetallicRoughnessTexture: &TextureInfo{Extras: 8.0, Index: 6, TexCoord: 5},
 				},
 			},
 			{Extras: 8.0, Name: "normal", AlphaCutoff: Float64(0.7), AlphaMode: Blend,
@@ -106,7 +105,7 @@ func TestEncoder_Encode(t *testing.T) {
 			{Extras: 8.0, Name: "occlusion", AlphaCutoff: Float64(0.5), AlphaMode: Mask,
 				OcclusionTexture: &OcclusionTexture{Extras: 8.0, Index: Index(1), TexCoord: 2, Strength: Float64(2.0)},
 			},
-			{Extras: 8.0, Name: "emmisice", AlphaCutoff: Float64(0.5), AlphaMode: Mask, EmissiveTexture: &TextureInfo{Extras: 8.0, Index: Index(4), TexCoord: 50}},
+			{Extras: 8.0, Name: "emmisice", AlphaCutoff: Float64(0.5), AlphaMode: Mask, EmissiveTexture: &TextureInfo{Extras: 8.0, Index: 4, TexCoord: 50}},
 		}}}, false},
 		{"withMeshes", args{&Document{Meshes: []Mesh{
 			{Extras: 8.0, Name: "mesh_1", Weights: []float64{1.2, 2}},
diff --git a/example_test.go b/example_test.go
index 4ced52d..94d4719 100644
--- a/example_test.go
+++ b/example_test.go
@@ -1,13 +1,53 @@
-package gltf
+package gltf_test
 
 import (
 	"fmt"
+
+	"github.com/qmuntal/gltf"
 )
 
 func ExampleOpen() {
-	doc, err := Open("fake")
+	doc, err := gltf.Open("fake")
 	if err != nil {
 		panic(err)
 	}
 	fmt.Print(doc.Asset)
 }
+
+func ExampleSave() {
+	doc := &gltf.Document{
+		Accessors: []gltf.Accessor{
+			{BufferView: gltf.Index(0), ComponentType: gltf.UnsignedShort, Count: 36, Type: gltf.Scalar},
+			{BufferView: gltf.Index(1), ComponentType: gltf.Float, Count: 24, Max: []float64{0.5, 0.5, 0.5}, Min: []float64{-0.5, -0.5, -0.5}, Type: gltf.Vec3},
+			{BufferView: gltf.Index(2), ComponentType: gltf.Float, Count: 24, Type: gltf.Vec3},
+			{BufferView: gltf.Index(3), ComponentType: gltf.Float, Count: 24, Type: gltf.Vec4},
+			{BufferView: gltf.Index(4), ComponentType: gltf.Float, Count: 24, Type: gltf.Vec2},
+		},
+		Asset: gltf.Asset{Version: "2.0", Generator: "FBX2glTF"},
+		BufferViews: []gltf.BufferView{
+			{Buffer: 0, ByteLength: 72, ByteOffset: 0, Target: gltf.ElementArrayBuffer},
+			{Buffer: 0, ByteLength: 288, ByteOffset: 72, Target: gltf.ArrayBuffer},
+			{Buffer: 0, ByteLength: 288, ByteOffset: 360, Target: gltf.ArrayBuffer},
+			{Buffer: 0, ByteLength: 384, ByteOffset: 648, Target: gltf.ArrayBuffer},
+			{Buffer: 0, ByteLength: 192, ByteOffset: 1032, Target: gltf.ArrayBuffer},
+		},
+		Buffers: []gltf.Buffer{{ByteLength: 1224, Data: []uint8{97, 110, 121, 32, 99, 97, 114, 110, 97, 108, 32, 112, 108, 101, 97, 115}}},
+		Materials: []gltf.Material{{
+			Name: "Default", AlphaMode: gltf.Opaque, 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)},
+		}},
+		Meshes: []gltf.Mesh{{Name: "Cube", Primitives: []gltf.Primitive{{Indices: gltf.Index(0), Material: gltf.Index(0), Mode: gltf.Triangles, Attributes: map[string]uint32{"POSITION": 1, "COLOR_0": 3, "NORMAL": 2, "TEXCOORD_0": 4}}}}},
+		Nodes: []gltf.Node{
+			{Name: "RootNode", Children: []uint32{1, 2, 3}},
+			{Name: "Mesh"},
+			{Name: "Cube", Mesh: gltf.Index(0)},
+			{Name: "Texture Group"},
+		},
+		Samplers: []gltf.Sampler{{WrapS: gltf.Repeat, WrapT: gltf.Repeat}},
+		Scene:    gltf.Index(0),
+		Scenes:   []gltf.Scene{{Name: "Root Scene", Nodes: []uint32{0}}},
+	}
+	if err := gltf.Save(doc, "./a.gltf", true); err != nil {
+		panic(err)
+	}
+}