From 7f338875b5b42980642aa39fb778c2ec4f2727bd Mon Sep 17 00:00:00 2001 From: Quim Muntal Date: Sun, 24 Mar 2019 21:35:26 +0100 Subject: [PATCH 1/8] use pointers instead of int64 --- const.go | 350 +++++++++++++++++++++++----- decoder_test.go | 60 ++--- encode_test.go | 48 ++-- struct.go | 463 +++++++------------------------------ struct_test.go | 572 ++++------------------------------------------ validator_test.go | 36 ++- 6 files changed, 486 insertions(+), 1043 deletions(-) diff --git a/const.go b/const.go index 6d73a9f..5747ac3 100644 --- a/const.go +++ b/const.go @@ -1,65 +1,118 @@ package gltf +import "encoding/json" + // The ComponentType is the datatype of components in the attribute. All valid values correspond to WebGL enums. // 5125 (UNSIGNED_INT) is only allowed when the accessor contains indices. type ComponentType uint16 const ( + // Float corresponds to a Float32Array. + Float ComponentType = iota // Byte corresponds to a Int8Array. - Byte ComponentType = 5120 + Byte // UnsignedByte corresponds to a Uint8Array. - UnsignedByte = 5121 + UnsignedByte // Short corresponds to a Int16Array. - Short = 5122 + Short // UnsignedShort corresponds to a Uint16Array. - UnsignedShort = 5123 + UnsignedShort // UnsignedInt corresponds to a Uint32Array. - UnsignedInt = 5125 - // Float corresponds to a Float32Array. - Float = 5126 + UnsignedInt ) +// UnmarshalJSON unmarshal the component type with the correct default values. +func (c *ComponentType) UnmarshalJSON(data []byte) error { + var tmp uint16 + err := json.Unmarshal(data, &tmp) + if err == nil { + *c = map[uint16]ComponentType{ + 5120: Byte, + 5121: UnsignedByte, + 5122: Short, + 5123: UnsignedShort, + 5125: UnsignedInt, + 5126: Float, + }[tmp] + } + return err +} + +// MarshalJSON marshal the component type with the correct default values. +func (c *ComponentType) MarshalJSON() ([]byte, error) { + return json.Marshal(map[ComponentType]uint16{ + Byte: 5120, + UnsignedByte: 5121, + Short: 5122, + UnsignedShort: 5123, + UnsignedInt: 5125, + Float: 5126, + }[*c]) +} + // AccessorType specifies if the attribute is a scalar, vector, or matrix. -type AccessorType string +type AccessorType uint8 const ( // Scalar corresponds to a single dimension value. - Scalar AccessorType = "SCALAR" + Scalar AccessorType = iota // Vec2 corresponds to a two dimensions array. - Vec2 = "VEC2" + Vec2 // Vec3 corresponds to a three dimensions array. - Vec3 = "VEC3" + Vec3 // Vec4 corresponds to a four dimensions array. - Vec4 = "VEC4" + Vec4 // Mat2 corresponds to a 2x2 matrix. - Mat2 = "MAT2" + Mat2 // Mat3 corresponds to a 3x3 matrix. - Mat3 = "MAT3" + Mat3 // Mat4 corresponds to a 4x4 matrix. - Mat4 = "MAT4" + Mat4 ) +// UnmarshalJSON unmarshal the accessor type with the correct default values. +func (a *AccessorType) UnmarshalJSON(data []byte) error { + var tmp string + err := json.Unmarshal(data, &tmp) + if err == nil { + *a = map[string]AccessorType{ + "SCALAR": Scalar, + "VEC2": Vec2, + "VEC3": Vec3, + "VEC4": Vec4, + "MAT2": Mat2, + "MAT3": Mat3, + "MAT4": Mat4, + }[tmp] + } + return err +} + +// MarshalJSON marshal the accessor type with the correct default values. +func (a *AccessorType) MarshalJSON() ([]byte, error) { + return json.Marshal(map[AccessorType]string{ + Scalar: "SCALAR", + Vec2: "VEC2", + Vec3: "VEC3", + Vec4: "VEC4", + Mat2: "MAT2", + Mat3: "MAT3", + Mat4: "MAT4", + }[*a]) +} + // The Target that the GPU buffer should be bound to. type Target uint16 const ( + // None is used when the buffer should not bound to a target, for example when referenced by an sparce indices. + None = 0 // ArrayBuffer corresponds to an array buffer. ArrayBuffer Target = 34962 // ElementArrayBuffer corresponds to an element array buffer. ElementArrayBuffer = 34963 ) -// CameraType specifies if the camera uses a perspective or orthographic projection. -// Based on this, either the camera's perspective or orthographic property will be defined. -type CameraType string - -const ( - // PerspectiveType corresponds to a perspective camera. - PerspectiveType CameraType = "perspective" - // OrthographicType corresponds to an orthographic camera. - OrthographicType = "orthographic" -) - // Attribute is a map that each key corresponds to mesh attribute semantic and each value is the index of the accessor containing attribute's data. type Attribute = map[string]uint32 @@ -67,101 +120,276 @@ type Attribute = map[string]uint32 type PrimitiveMode uint8 const ( + // Triangles corresponds to a Triangle primitive. + Triangles PrimitiveMode = iota // Points corresponds to a Point primitive. - Points PrimitiveMode = 0 + Points // Lines corresponds to a Line primitive. - Lines = 1 + Lines // LineLoop corresponds to a Line Loop primitive. - LineLoop = 2 + LineLoop // LineStrip corresponds to a Line Strip primitive. - LineStrip = 3 - // Triangles corresponds to a Triangle primitive. - Triangles = 4 + LineStrip // TriangleStrip corresponds to a Triangle Strip primitive. - TriangleStrip = 5 + TriangleStrip // TriangleFan corresponds to a Triangle Fan primitive. - TriangleFan = 6 + TriangleFan ) +// UnmarshalJSON unmarshal the primitive mode with the correct default values. +func (p *PrimitiveMode) UnmarshalJSON(data []byte) error { + var tmp uint8 + err := json.Unmarshal(data, &tmp) + if err == nil { + *p = map[uint8]PrimitiveMode{ + 0: Points, + 1: Lines, + 2: LineLoop, + 3: LineStrip, + 4: Triangles, + 5: TriangleStrip, + 6: TriangleFan, + }[tmp] + } + return err +} + +// MarshalJSON marshal the primitive mode with the correct default values. +func (p *PrimitiveMode) MarshalJSON() ([]byte, error) { + return json.Marshal(map[PrimitiveMode]uint8{ + Points: 0, + Lines: 1, + LineLoop: 2, + LineStrip: 3, + Triangles: 4, + TriangleStrip: 5, + TriangleFan: 6, + }[*p]) +} + // The AlphaMode enumeration specifying the interpretation of the alpha value of the main factor and texture. -type AlphaMode string +type AlphaMode uint8 const ( // Opaque corresponds to an Opaque material. - Opaque AlphaMode = "OPAQUE" + Opaque AlphaMode = iota // Mask corresponds to a masked material. - Mask = "MASK" + Mask // Blend corresponds to a Blend material. - Blend = "BLEND" + Blend ) +// UnmarshalJSON unmarshal the alpha mode with the correct default values. +func (a *AlphaMode) UnmarshalJSON(data []byte) error { + var tmp string + err := json.Unmarshal(data, &tmp) + if err == nil { + *a = map[string]AlphaMode{ + "OPAQUE": Opaque, + "MASK": Mask, + "BLEND": Blend, + }[tmp] + } + return err +} + +// MarshalJSON marshal the alpha mode with the correct default values. +func (a *AlphaMode) MarshalJSON() ([]byte, error) { + return json.Marshal(map[AlphaMode]string{ + Opaque: "OPAQUE", + Mask: "MASK", + Blend: "BLEND", + }[*a]) +} + // MagFilter is the magnification filter. type MagFilter uint16 const ( - // MagNearest corresponds to a nearest magnification filter. - MagNearest MagFilter = 9728 // MagLinear corresponds to a linear magnification filter. - MagLinear = 9729 + MagLinear MagFilter = iota + // MagNearest corresponds to a nearest magnification filter. + MagNearest ) +// UnmarshalJSON unmarshal the mag filter with the correct default values. +func (m *MagFilter) UnmarshalJSON(data []byte) error { + var tmp uint16 + err := json.Unmarshal(data, &tmp) + if err == nil { + *m = map[uint16]MagFilter{ + 9728: MagNearest, + 9729: MagLinear, + }[tmp] + } + return err +} + +// MarshalJSON marshal the alpha mode with the correct default values. +func (m *MagFilter) MarshalJSON() ([]byte, error) { + return json.Marshal(map[MagFilter]uint16{ + MagNearest: 9728, + MagLinear: 9729, + }[*m]) +} + // MinFilter is the minification filter. type MinFilter uint16 const ( + // MinNearestMipMapLinear corresponds to a nearest mipmap linear minification filter. + MinNearestMipMapLinear MinFilter = iota // MinNearest corresponds to a nearest minification filter. - MinNearest MinFilter = 9728 + MinNearest // MinLinear corresponds to a linear minification filter. - MinLinear = 9729 + MinLinear // MinNearestMipMapNearest corresponds to a nearest mipmap nearest minification filter. - MinNearestMipMapNearest = 9984 + MinNearestMipMapNearest // MinLinearMipMapNearest corresponds to a linear mipmap nearest minification filter. - MinLinearMipMapNearest = 9985 - // MinNearestMipMapLinear corresponds to a nearest mipmap linear minification filter. - MinNearestMipMapLinear = 9986 + MinLinearMipMapNearest // MinLinearMipMapLinear corresponds to a linear mipmap linear minification filter. - MinLinearMipMapLinear = 9987 + MinLinearMipMapLinear ) +// UnmarshalJSON unmarshal the min filter with the correct default values. +func (m *MinFilter) UnmarshalJSON(data []byte) error { + var tmp uint16 + err := json.Unmarshal(data, &tmp) + if err == nil { + *m = map[uint16]MinFilter{ + 9728: MinNearest, + 9729: MinLinear, + 9984: MinNearestMipMapNearest, + 9985: MinLinearMipMapNearest, + 9986: MinNearestMipMapLinear, + 9987: MinLinearMipMapLinear, + }[tmp] + } + return err +} + +// MarshalJSON marshal the min filter with the correct default values. +func (m *MinFilter) MarshalJSON() ([]byte, error) { + return json.Marshal(map[MinFilter]uint16{ + MinNearest: 9728, + MinLinear: 9729, + MinNearestMipMapNearest: 9984, + MinLinearMipMapNearest: 9985, + MinNearestMipMapLinear: 9986, + MinLinearMipMapLinear: 9987, + }[*m]) +} + // WrappingMode is the wrapping mode of a texture. type WrappingMode uint16 const ( + // Repeat corresponds to a repeat wrapping. + Repeat WrappingMode = iota // ClampToEdge corresponds to a clamp to edge wrapping. - ClampToEdge WrappingMode = 33071 + ClampToEdge // MirroredRepeat corresponds to a mirrored repeat wrapping. - MirroredRepeat = 33648 - // Repeat corresponds to a repeat wrapping. - Repeat = 10497 + MirroredRepeat ) +// UnmarshalJSON unmarshal the wrapping mode with the correct default values. +func (w *WrappingMode) UnmarshalJSON(data []byte) error { + var tmp uint16 + err := json.Unmarshal(data, &tmp) + if err == nil { + *w = map[uint16]WrappingMode{ + 33071: ClampToEdge, + 33648: MirroredRepeat, + 10497: Repeat, + }[tmp] + } + return err +} + +// MarshalJSON marshal the wrapping mode with the correct default values. +func (w *WrappingMode) MarshalJSON() ([]byte, error) { + return json.Marshal(map[WrappingMode]uint16{ + ClampToEdge: 33071, + MirroredRepeat: 33648, + Repeat: 10497, + }[*w]) +} + // Interpolation algorithm. -type Interpolation string +type Interpolation uint8 const ( // Linear corresponds to a linear interpolation. - Linear Interpolation = "LINEAR" + Linear Interpolation = iota // Step corresponds to a step interpolation. - Step = "STEP" + Step // CubicSpline corresponds to a cubic spline interpolation. - CubicSpline = "CUBICSPLINE" + CubicSpline ) +// UnmarshalJSON unmarshal the interpolation with the correct default values. +func (i *Interpolation) UnmarshalJSON(data []byte) error { + var tmp string + err := json.Unmarshal(data, &tmp) + if err == nil { + *i = map[string]Interpolation{ + "LINEAR": Linear, + "STEP": Step, + "CUBICSPLINE": CubicSpline, + }[tmp] + } + return err +} + +// MarshalJSON marshal the interpolation with the correct default values. +func (i *Interpolation) MarshalJSON() ([]byte, error) { + return json.Marshal(map[Interpolation]string{ + Linear: "LINEAR", + Step: "STEP", + CubicSpline: "CUBICSPLINE", + }[*i]) +} + // TRSProperty defines a local space transformation. // TRSproperties are converted to matrices and postmultiplied in the T * R * S order to compose the transformation matrix. -type TRSProperty string +type TRSProperty uint8 const ( // Translation corresponds to a translation transform. - Translation TRSProperty = "translation" + Translation TRSProperty = iota // Rotation corresponds to a rotation transform. - Rotation = "rotation" + Rotation // Scale corresponds to a scale transform. - Scale = "scale" + Scale // Weights corresponds to a weights transform. - Weights = "weights" + Weights ) +// UnmarshalJSON unmarshal the TRSProperty with the correct default values. +func (t *TRSProperty) UnmarshalJSON(data []byte) error { + var tmp string + err := json.Unmarshal(data, &tmp) + if err == nil { + *t = map[string]TRSProperty{ + "translation": Translation, + "rotation": Rotation, + "scale": Scale, + "weights": Weights, + }[tmp] + } + return err +} + +// MarshalJSON marshal the TRSProperty with the correct default values. +func (t *TRSProperty) MarshalJSON() ([]byte, error) { + return json.Marshal(map[TRSProperty]string{ + Translation: "translation", + Rotation: "rotation", + Scale: "scale", + Weights: "weights", + }[*t]) +} + const ( glbHeaderMagic = 0x46546c67 glbChunkJSON = 0x4e4f534a diff --git a/decoder_test.go b/decoder_test.go index c294943..e05c4f9 100644 --- a/decoder_test.go +++ b/decoder_test.go @@ -29,11 +29,11 @@ func TestOpen(t *testing.T) { {args{"openError", ""}, nil, true}, {args{"testdata/Cube/glTF/Cube.gltf", ""}, &Document{ Accessors: []Accessor{ - {BufferView: 0, ByteOffset: 0, ComponentType: UnsignedShort, Count: 36, Max: []float64{35}, Min: []float64{0}, Type: Scalar}, - {BufferView: 1, ByteOffset: 0, ComponentType: Float, Count: 36, Max: []float64{1, 1, 1}, Min: []float64{-1, -1, -1}, Type: Vec3}, - {BufferView: 2, ByteOffset: 0, ComponentType: Float, Count: 36, Max: []float64{1, 1, 1}, Min: []float64{-1, -1, -1}, Type: Vec3}, - {BufferView: 3, ByteOffset: 0, ComponentType: Float, Count: 36, Max: []float64{1, 0, 0, 1}, Min: []float64{0, 0, -1, -1}, Type: Vec4}, - {BufferView: 4, ByteOffset: 0, ComponentType: Float, Count: 36, Max: []float64{1, 1}, Min: []float64{-1, -1}, Type: Vec2}}, + {BufferView: Index(0), ByteOffset: 0, ComponentType: UnsignedShort, Count: 36, Max: []float64{35}, Min: []float64{0}, Type: Scalar}, + {BufferView: Index(1), ByteOffset: 0, ComponentType: Float, Count: 36, Max: []float64{1, 1, 1}, Min: []float64{-1, -1, -1}, Type: Vec3}, + {BufferView: Index(2), ByteOffset: 0, ComponentType: Float, Count: 36, Max: []float64{1, 1, 1}, Min: []float64{-1, -1, -1}, Type: Vec3}, + {BufferView: Index(3), ByteOffset: 0, ComponentType: Float, Count: 36, Max: []float64{1, 0, 0, 1}, Min: []float64{0, 0, -1, -1}, Type: Vec4}, + {BufferView: Index(4), ByteOffset: 0, ComponentType: Float, Count: 36, Max: []float64{1, 1}, Min: []float64{-1, -1}, Type: Vec2}}, Asset: Asset{Generator: "VKTS glTF 2.0 exporter", Version: "2.0"}, BufferViews: []BufferView{ {Buffer: 0, ByteLength: 72, ByteOffset: 0, Target: ElementArrayBuffer}, @@ -44,20 +44,20 @@ 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: 0.5, PBRMetallicRoughness: &PBRMetallicRoughness{BaseColorFactor: [4]float64{1, 1, 1, 1}, MetallicFactor: 1, RoughnessFactor: 1, BaseColorTexture: &TextureInfo{Index: 0}, MetallicRoughnessTexture: &TextureInfo{Index: 1}}}}, - Meshes: []Mesh{{Name: "Cube", Primitives: []Primitive{{Indices: 0, Material: 0, Mode: Triangles, Attributes: map[string]uint32{"NORMAL": 2, "POSITION": 1, "TANGENT": 3, "TEXCOORD_0": 4}}}}}, - Nodes: []Node{{Mesh: 0, Name: "Cube", Camera: -1, Skin: -1, 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}}}, + Materials: []Material{{Name: "Cube", AlphaMode: Opaque, AlphaCutoff: 0.5, PBRMetallicRoughness: &PBRMetallicRoughness{BaseColorFactor: [4]float64{1, 1, 1, 1}, MetallicFactor: 1, RoughnessFactor: 1, BaseColorTexture: &TextureInfo{Index: Index(0)}, MetallicRoughnessTexture: &TextureInfo{Index: 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}}, - Scene: 0, + Scene: Index(0), Scenes: []Scene{{Nodes: []uint32{0}}}, Textures: []Texture{ - {Sampler: 0, Source: 0}, {Sampler: 0, Source: 1}, + {Sampler: Index(0), Source: Index(0)}, {Sampler: Index(0), Source: Index(1)}, }, }, false}, {args{"testdata/Cameras/glTF/Cameras.gltf", "testdata/Cameras/glTF-Embedded/Cameras.gltf"}, &Document{ Accessors: []Accessor{ - {BufferView: 0, ByteOffset: 0, ComponentType: UnsignedShort, Count: 6, Max: []float64{3}, Min: []float64{0}, Type: Scalar}, - {BufferView: 1, ByteOffset: 0, ComponentType: Float, Count: 4, Max: []float64{1, 1, 0}, Min: []float64{0, 0, 0}, Type: Vec3}, + {BufferView: Index(0), ByteOffset: 0, ComponentType: UnsignedShort, Count: 6, Max: []float64{3}, Min: []float64{0}, Type: Scalar}, + {BufferView: Index(1), ByteOffset: 0, ComponentType: Float, Count: 4, Max: []float64{1, 1, 0}, Min: []float64{0, 0, 0}, Type: Vec3}, }, Asset: Asset{Version: "2.0"}, BufferViews: []BufferView{ @@ -66,25 +66,25 @@ func TestOpen(t *testing.T) { }, Buffers: []Buffer{{ByteLength: 60, URI: "simpleSquare.bin", Data: readFile("testdata/Cameras/glTF/simpleSquare.bin")}}, Cameras: []Camera{ - {Type: PerspectiveType, Perspective: &Perspective{AspectRatio: 1.0, Yfov: 0.7, Zfar: 100, Znear: 0.01}}, - {Type: OrthographicType, Orthographic: &Orthographic{Xmag: 1.0, Ymag: 1.0, Zfar: 100, Znear: 0.01}}, + {Perspective: &Perspective{AspectRatio: 1.0, Yfov: 0.7, Zfar: 100, Znear: 0.01}}, + {Orthographic: &Orthographic{Xmag: 1.0, Ymag: 1.0, Zfar: 100, Znear: 0.01}}, }, - Meshes: []Mesh{{Primitives: []Primitive{{Indices: 0, Material: -1, Mode: Triangles, Attributes: map[string]uint32{"POSITION": 1}}}}}, + Meshes: []Mesh{{Primitives: []Primitive{{Indices: Index(0), Mode: Triangles, Attributes: map[string]uint32{"POSITION": 1}}}}}, Nodes: []Node{ - {Mesh: 0, Camera: -1, Skin: -1, Matrix: [16]float64{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}, Rotation: [4]float64{-0.3, 0, 0, 0.9}, Scale: [3]float64{1, 1, 1}}, - {Mesh: -1, Camera: 0, Skin: -1, 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}, Translation: [3]float64{0.5, 0.5, 3.0}}, - {Mesh: -1, Camera: 1, Skin: -1, 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}, Translation: [3]float64{0.5, 0.5, 3.0}}, + {Mesh: Index(0), Matrix: [16]float64{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}, Rotation: [4]float64{-0.3, 0, 0, 0.9}, Scale: [3]float64{1, 1, 1}}, + {Camera: Index(0), 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}, Translation: [3]float64{0.5, 0.5, 3.0}}, + {Camera: Index(1), 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}, Translation: [3]float64{0.5, 0.5, 3.0}}, }, - Scene: -1, + Scene: nil, Scenes: []Scene{{Nodes: []uint32{0, 1, 2}}}, }, false}, {args{"testdata/BoxVertexColors/glTF-Binary/BoxVertexColors.glb", ""}, &Document{ Accessors: []Accessor{ - {BufferView: 0, ByteOffset: 0, ComponentType: UnsignedShort, Count: 36, Type: Scalar}, - {BufferView: 1, ByteOffset: 0, ComponentType: Float, Count: 24, Max: []float64{0.5, 0.5, 0.5}, Min: []float64{-0.5, -0.5, -0.5}, Type: Vec3}, - {BufferView: 2, ByteOffset: 0, ComponentType: Float, Count: 24, Type: Vec3}, - {BufferView: 3, ByteOffset: 0, ComponentType: Float, Count: 24, Type: Vec4}, - {BufferView: 4, ByteOffset: 0, ComponentType: Float, Count: 24, Type: Vec2}, + {BufferView: Index(0), ByteOffset: 0, ComponentType: UnsignedShort, Count: 36, Type: Scalar}, + {BufferView: Index(1), ByteOffset: 0, ComponentType: Float, Count: 24, Max: []float64{0.5, 0.5, 0.5}, Min: []float64{-0.5, -0.5, -0.5}, Type: Vec3}, + {BufferView: Index(2), ByteOffset: 0, ComponentType: Float, Count: 24, Type: Vec3}, + {BufferView: Index(3), ByteOffset: 0, ComponentType: Float, Count: 24, Type: Vec4}, + {BufferView: Index(4), ByteOffset: 0, ComponentType: Float, Count: 24, Type: Vec2}, }, Asset: Asset{Version: "2.0", Generator: "FBX2glTF"}, BufferViews: []BufferView{ @@ -96,15 +96,15 @@ func TestOpen(t *testing.T) { }, Buffers: []Buffer{{ByteLength: 1224, Data: readFile("testdata/BoxVertexColors/glTF-Binary/BoxVertexColors.glb")[1628+20+8:]}}, Materials: []Material{{Name: "Default", AlphaMode: Opaque, AlphaCutoff: 0.5, PBRMetallicRoughness: &PBRMetallicRoughness{BaseColorFactor: [4]float64{0.8, 0.8, 0.8, 1}, MetallicFactor: 0.1, RoughnessFactor: 0.99}}}, - Meshes: []Mesh{{Name: "Cube", Primitives: []Primitive{{Indices: 0, Material: 0, Mode: Triangles, Attributes: map[string]uint32{"POSITION": 1, "COLOR_0": 3, "NORMAL": 2, "TEXCOORD_0": 4}}}}}, + Meshes: []Mesh{{Name: "Cube", Primitives: []Primitive{{Indices: Index(0), Material: Index(0), Mode: Triangles, Attributes: map[string]uint32{"POSITION": 1, "COLOR_0": 3, "NORMAL": 2, "TEXCOORD_0": 4}}}}}, Nodes: []Node{ - {Name: "RootNode", Mesh: -1, Camera: -1, Skin: -1, Children: []uint32{1, 2, 3}, 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}}, - {Name: "Mesh", Mesh: -1, Camera: -1, Skin: -1, 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}}, - {Name: "Cube", Mesh: 0, Camera: -1, Skin: -1, 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}}, - {Name: "Texture Group", Mesh: -1, Camera: -1, Skin: -1, 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}}, + {Name: "RootNode", Children: []uint32{1, 2, 3}, 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}}, + {Name: "Mesh", 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}}, + {Name: "Cube", Mesh: Index(0), 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}}, + {Name: "Texture Group", 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}}, - Scene: 0, + Scene: Index(0), Scenes: []Scene{{Name: "Root Scene", Nodes: []uint32{0}}}, }, false}, } diff --git a/encode_test.go b/encode_test.go index a2ec70f..09bc974 100644 --- a/encode_test.go +++ b/encode_test.go @@ -49,20 +49,20 @@ func TestEncoder_Encode(t *testing.T) { {"withExtensions", args{&Document{Extras: 8.0, ExtensionsUsed: []string{"c"}, ExtensionsRequired: []string{"d", "e"}}}, false}, {"withAsset", args{&Document{Asset: Asset{Extras: 8.0, Copyright: "@2019", Generator: "qmuntal/gltf", Version: "2.0", MinVersion: "1.0"}}}, false}, {"withAccessors", args{&Document{Accessors: []Accessor{ - {Extras: 8.0, Name: "acc_1", BufferView: 0, ByteOffset: 50, ComponentType: Byte, Normalized: true, Count: 5, Type: Vec3, Max: []float64{1, 2}, Min: []float64{2.4}}, - {BufferView: 0, Normalized: false, Count: 50, Type: Vec4, Sparse: &Sparse{Extras: 8.0, Count: 2, + {Extras: 8.0, Name: "acc_1", BufferView: Index(0), ByteOffset: 50, ComponentType: Byte, Normalized: true, Count: 5, Type: Vec3, Max: []float64{1, 2}, Min: []float64{2.4}}, + {BufferView: Index(0), Normalized: false, Count: 50, Type: Vec4, Sparse: &Sparse{Extras: 8.0, Count: 2, Values: SparseValues{Extras: 8.0, BufferView: 1, ByteOffset: 2}, Indices: SparseIndices{Extras: 8.0, BufferView: 1, ByteOffset: 2, ComponentType: Float}}, }, }}}, false}, {"withAnimations", args{&Document{Animations: []Animation{ {Extras: 8.0, Name: "an_1", Channels: []Channel{ - {Extras: 8.0, Sampler: 1, Target: ChannelTarget{Extras: 8.0, Node: 10, Path: Rotation}}, - {Extras: 8.0, Sampler: 2, Target: ChannelTarget{Extras: 8.0, Node: 10, Path: Scale}}, + {Extras: 8.0, Sampler: Index(1), Target: ChannelTarget{Extras: 8.0, Node: Index(10), Path: Rotation}}, + {Extras: 8.0, Sampler: Index(2), Target: ChannelTarget{Extras: 8.0, Node: Index(10), Path: Scale}}, }}, {Extras: 8.0, Name: "an_2", Channels: []Channel{ - {Extras: 8.0, Sampler: 1, Target: ChannelTarget{Extras: 8.0, Node: 3, Path: Weights}}, - {Extras: 8.0, Sampler: 2, Target: ChannelTarget{Extras: 8.0, Node: 5, Path: Translation}}, + {Extras: 8.0, Sampler: Index(1), Target: ChannelTarget{Extras: 8.0, Node: Index(3), Path: Weights}}, + {Extras: 8.0, Sampler: Index(2), Target: ChannelTarget{Extras: 8.0, Node: Index(5), Path: Translation}}, }}, }}}, false}, {"withBuffer", args{&Document{Buffers: []Buffer{ @@ -75,8 +75,8 @@ func TestEncoder_Encode(t *testing.T) { {Buffer: 10, ByteOffset: 10, ByteLength: 20, ByteStride: 50, Target: ElementArrayBuffer}, }}}, false}, {"withCameras", args{&Document{Cameras: []Camera{ - {Extras: 8.0, Name: "cam_1", Type: OrthographicType, Orthographic: &Orthographic{Extras: 8.0, Xmag: 1, Ymag: 2, Zfar: 3, Znear: 4}}, - {Extras: 8.0, Name: "cam_2", Type: PerspectiveType, Perspective: &Perspective{Extras: 8.0, AspectRatio: 1, Yfov: 2, Zfar: 3, Znear: 4}}, + {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}}, }}}, false}, {"withImages", args{&Document{Images: []Image{ {Extras: 8.0, Name: "binary", BufferView: 1, MimeType: "data:image/png"}, @@ -88,47 +88,47 @@ func TestEncoder_Encode(t *testing.T) { {Extras: 8.0, Name: "pbr", AlphaCutoff: 0.5, AlphaMode: Opaque, PBRMetallicRoughness: &PBRMetallicRoughness{ Extras: 8.0, MetallicFactor: 1, RoughnessFactor: 2, BaseColorFactor: [4]float64{1, 2, 3, 4}, - BaseColorTexture: &TextureInfo{Extras: 8.0, Index: 1, TexCoord: 3}, - MetallicRoughnessTexture: &TextureInfo{Extras: 8.0, Index: 6, TexCoord: 5}, + BaseColorTexture: &TextureInfo{Extras: 8.0, Index: Index(1), TexCoord: 3}, + MetallicRoughnessTexture: &TextureInfo{Extras: 8.0, Index: Index(6), TexCoord: 5}, }, }, {Extras: 8.0, Name: "normal", AlphaCutoff: 0.7, AlphaMode: Blend, - NormalTexture: &NormalTexture{Extras: 8.0, Index: 1, TexCoord: 2, Scale: 2.0}, + NormalTexture: &NormalTexture{Extras: 8.0, Index: Index(1), TexCoord: 2, Scale: 2.0}, }, {Extras: 8.0, Name: "occlusion", AlphaCutoff: 0.5, AlphaMode: Mask, - OcclusionTexture: &OcclusionTexture{Extras: 8.0, Index: 1, TexCoord: 2, Strength: 2.0}, + OcclusionTexture: &OcclusionTexture{Extras: 8.0, Index: Index(1), TexCoord: 2, Strength: 2.0}, }, - {Extras: 8.0, Name: "emmisice", AlphaCutoff: 0.5, AlphaMode: Mask, EmissiveTexture: &TextureInfo{Extras: 8.0, Index: 4, TexCoord: 50}}, + {Extras: 8.0, Name: "emmisice", AlphaCutoff: 0.5, AlphaMode: Mask, EmissiveTexture: &TextureInfo{Extras: 8.0, Index: Index(4), TexCoord: 50}}, }}}, false}, {"withMeshes", args{&Document{Meshes: []Mesh{ {Extras: 8.0, Name: "mesh_1", Weights: []float64{1.2, 2}}, {Extras: 8.0, Name: "mesh_2", Primitives: []Primitive{ - {Extras: 8.0, Attributes: Attribute{"POSITION": 1}, Indices: 2, Material: 1, Mode: Lines}, - {Extras: 8.0, Targets: []Attribute{{"POSITION": 1, "THEN": 4}, {"OTHER": 2}}, Indices: 2, Material: 1, Mode: Lines}, + {Extras: 8.0, Attributes: Attribute{"POSITION": 1}, Indices: Index(2), Material: Index(1), Mode: Lines}, + {Extras: 8.0, Targets: []Attribute{{"POSITION": 1, "THEN": 4}, {"OTHER": 2}}, Indices: Index(2), Material: Index(1), Mode: Lines}, }}, }}}, false}, {"withNodes", args{&Document{Nodes: []Node{ - {Extras: 8.0, Name: "n-1", Camera: 1, Children: []uint32{1, 2}, Skin: 3, - Matrix: [16]float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, Mesh: 15, Rotation: [4]float64{1.5, 1.3, 12, 0}, Scale: [3]float64{1, 3, 4}, Translation: [3]float64{0, 7.8, 9}, Weights: []float64{1, 3}}, - {Extras: 8.0, Name: "n-2", Camera: 1, Children: []uint32{1, 2}, Skin: 3, - Matrix: [16]float64{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}, Mesh: 15, Rotation: [4]float64{0, 0, 0, 1}, Scale: [3]float64{1, 1, 1}}, + {Extras: 8.0, Name: "n-1", Camera: Index(1), Children: []uint32{1, 2}, Skin: Index(3), + Matrix: [16]float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, Mesh: Index(15), Rotation: [4]float64{1.5, 1.3, 12, 0}, Scale: [3]float64{1, 3, 4}, Translation: [3]float64{0, 7.8, 9}, Weights: []float64{1, 3}}, + {Extras: 8.0, Name: "n-2", Camera: Index(1), Children: []uint32{1, 2}, Skin: Index(3), + Matrix: [16]float64{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}, Mesh: Index(15), Rotation: [4]float64{0, 0, 0, 1}, Scale: [3]float64{1, 1, 1}}, }}}, false}, {"withSampler", args{&Document{Samplers: []Sampler{ {Extras: 8.0, Name: "s_1", MagFilter: MagLinear, MinFilter: MinLinearMipMapLinear, WrapS: ClampToEdge, WrapT: MirroredRepeat}, {Extras: 8.0, Name: "s_2", MagFilter: MagNearest, MinFilter: MinLinearMipMapLinear, WrapS: MirroredRepeat, WrapT: Repeat}, }}}, false}, - {"withScene", args{&Document{Scene: 1}}, false}, + {"withScene", args{&Document{Scene: Index(1)}}, false}, {"withScenes", args{&Document{Scenes: []Scene{ {Extras: 8.0, Name: "s_1", Nodes: []uint32{1, 2}}, {Extras: 8.0, Name: "s_2", Nodes: []uint32{2, 3}}, }}}, false}, {"withSkins", args{&Document{Skins: []Skin{ - {Extras: 8.0, Name: "skin_1", InverseBindMatrices: 2, Skeleton: 4, Joints: []uint32{5, 6}}, - {Extras: 8.0, Name: "skin_2", InverseBindMatrices: 3, Skeleton: 4, Joints: []uint32{7, 8}}, + {Extras: 8.0, Name: "skin_1", InverseBindMatrices: Index(2), Skeleton: Index(4), Joints: []uint32{5, 6}}, + {Extras: 8.0, Name: "skin_2", InverseBindMatrices: Index(3), Skeleton: Index(4), Joints: []uint32{7, 8}}, }}}, false}, {"withTextures", args{&Document{Textures: []Texture{ - {Extras: 8.0, Name: "t_1", Sampler: 2, Source: 3}, - {Extras: 8.0, Name: "t_2", Sampler: 3, Source: 4}, + {Extras: 8.0, Name: "t_1", Sampler: Index(2), Source: Index(3)}, + {Extras: 8.0, Name: "t_2", Sampler: Index(3), Source: Index(4)}, }}}, false}, } for _, tt := range tests { diff --git a/struct.go b/struct.go index 557aef3..30cbea9 100644 --- a/struct.go +++ b/struct.go @@ -4,10 +4,16 @@ import ( "bytes" "encoding/base64" "encoding/json" + "errors" "fmt" "strings" ) +// Index is an utility function that returns a pointer to a uin32. +func Index(i uint32) *uint32 { + return &i +} + // An Asset is metadata about the glTF asset. type Asset struct { Extensions Extensions `json:"extensions,omitempty"` @@ -35,38 +41,12 @@ type Document struct { Meshes []Mesh `json:"meshes,omitempty" validate:"dive"` Nodes []Node `json:"nodes,omitempty" validate:"dive"` Samplers []Sampler `json:"samplers,omitempty" validate:"dive"` - Scene int32 `json:"scene" validate:"gte=-1"` + Scene *uint32 `json:"scene,omitempty"` Scenes []Scene `json:"scenes,omitempty" validate:"dive"` Skins []Skin `json:"skins,omitempty" validate:"dive"` Textures []Texture `json:"textures,omitempty" validate:"dive"` } -// UnmarshalJSON unmarshal the document with the correct default values. -func (d *Document) UnmarshalJSON(data []byte) error { - type alias Document - tmp := &alias{Scene: -1} - err := json.Unmarshal(data, tmp) - if err == nil { - *d = Document(*tmp) - } - return err -} - -// MarshalJSON marshal the document with the correct default values. -func (d *Document) MarshalJSON() ([]byte, error) { - type alias Document - if d.Scene == -1 { - return json.Marshal(&struct { - Scene int32 `json:"scene,omitempty"` - *alias - }{ - Scene: 0, - alias: (*alias)(d), - }) - } - return json.Marshal(&struct{ *alias }{alias: (*alias)(d)}) -} - // An Accessor is a typed view into a bufferView. // An accessor provides a typed view into a bufferView or a subset of a bufferView // similar to how WebGL's vertexAttribPointer() defines an attribute in a buffer. @@ -74,48 +54,17 @@ type Accessor struct { Extensions Extensions `json:"extensions,omitempty"` Extras interface{} `json:"extras,omitempty"` Name string `json:"name,omitempty"` - BufferView int32 `json:"bufferView" validate:"gte=-1"` + BufferView *uint32 `json:"bufferView,omitempty"` ByteOffset uint32 `json:"byteOffset,omitempty"` - ComponentType ComponentType `json:"componentType" validate:"oneof=5120 5121 5122 5123 5125 5126"` + ComponentType ComponentType `json:"componentType" validate:"lte=5"` Normalized bool `json:"normalized,omitempty"` // Specifies whether integer data values should be normalized. Count uint32 `json:"count" validate:"required"` // The number of attributes referenced by this accessor. - Type AccessorType `json:"type" validate:"oneof=SCALAR VEC2 VEC3 VEC4 MAT2 MAT3 MAT4"` + Type AccessorType `json:"type" validate:"lte=6"` Max []float64 `json:"max,omitempty" validate:"omitempty,lte=16"` // Maximum value of each component in this attribute. Min []float64 `json:"min,omitempty" validate:"omitempty,lte=16"` // Minimum value of each component in this attribute. Sparse *Sparse `json:"sparse,omitempty"` // Sparse storage of attributes that deviate from their initialization value. } -// NewAccessor returns a default accessor. -func NewAccessor() *Accessor { - return &Accessor{BufferView: -1} -} - -// UnmarshalJSON unmarshal the accessor with the correct default values. -func (a *Accessor) UnmarshalJSON(data []byte) error { - type alias Accessor - tmp := alias(*NewAccessor()) - err := json.Unmarshal(data, &tmp) - if err == nil { - *a = Accessor(tmp) - } - return err -} - -// MarshalJSON marshal the accessor with the correct default values. -func (a *Accessor) MarshalJSON() ([]byte, error) { - type alias Accessor - if a.BufferView == -1 { - return json.Marshal(&struct { - BufferView int32 `json:"bufferView,omitempty"` - *alias - }{ - BufferView: 0, - alias: (*alias)(a), - }) - } - return json.Marshal(&struct{ *alias }{alias: (*alias)(a)}) -} - // Sparse storage of attributes that deviate from their initialization value. type Sparse struct { Extensions Extensions `json:"extensions,omitempty"` @@ -139,7 +88,7 @@ type SparseIndices struct { Extras interface{} `json:"extras,omitempty"` BufferView uint32 `json:"bufferView"` ByteOffset uint32 `json:"byteOffset,omitempty"` - ComponentType ComponentType `json:"componentType" validate:"oneof=5121 5123 5125"` + ComponentType ComponentType `json:"componentType" validate:"oneof=2 4 5"` } // A Buffer points to binary geometry, animation, or skins. @@ -175,39 +124,13 @@ func (b *Buffer) marshalData() ([]uint8, error) { type BufferView struct { Extensions Extensions `json:"extensions,omitempty"` Extras interface{} `json:"extras,omitempty"` - Buffer int32 `json:"buffer" validate:"gte=-1"` + Buffer uint32 `json:"buffer"` ByteOffset uint32 `json:"byteOffset,omitempty"` ByteLength uint32 `json:"byteLength" validate:"required"` ByteStride uint32 `json:"byteStride,omitempty" validate:"omitempty,gte=4,lte=252"` Target Target `json:"target,omitempty" validate:"omitempty,oneof=34962 34963"` } -// UnmarshalJSON unmarshal the buffer view with the correct default values. -func (b *BufferView) UnmarshalJSON(data []byte) error { - type alias BufferView - tmp := &alias{Buffer: -1} - err := json.Unmarshal(data, tmp) - if err == nil { - *b = BufferView(*tmp) - } - return err -} - -// MarshalJSON marshal the buffer view with the correct default values. -func (b *BufferView) MarshalJSON() ([]byte, error) { - type alias BufferView - if b.Buffer == -1 { - return json.Marshal(&struct { - Buffer int32 `json:"buffer,omitempty"` - *alias - }{ - Buffer: 0, - alias: (*alias)(b), - }) - } - return json.Marshal(&struct{ *alias }{alias: (*alias)(b)}) -} - // The Scene contains a list of root nodes. type Scene struct { Extensions Extensions `json:"extensions,omitempty"` @@ -222,11 +145,11 @@ type Node struct { Extensions Extensions `json:"extensions,omitempty"` Extras interface{} `json:"extras,omitempty"` Name string `json:"name,omitempty"` - Camera int32 `json:"camera" validate:"gte=-1"` + Camera *uint32 `json:"camera,omitempty"` Children []uint32 `json:"children,omitempty" validate:"omitempty,unique"` - Skin int32 `json:"skin" validate:"gte=-1"` + Skin *uint32 `json:"skin,omitempty"` Matrix [16]float64 `json:"matrix"` // A 4x4 transformation matrix stored in column-major order. - Mesh int32 `json:"mesh" validate:"gte=-1"` + Mesh *uint32 `json:"mesh,omitempty"` Rotation [4]float64 `json:"rotation" validate:"omitempty,dive,gte=-1,lte=1"` // The node's unit quaternion rotation in the order (x, y, z, w), where w is the scalar. Scale [3]float64 `json:"scale"` Translation [3]float64 `json:"translation"` @@ -239,9 +162,6 @@ func NewNode() *Node { 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}, - Camera: -1, - Skin: -1, - Mesh: -1, } } @@ -273,15 +193,6 @@ func (n *Node) MarshalJSON() ([]byte, error) { if n.Translation == [3]float64{0, 0, 0} { out = removeProperty([]byte(`"translation":[0,0,0]`), out) } - if n.Camera == -1 { - out = removeProperty([]byte(`"camera":-1`), out) - } - if n.Skin == -1 { - out = removeProperty([]byte(`"skin":-1`), out) - } - if n.Mesh == -1 { - out = removeProperty([]byte(`"mesh":-1`), out) - } out = sanitizeJSON(out) } return out, err @@ -292,41 +203,9 @@ type Skin struct { Extensions Extensions `json:"extensions,omitempty"` Extras interface{} `json:"extras,omitempty"` Name string `json:"name,omitempty"` - InverseBindMatrices int32 `json:"inverseBindMatrices" validate:"gte=-1"` // The index of the accessor containing the floating-point 4x4 inverse-bind matrices. - Skeleton int32 `json:"skeleton" validate:"gte=-1"` // The index of the node used as a skeleton root. When undefined, joints transforms resolve to scene root. - Joints []uint32 `json:"joints" validate:"omitempty,unique"` // Indices of skeleton nodes, used as joints in this skin. -} - -// NewSkin create a default Skin. -func NewSkin() *Skin { - return &Skin{InverseBindMatrices: -1, Skeleton: -1} -} - -// UnmarshalJSON unmarshal the skin with the correct default values. -func (s *Skin) UnmarshalJSON(data []byte) error { - type alias Skin - tmp := alias(*NewSkin()) - err := json.Unmarshal(data, &tmp) - if err == nil { - *s = Skin(tmp) - } - return err -} - -// MarshalJSON marshal the skin with the correct default values. -func (s *Skin) MarshalJSON() ([]byte, error) { - type alias Skin - out, err := json.Marshal(&struct{ *alias }{alias: (*alias)(s)}) - if err == nil { - if s.InverseBindMatrices == -1 { - out = removeProperty([]byte(`"inverseBindMatrices":-1`), out) - } - if s.Skeleton == -1 { - out = removeProperty([]byte(`"skeleton":-1`), out) - } - out = sanitizeJSON(out) - } - return out, err + InverseBindMatrices *uint32 `json:"inverseBindMatrices,omitempty"` // The index of the accessor containing the floating-point 4x4 inverse-bind matrices. + Skeleton *uint32 `json:"skeleton,omitempty"` // The index of the node used as a skeleton root. When undefined, joints transforms resolve to scene root. + Joints []uint32 `json:"joints" validate:"omitempty,unique"` // Indices of skeleton nodes, used as joints in this skin. } // A Camera projection. A node can reference a camera to apply a transform to place the camera in the scene. @@ -336,7 +215,29 @@ type Camera struct { Name string `json:"name,omitempty"` Orthographic *Orthographic `json:"orthographic,omitempty"` Perspective *Perspective `json:"perspective,omitempty"` - Type CameraType `json:"type" validate:"oneof=perspective orthographic"` +} + +// MarshalJSON marshal the camera with the correct default values. +func (c *Camera) MarshalJSON() ([]byte, error) { + type alias Camera + if c.Perspective != nil { + return json.Marshal(&struct { + Type string `json:"type"` + *alias + }{ + Type: "perspective", + alias: (*alias)(c), + }) + } else if c.Orthographic != nil { + return json.Marshal(&struct { + Type string `json:"type"` + *alias + }{ + Type: "orthographic", + alias: (*alias)(c), + }) + } + return nil, errors.New("gltf: camera must defined either the perspective or orthographic property") } // Orthographic camera containing properties to create an orthographic projection matrix. @@ -373,44 +274,12 @@ type Primitive struct { Extensions Extensions `json:"extensions,omitempty"` Extras interface{} `json:"extras,omitempty"` Attributes Attribute `json:"attributes"` - Indices int32 `json:"indices" validate:"gte=-1"` // The index of the accessor that contains the indices. - Material int32 `json:"material" validate:"gte=-1"` - Mode PrimitiveMode `json:"mode" validate:"lte=6"` + Indices *uint32 `json:"indices,omitempty"` // The index of the accessor that contains the indices. + Material *uint32 `json:"material,omitempty"` + Mode PrimitiveMode `json:"mode,omitempty" validate:"lte=6"` Targets []Attribute `json:"targets,omitempty" validate:"omitempty,dive,dive,keys,oneof=POSITION NORMAL TANGENT,endkeys"` // Only POSITION, NORMAL, and TANGENT supported. } -// NewPrimitive create a default Primitive. -func NewPrimitive() *Primitive { - return &Primitive{Mode: Triangles, Indices: -1, Material: -1} -} - -// UnmarshalJSON unmarshal the primitive with the correct default values. -func (p *Primitive) UnmarshalJSON(data []byte) error { - type alias Primitive - tmp := alias(*NewPrimitive()) - err := json.Unmarshal(data, &tmp) - if err == nil { - *p = Primitive(tmp) - } - return err -} - -// MarshalJSON marshal the primitive with the correct default values. -func (p *Primitive) MarshalJSON() ([]byte, error) { - type alias Primitive - out, err := json.Marshal(&struct{ *alias }{alias: (*alias)(p)}) - if err == nil { - if p.Indices == -1 { - out = removeProperty([]byte(`"indices":-1`), out) - } - if p.Material == -1 { - out = removeProperty([]byte(`"material":-1`), out) - } - out = sanitizeJSON(out) - } - return out, err -} - // The Material appearance of a primitive. type Material struct { Extensions Extensions `json:"extensions,omitempty"` @@ -421,14 +290,14 @@ type Material struct { OcclusionTexture *OcclusionTexture `json:"occlusionTexture,omitempty"` EmissiveTexture *TextureInfo `json:"emissiveTexture,omitempty"` EmissiveFactor [3]float64 `json:"emissiveFactor,omitempty" validate:"dive,gte=0,lte=1"` - AlphaMode AlphaMode `json:"alphaMode,omitempty" validate:"oneof=OPAQUE MASK BLEND"` + AlphaMode AlphaMode `json:"alphaMode,omitempty" validate:"lte=2"` AlphaCutoff float64 `json:"alphaCutoff" validate:"gte=0"` DoubleSided bool `json:"doubleSided,omitempty"` } // NewMaterial create a default Material. func NewMaterial() *Material { - return &Material{AlphaCutoff: 0.5, AlphaMode: Opaque} + return &Material{AlphaCutoff: 0.5} } // UnmarshalJSON unmarshal the material with the correct default values. @@ -450,9 +319,6 @@ func (m *Material) MarshalJSON() ([]byte, error) { if m.AlphaCutoff == 0.5 { out = removeProperty([]byte(`"alphaCutoff":0.5`), out) } - if m.AlphaMode == Opaque { - out = removeProperty([]byte(`"alphaMode":"OPAQUE"`), out) - } if m.EmissiveFactor == [3]float64{0, 0, 0} { out = removeProperty([]byte(`"emissiveFactor":[0,0,0]`), out) } @@ -465,20 +331,20 @@ func (m *Material) MarshalJSON() ([]byte, error) { type NormalTexture struct { Extensions Extensions `json:"extensions,omitempty"` Extras interface{} `json:"extras,omitempty"` - Index int32 `json:"index" validate:"gte=-1"` + Index *uint32 `json:"index,omitempty"` TexCoord uint32 `json:"texCoord,omitempty"` // The index of texture's TEXCOORD attribute used for texture coordinate mapping. Scale float64 `json:"scale"` } // NewNormalTexture returns a default NormalTexture. -func NewNormalTexture(index int32) *NormalTexture { - return &NormalTexture{Index: index, Scale: 1} +func NewNormalTexture() *NormalTexture { + return &NormalTexture{Scale: 1} } // UnmarshalJSON unmarshal the texture info with the correct default values. func (n *NormalTexture) UnmarshalJSON(data []byte) error { type alias NormalTexture - tmp := alias(*NewNormalTexture(-1)) + tmp := alias(*NewNormalTexture()) err := json.Unmarshal(data, &tmp) if err == nil { *n = NormalTexture(tmp) @@ -489,37 +355,36 @@ func (n *NormalTexture) UnmarshalJSON(data []byte) error { // MarshalJSON marshal the texture info with the correct default values. func (n *NormalTexture) MarshalJSON() ([]byte, error) { type alias NormalTexture - out, err := json.Marshal(&struct{ *alias }{alias: (*alias)(n)}) - if err == nil { - if n.Index == -1 { - out = removeProperty([]byte(`"index":-1`), out) - } - if n.Scale == -1 { - out = removeProperty([]byte(`"scale":-1`), out) - } - out = sanitizeJSON(out) + if n.Scale == 1 { + return json.Marshal(&struct { + Scale float64 `json:"scale,omitempty"` + *alias + }{ + Scale: 0, + alias: (*alias)(n), + }) } - return out, err + return json.Marshal(&struct{ *alias }{alias: (*alias)(n)}) } // An OcclusionTexture references to an occlusion texture type OcclusionTexture struct { Extensions Extensions `json:"extensions,omitempty"` Extras interface{} `json:"extras,omitempty"` - Index int32 `json:"index" validate:"gte=-1"` + Index *uint32 `json:"index,omitempty"` TexCoord uint32 `json:"texCoord,omitempty"` // The index of texture's TEXCOORD attribute used for texture coordinate mapping. Strength float64 `json:"strength" validate:"gte=0,lte=1"` } // NewOcclusionTexture returns a default OcclusionTexture. -func NewOcclusionTexture(index int32) *OcclusionTexture { - return &OcclusionTexture{Index: index, Strength: 1} +func NewOcclusionTexture() *OcclusionTexture { + return &OcclusionTexture{Strength: 1} } // UnmarshalJSON unmarshal the texture info with the correct default values. func (o *OcclusionTexture) UnmarshalJSON(data []byte) error { type alias OcclusionTexture - tmp := alias(*NewOcclusionTexture(-1)) + tmp := alias(*NewOcclusionTexture()) err := json.Unmarshal(data, &tmp) if err == nil { *o = OcclusionTexture(tmp) @@ -530,17 +395,16 @@ func (o *OcclusionTexture) UnmarshalJSON(data []byte) error { // MarshalJSON marshal the texture info with the correct default values. func (o *OcclusionTexture) MarshalJSON() ([]byte, error) { type alias OcclusionTexture - out, err := json.Marshal(&struct{ *alias }{alias: (*alias)(o)}) - if err == nil { - if o.Index == -1 { - out = removeProperty([]byte(`"index":-1`), out) - } - if o.Strength == 1 { - out = removeProperty([]byte(`"strength":1`), out) - } - out = sanitizeJSON(out) + if o.Strength == 1 { + return json.Marshal(&struct { + Strength float64 `json:"strength,omitempty"` + *alias + }{ + Strength: 0, + alias: (*alias)(o), + }) } - return out, err + return json.Marshal(&struct{ *alias }{alias: (*alias)(o)}) } // PBRMetallicRoughness defines a set of parameter values that are used to define the metallic-roughness material model from Physically-Based Rendering (PBR) methodology. @@ -593,80 +457,17 @@ func (p *PBRMetallicRoughness) MarshalJSON() ([]byte, error) { type TextureInfo struct { Extensions Extensions `json:"extensions,omitempty"` Extras interface{} `json:"extras,omitempty"` - Index int32 `json:"index" validate:"gte=-1"` + Index *uint32 `json:"index,omitempty"` TexCoord uint32 `json:"texCoord,omitempty"` // The index of texture's TEXCOORD attribute used for texture coordinate mapping. } -// NewTextureInfo returns a default TextureInfo. -func NewTextureInfo(index int32) *TextureInfo { - return &TextureInfo{Index: index} -} - -// UnmarshalJSON unmarshal the texture info with the correct default values. -func (t *TextureInfo) UnmarshalJSON(data []byte) error { - type alias TextureInfo - tmp := alias(*NewTextureInfo(-1)) - err := json.Unmarshal(data, &tmp) - if err == nil { - *t = TextureInfo(tmp) - } - return err -} - -// MarshalJSON marshal the texture info with the correct default values. -func (t *TextureInfo) MarshalJSON() ([]byte, error) { - type alias TextureInfo - if t.Index == -1 { - return json.Marshal(&struct { - Index int32 `json:"index,omitempty"` - *alias - }{ - Index: 0, - alias: (*alias)(t), - }) - } - return json.Marshal(&struct{ *alias }{alias: (*alias)(t)}) -} - // A Texture and its sampler. type Texture struct { Extensions Extensions `json:"extensions,omitempty"` Extras interface{} `json:"extras,omitempty"` Name string `json:"name,omitempty"` - Sampler int32 `json:"sampler" validate:"gte=-1"` - Source int32 `json:"source" validate:"gte=-1"` -} - -// NewTexture returns a default Texture. -func NewTexture() *Texture { - return &Texture{Sampler: -1, Source: -1} -} - -// UnmarshalJSON unmarshal the texture with the correct default values. -func (t *Texture) UnmarshalJSON(data []byte) error { - type alias Texture - tmp := alias(*NewTexture()) - err := json.Unmarshal(data, &tmp) - if err == nil { - *t = Texture(tmp) - } - return err -} - -// MarshalJSON marshal the texture with the correct default values. -func (t *Texture) MarshalJSON() ([]byte, error) { - type alias Texture - out, err := json.Marshal(&struct{ *alias }{alias: (*alias)(t)}) - if err == nil { - if t.Sampler == -1 { - out = removeProperty([]byte(`"sampler":-1`), out) - } - if t.Source == -1 { - out = removeProperty([]byte(`"source":-1`), out) - } - out = sanitizeJSON(out) - } - return out, err + Sampler *uint32 `json:"sampler,omitempty"` + Source *uint32 `json:"source,omitempty"` } // Sampler of a texture for filtering and wrapping modes. @@ -674,26 +475,10 @@ type Sampler struct { Extensions Extensions `json:"extensions,omitempty"` Extras interface{} `json:"extras,omitempty"` Name string `json:"name,omitempty"` - MagFilter MagFilter `json:"magFilter,omitempty" validate:"omitempty,oneof=9728 9729"` - MinFilter MinFilter `json:"minFilter,omitempty" validate:"omitempty,oneof=9728 9729 9984 9985 9986 9987"` - WrapS WrappingMode `json:"wrapS,omitempty" validate:"omitempty,oneof=33071 33648 10497"` - WrapT WrappingMode `json:"wrapT,omitempty" validate:"omitempty,oneof=33071 33648 10497"` -} - -// NewSampler returns a default Sampler. -func NewSampler() *Sampler { - return &Sampler{WrapS: Repeat, WrapT: Repeat} -} - -// UnmarshalJSON unmarshal the sampler with the correct default values. -func (s *Sampler) UnmarshalJSON(data []byte) error { - type alias Sampler - tmp := alias(*NewSampler()) - err := json.Unmarshal(data, &tmp) - if err == nil { - *s = Sampler(tmp) - } - return err + MagFilter MagFilter `json:"magFilter,omitempty" validate:"lte=1"` + MinFilter MinFilter `json:"minFilter,omitempty" validate:"lte=5"` + WrapS WrappingMode `json:"wrapS,omitempty" validate:"lte=2"` + WrapT WrappingMode `json:"wrapT,omitempty" validate:"lte=2"` } // Image data used to create a texture. Image can be referenced by URI or bufferView index. @@ -738,66 +523,19 @@ type Animation struct { type AnimationSampler struct { Extensions Extensions `json:"extensions,omitempty"` Extras interface{} `json:"extras,omitempty"` - Input int32 `json:"input" validate:"gte=-1"` // The index of an accessor containing keyframe input values. - Interpolation Interpolation `json:"interpolation,omitempty" validate:"omitempty,oneof=LINEAR STEP CUBICSPLINE"` - Output int32 `json:"output" validate:"gte=-1"` // The index of an accessor containing keyframe output values. -} - -// NewAnimationSampler returns a default AnimationSampler. -func NewAnimationSampler() *AnimationSampler { - return &AnimationSampler{Input: -1, Interpolation: Linear, Output: -1} -} - -// UnmarshalJSON unmarshal the animation sampler with the correct default values. -func (as *AnimationSampler) UnmarshalJSON(data []byte) error { - type alias AnimationSampler - tmp := alias(*NewAnimationSampler()) - err := json.Unmarshal(data, &tmp) - if err == nil { - *as = AnimationSampler(tmp) - } - return err + Input *uint32 `json:"input,omitempty"` // The index of an accessor containing keyframe input values. + Interpolation Interpolation `json:"interpolation,omitempty" validate:"lte=2"` + Output *uint32 `json:"output,omitempty"` // The index of an accessor containing keyframe output values. } // The Channel targets an animation's sampler at a node's property. type Channel struct { Extensions Extensions `json:"extensions,omitempty"` Extras interface{} `json:"extras,omitempty"` - Sampler int32 `json:"sampler" validate:"gte=-1"` + Sampler *uint32 `json:"sampler,omitempty"` Target ChannelTarget `json:"target"` } -// NewChannel returns a default Channel. -func NewChannel(sampler int32) *Channel { - return &Channel{Sampler: sampler} -} - -// UnmarshalJSON unmarshal the channel with the correct default values. -func (ch *Channel) UnmarshalJSON(data []byte) error { - type alias Channel - tmp := alias(*NewChannel(-1)) - err := json.Unmarshal(data, &tmp) - if err == nil { - *ch = Channel(tmp) - } - return err -} - -// MarshalJSON marshal the channel with the correct default values. -func (ch *Channel) MarshalJSON() ([]byte, error) { - type alias Channel - if ch.Sampler == -1 { - return json.Marshal(&struct { - Sampler int32 `json:"sampler,omitempty"` - *alias - }{ - Sampler: 0, - alias: (*alias)(ch), - }) - } - return json.Marshal(&struct{ *alias }{alias: (*alias)(ch)}) -} - // ChannelTarget describes the index of the node and TRS property that an animation channel targets. // The Path represents the name of the node's TRS property to modify, or the "weights" of the Morph Targets it instantiates. // For the "translation" property, the values that are provided by the sampler are the translation along the x, y, and z axes. @@ -806,39 +544,8 @@ func (ch *Channel) MarshalJSON() ([]byte, error) { type ChannelTarget struct { Extensions Extensions `json:"extensions,omitempty"` Extras interface{} `json:"extras,omitempty"` - Node int32 `json:"node" validate:"gte=-1"` - Path TRSProperty `json:"path" validate:"oneof=translation rotation scale weights"` -} - -// NewChannelTarget returns a default ChannelTarget. -func NewChannelTarget(path TRSProperty) *ChannelTarget { - return &ChannelTarget{Node: -1, Path: path} -} - -// UnmarshalJSON unmarshal the channel target with the correct default values. -func (ch *ChannelTarget) UnmarshalJSON(data []byte) error { - type alias ChannelTarget - tmp := alias(*NewChannelTarget("")) - err := json.Unmarshal(data, &tmp) - if err == nil { - *ch = ChannelTarget(tmp) - } - return err -} - -// MarshalJSON marshal the channel target with the correct default values. -func (ch *ChannelTarget) MarshalJSON() ([]byte, error) { - type alias ChannelTarget - if ch.Node == -1 { - return json.Marshal(&struct { - Node int32 `json:"node,omitempty"` - *alias - }{ - Node: 0, - alias: (*alias)(ch), - }) - } - return json.Marshal(&struct{ *alias }{alias: (*alias)(ch)}) + Node *uint32 `json:"node,omitempty"` + Path TRSProperty `json:"path" validate:"lte=4"` } func removeProperty(str []byte, b []byte) []byte { diff --git a/struct_test.go b/struct_test.go index 2bb74fc..67a90ad 100644 --- a/struct_test.go +++ b/struct_test.go @@ -103,25 +103,22 @@ func TestNode_UnmarshalJSON(t *testing.T) { 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}, - Camera: -1, - Mesh: -1, - Skin: -1, }, false}, {"nodefault", new(Node), args{[]byte(`{"matrix":[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],"rotation":[0,0,0,1],"scale":[1,1,1],"camera":0,"mesh":0,"skin":0}`)}, &Node{ 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}, - Camera: 0, - Mesh: 0, - Skin: 0, + Camera: Index(0), + Mesh: Index(0), + Skin: Index(0), }, false}, {"nodefault", new(Node), args{[]byte(`{"matrix":[1,2,2,0,0,1,3,4,0,0,1,0,5,0,0,5],"rotation":[1,2,3,4],"scale":[2,3,4],"camera":1,"mesh":2,"skin":3}`)}, &Node{ Matrix: [16]float64{1, 2, 2, 0, 0, 1, 3, 4, 0, 0, 1, 0, 5, 0, 0, 5}, Rotation: [4]float64{1, 2, 3, 4}, Scale: [3]float64{2, 3, 4}, - Camera: 1, - Mesh: 2, - Skin: 3, + Camera: Index(1), + Mesh: Index(2), + Skin: Index(3), }, false}, } for _, tt := range tests { @@ -137,34 +134,6 @@ func TestNode_UnmarshalJSON(t *testing.T) { } } -func TestPrimitive_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - p *Primitive - args args - want *Primitive - wantErr bool - }{ - {"default", new(Primitive), args{[]byte("{}")}, &Primitive{Mode: Triangles, Indices: -1, Material: -1}, false}, - {"empty", new(Primitive), args{[]byte(`{"mode": 0, "indices": 0, "material": 0}`)}, &Primitive{Mode: Points, Indices: 0, Material: 0}, false}, - {"nodefault", new(Primitive), args{[]byte(`{"mode": 1, "indices": 2, "material": 3}`)}, &Primitive{Mode: Lines, Indices: 2, Material: 3}, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := tt.p.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("Primitive.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(tt.p, tt.want) { - t.Errorf("Primitive.UnmarshalJSON() = %v, want %v", tt.p, tt.want) - } - }) - } -} - func TestMaterial_UnmarshalJSON(t *testing.T) { type args struct { data []byte @@ -203,9 +172,9 @@ func TestNormalTexture_UnmarshalJSON(t *testing.T) { want *NormalTexture wantErr bool }{ - {"default", new(NormalTexture), args{[]byte("{}")}, &NormalTexture{Scale: 1, Index: -1}, false}, - {"empty", new(NormalTexture), args{[]byte(`{"scale": 0, "index": 0}`)}, &NormalTexture{Scale: 0, Index: 0}, false}, - {"nodefault", new(NormalTexture), args{[]byte(`{"scale": 0.5, "index":2}`)}, &NormalTexture{Scale: 0.5, Index: 2}, false}, + {"default", new(NormalTexture), args{[]byte("{}")}, &NormalTexture{Scale: 1}, false}, + {"empty", new(NormalTexture), args{[]byte(`{"scale": 0, "index": 0}`)}, &NormalTexture{Scale: 0, Index: Index(0)}, false}, + {"nodefault", new(NormalTexture), args{[]byte(`{"scale": 0.5, "index":2}`)}, &NormalTexture{Scale: 0.5, Index: Index(2)}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -231,9 +200,9 @@ func TestOcclusionTexture_UnmarshalJSON(t *testing.T) { want *OcclusionTexture wantErr bool }{ - {"default", new(OcclusionTexture), args{[]byte("{}")}, &OcclusionTexture{Strength: 1, Index: -1}, false}, - {"empty", new(OcclusionTexture), args{[]byte(`{"strength": 0, "index": 0}`)}, &OcclusionTexture{Strength: 0, Index: 0}, false}, - {"nodefault", new(OcclusionTexture), args{[]byte(`{"strength": 0.5, "index":2}`)}, &OcclusionTexture{Strength: 0.5, Index: 2}, false}, + {"default", new(OcclusionTexture), args{[]byte("{}")}, &OcclusionTexture{Strength: 1}, false}, + {"empty", new(OcclusionTexture), args{[]byte(`{"strength": 0, "index": 0}`)}, &OcclusionTexture{Strength: 0, Index: Index(0)}, false}, + {"nodefault", new(OcclusionTexture), args{[]byte(`{"strength": 0.5, "index":2}`)}, &OcclusionTexture{Strength: 0.5, Index: Index(2)}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -277,360 +246,6 @@ func TestPBRMetallicRoughness_UnmarshalJSON(t *testing.T) { } } -func TestSampler_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - s *Sampler - args args - want *Sampler - wantErr bool - }{ - {"default", new(Sampler), args{[]byte("{}")}, &Sampler{WrapS: Repeat, WrapT: Repeat}, false}, - {"nodefault", new(Sampler), args{[]byte(`{"wrapS": 33648, "wrapT": 33071}`)}, &Sampler{WrapS: MirroredRepeat, WrapT: ClampToEdge}, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := tt.s.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("Sampler.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(tt.s, tt.want) { - t.Errorf("Sampler.UnmarshalJSON() = %v, want %v", tt.s, tt.want) - } - }) - } -} - -func TestAnimationSampler_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - as *AnimationSampler - args args - want *AnimationSampler - wantErr bool - }{ - {"default", new(AnimationSampler), args{[]byte("{}")}, &AnimationSampler{Interpolation: Linear, Input: -1, Output: -1}, false}, - {"empty", new(AnimationSampler), args{[]byte(`{"interpolation": "LINEAR", "input": 0, "output":0}`)}, &AnimationSampler{Interpolation: Linear, Input: 0, Output: 0}, false}, - {"nodefault", new(AnimationSampler), args{[]byte(`{"interpolation": "STEP", "input": 1, "output":2}`)}, &AnimationSampler{Interpolation: Step, Input: 1, Output: 2}, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := tt.as.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("AnimationSampler.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(tt.as, tt.want) { - t.Errorf("AnimationSampler.UnmarshalJSON() = %v, want %v", tt.as, tt.want) - } - }) - } -} - -func TestAccessor_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - a *Accessor - args args - want *Accessor - wantErr bool - }{ - {"default", new(Accessor), args{[]byte("{}")}, &Accessor{BufferView: -1}, false}, - {"empty", new(Accessor), args{[]byte(`{"bufferView": 0}`)}, &Accessor{BufferView: 0}, false}, - {"nodefault", new(Accessor), args{[]byte(`{"bufferView": 1}`)}, &Accessor{BufferView: 1}, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := tt.a.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("Accessor.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(tt.a, tt.want) { - t.Errorf("Accessor.UnmarshalJSON() = %v, want %v", tt.a, tt.want) - } - }) - } -} - -func TestChannelTarget_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - ch *ChannelTarget - args args - want *ChannelTarget - wantErr bool - }{ - {"default", new(ChannelTarget), args{[]byte("{}")}, &ChannelTarget{Node: -1}, false}, - {"empty", new(ChannelTarget), args{[]byte(`{"node": 0}`)}, &ChannelTarget{Node: 0}, false}, - {"nodefault", new(ChannelTarget), args{[]byte(`{"node": 1}`)}, &ChannelTarget{Node: 1}, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := tt.ch.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("ChannelTarget.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(tt.ch, tt.want) { - t.Errorf("ChannelTarget.UnmarshalJSON() = %v, want %v", tt.ch, tt.want) - } - }) - } -} - -func TestChannel_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - ch *Channel - args args - want *Channel - wantErr bool - }{ - {"default", new(Channel), args{[]byte("{}")}, &Channel{Sampler: -1}, false}, - {"empty", new(Channel), args{[]byte(`{"sampler": 0}`)}, &Channel{Sampler: 0}, false}, - {"nodefault", new(Channel), args{[]byte(`{"sampler": 1}`)}, &Channel{Sampler: 1}, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := tt.ch.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("Channel.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(tt.ch, tt.want) { - t.Errorf("Channel.UnmarshalJSON() = %v, want %v", tt.ch, tt.want) - } - }) - } -} - -func TestBufferView_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - b *BufferView - args args - want *BufferView - wantErr bool - }{ - {"default", new(BufferView), args{[]byte("{}")}, &BufferView{Buffer: -1}, false}, - {"empty", new(BufferView), args{[]byte(`{"Buffer": 0}`)}, &BufferView{Buffer: 0}, false}, - {"nodefault", new(BufferView), args{[]byte(`{"Buffer": 1}`)}, &BufferView{Buffer: 1}, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := tt.b.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("BufferView.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(tt.b, tt.want) { - t.Errorf("BufferView.UnmarshalJSON() = %v, want %v", tt.b, tt.want) - } - }) - } -} - -func TestTextureInfo_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - t *TextureInfo - args args - want *TextureInfo - wantErr bool - }{ - {"default", new(TextureInfo), args{[]byte("{}")}, &TextureInfo{Index: -1}, false}, - {"empty", new(TextureInfo), args{[]byte(`{"index": 0}`)}, &TextureInfo{Index: 0}, false}, - {"nodefault", new(TextureInfo), args{[]byte(`{"index": 1}`)}, &TextureInfo{Index: 1}, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := tt.t.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("TextureInfo.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(tt.t, tt.want) { - t.Errorf("TextureInfo.UnmarshalJSON() = %v, want %v", tt.t, tt.want) - } - }) - } -} - -func TestSkin_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - s *Skin - args args - want *Skin - wantErr bool - }{ - {"default", new(Skin), args{[]byte("{}")}, &Skin{InverseBindMatrices: -1, Skeleton: -1}, false}, - {"empty", new(Skin), args{[]byte(`{"InverseBindMatrices": 0, "skeleton": 0}`)}, &Skin{InverseBindMatrices: 0, Skeleton: 0}, false}, - {"nodefault", new(Skin), args{[]byte(`{"InverseBindMatrices": 1, "skeleton": 2}`)}, &Skin{InverseBindMatrices: 1, Skeleton: 2}, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := tt.s.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("Skin.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(tt.s, tt.want) { - t.Errorf("Skin.UnmarshalJSON() = %v, want %v", tt.s, tt.want) - } - }) - } -} - -func TestTexture_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - t *Texture - args args - want *Texture - wantErr bool - }{ - {"default", new(Texture), args{[]byte("{}")}, &Texture{Sampler: -1, Source: -1}, false}, - {"empty", new(Texture), args{[]byte(`{"sampler": 0, "source": 0}`)}, &Texture{Sampler: 0, Source: 0}, false}, - {"nodefault", new(Texture), args{[]byte(`{"sampler": 1, "source": 2}`)}, &Texture{Sampler: 1, Source: 2}, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := tt.t.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("Texture.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(tt.t, tt.want) { - t.Errorf("Texture.UnmarshalJSON() = %v, want %v", tt.t, tt.want) - } - }) - } -} - -func TestDocument_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - d *Document - args args - want *Document - wantErr bool - }{ - {"default", new(Document), args{[]byte("{}")}, &Document{Scene: -1}, false}, - {"empty", new(Document), args{[]byte(`{"scene": 0}`)}, &Document{Scene: 0}, false}, - {"nodefault", new(Document), args{[]byte(`{"scene": 1}`)}, &Document{Scene: 1}, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := tt.d.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("Document.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(tt.d, tt.want) { - t.Errorf("Document.UnmarshalJSON() = %v, want %v", tt.d, tt.want) - } - }) - } -} - -func TestDocument_MarshalJSON(t *testing.T) { - tests := []struct { - name string - d *Document - want []byte - wantErr bool - }{ - {"default", &Document{Scene: -1}, []byte(`{"asset":{"version":""}}`), false}, - {"empty", &Document{Scene: 0}, []byte(`{"asset":{"version":""},"scene":0}`), false}, - {"nodefault", &Document{Scene: 1}, []byte(`{"asset":{"version":""},"scene":1}`), false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.d.MarshalJSON() - if (err != nil) != tt.wantErr { - t.Errorf("Document.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Document.MarshalJSON() = %v, want %v", string(got), string(tt.want)) - } - }) - } -} - -func TestAccessor_MarshalJSON(t *testing.T) { - tests := []struct { - name string - a *Accessor - want []byte - wantErr bool - }{ - {"default", &Accessor{BufferView: -1}, []byte(`{"componentType":0,"count":0,"type":""}`), false}, - {"empty", &Accessor{BufferView: 0}, []byte(`{"bufferView":0,"componentType":0,"count":0,"type":""}`), false}, - {"nodefault", &Accessor{BufferView: 1}, []byte(`{"bufferView":1,"componentType":0,"count":0,"type":""}`), false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.a.MarshalJSON() - if (err != nil) != tt.wantErr { - t.Errorf("Accessor.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Accessor.MarshalJSON() = %v, want %v", string(got), string(tt.want)) - } - }) - } -} - -func TestBufferView_MarshalJSON(t *testing.T) { - tests := []struct { - name string - b *BufferView - want []byte - wantErr bool - }{ - {"default", &BufferView{Buffer: -1}, []byte(`{"byteLength":0}`), false}, - {"empty", &BufferView{Buffer: 0}, []byte(`{"buffer":0,"byteLength":0}`), false}, - {"nodefault", &BufferView{Buffer: 1}, []byte(`{"buffer":1,"byteLength":0}`), false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.b.MarshalJSON() - if (err != nil) != tt.wantErr { - t.Errorf("BufferView.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("BufferView.MarshalJSON() = %v, want %v", string(got), string(tt.want)) - } - }) - } -} - func TestNode_MarshalJSON(t *testing.T) { tests := []struct { name string @@ -642,26 +257,23 @@ func TestNode_MarshalJSON(t *testing.T) { 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}, - Camera: -1, - Skin: -1, - Mesh: -1, }, []byte("{}"), false}, {"empty", &Node{ Matrix: [16]float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Rotation: [4]float64{0, 0, 0, 0}, Scale: [3]float64{0, 0, 0}, - Camera: 0, - Skin: 0, - Mesh: 0, + Camera: Index(0), + Skin: Index(0), + Mesh: Index(0), }, []byte(`{"camera":0,"skin":0,"matrix":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"mesh":0,"rotation":[0,0,0,0],"scale":[0,0,0]}`), false}, {"nodefault", &Node{ Matrix: [16]float64{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Rotation: [4]float64{1, 0, 0, 0}, Scale: [3]float64{1, 0, 0}, Translation: [3]float64{1, 0, 0}, - Camera: 1, - Skin: 1, - Mesh: 1, + Camera: Index(1), + Skin: Index(1), + Mesh: Index(1), }, []byte(`{"camera":1,"skin":1,"matrix":[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"mesh":1,"rotation":[1,0,0,0],"scale":[1,0,0],"translation":[1,0,0]}`), false}, } for _, tt := range tests { @@ -678,56 +290,6 @@ func TestNode_MarshalJSON(t *testing.T) { } } -func TestSkin_MarshalJSON(t *testing.T) { - tests := []struct { - name string - s *Skin - want []byte - wantErr bool - }{ - {"default", &Skin{InverseBindMatrices: -1, Skeleton: -1}, []byte(`{"joints":null}`), false}, - {"empty", &Skin{InverseBindMatrices: 0, Skeleton: 0}, []byte(`{"inverseBindMatrices":0,"skeleton":0,"joints":null}`), false}, - {"nodefault", &Skin{InverseBindMatrices: 1, Skeleton: 2}, []byte(`{"inverseBindMatrices":1,"skeleton":2,"joints":null}`), false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.s.MarshalJSON() - if (err != nil) != tt.wantErr { - t.Errorf("Skin.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Skin.MarshalJSON() = %v, want %v", string(got), string(tt.want)) - } - }) - } -} - -func TestPrimitive_MarshalJSON(t *testing.T) { - tests := []struct { - name string - p *Primitive - want []byte - wantErr bool - }{ - {"default", &Primitive{Indices: -1, Material: -1, Mode: Triangles}, []byte(`{"attributes":null,"mode":4}`), false}, - {"empty", &Primitive{Indices: 0, Material: 0, Mode: Points}, []byte(`{"attributes":null,"indices":0,"material":0,"mode":0}`), false}, - {"nodefault", &Primitive{Indices: 1, Material: 2, Mode: TriangleStrip}, []byte(`{"attributes":null,"indices":1,"material":2,"mode":5}`), false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.p.MarshalJSON() - if (err != nil) != tt.wantErr { - t.Errorf("Primitive.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Primitive.MarshalJSON() = %v, want %v", string(got), string(tt.want)) - } - }) - } -} - func TestMaterial_MarshalJSON(t *testing.T) { tests := []struct { name string @@ -760,9 +322,9 @@ func TestNormalTexture_MarshalJSON(t *testing.T) { want []byte wantErr bool }{ - {"default", &NormalTexture{Index: -1, Scale: -1}, []byte(`{}`), false}, - {"empty", &NormalTexture{Index: 0, Scale: 0}, []byte(`{"index":0,"scale":0}`), false}, - {"nodefault", &NormalTexture{Index: 1, Scale: 1}, []byte(`{"index":1,"scale":1}`), false}, + {"default", &NormalTexture{Scale: 1}, []byte(`{}`), false}, + {"empty", &NormalTexture{Index: Index(0), Scale: 0}, []byte(`{"index":0,"scale":0}`), false}, + {"nodefault", &NormalTexture{Index: Index(1), Scale: 0.5}, []byte(`{"index":1,"scale":0.5}`), false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -785,9 +347,9 @@ func TestOcclusionTexture_MarshalJSON(t *testing.T) { want []byte wantErr bool }{ - {"default", &OcclusionTexture{Index: -1, Strength: 1}, []byte(`{}`), false}, - {"empty", &OcclusionTexture{Index: 0, Strength: 0}, []byte(`{"index":0,"strength":0}`), false}, - {"nodefault", &OcclusionTexture{Index: 1, Strength: 0.5}, []byte(`{"index":1,"strength":0.5}`), false}, + {"default", &OcclusionTexture{Strength: 1}, []byte(`{}`), false}, + {"empty", &OcclusionTexture{Index: Index(0), Strength: 0}, []byte(`{"index":0,"strength":0}`), false}, + {"nodefault", &OcclusionTexture{Index: Index(1), Strength: 0.5}, []byte(`{"index":1,"strength":0.5}`), false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -828,103 +390,51 @@ func TestPBRMetallicRoughness_MarshalJSON(t *testing.T) { } } -func TestTextureInfo_MarshalJSON(t *testing.T) { - tests := []struct { - name string - t *TextureInfo - want []byte - wantErr bool - }{ - {"default", &TextureInfo{Index: -1}, []byte(`{}`), false}, - {"empty", &TextureInfo{Index: 0}, []byte(`{"index":0}`), false}, - {"nodefault", &TextureInfo{Index: 1}, []byte(`{"index":1}`), false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.t.MarshalJSON() - if (err != nil) != tt.wantErr { - t.Errorf("TextureInfo.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("TextureInfo.MarshalJSON() = %v, want %v", string(got), string(tt.want)) - } - }) - } -} - -func TestChannel_MarshalJSON(t *testing.T) { +func TestBuffer_marshalData(t *testing.T) { tests := []struct { name string - ch *Channel - want []byte + b *Buffer + want []uint8 wantErr bool }{ - {"default", &Channel{Sampler: -1, Target: ChannelTarget{Node: -1}}, []byte(`{"target":{"path":""}}`), false}, - {"empty", &Channel{Sampler: 0, Target: ChannelTarget{Node: 0}}, []byte(`{"sampler":0,"target":{"node":0,"path":""}}`), false}, - {"nodefault", &Channel{Sampler: 1, Target: ChannelTarget{Node: 1}}, []byte(`{"sampler":1,"target":{"node":1,"path":""}}`), false}, + {"error", &Buffer{URI: "data:application/octet-stream;base64,_"}, []uint8{}, true}, + {"external", &Buffer{URI: "http://web.com"}, []uint8{}, false}, + {"empty", &Buffer{URI: "data:application/octet-stream;base64,"}, []uint8{}, false}, + {"test", &Buffer{URI: "data:application/octet-stream;base64,TEST"}, []uint8{76, 68, 147}, false}, + {"complex", &Buffer{URI: "data:application/octet-stream;base64,YW55IGNhcm5hbCBwbGVhcw=="}, []uint8{97, 110, 121, 32, 99, 97, 114, 110, 97, 108, 32, 112, 108, 101, 97, 115}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := tt.ch.MarshalJSON() + got, err := tt.b.marshalData() if (err != nil) != tt.wantErr { - t.Errorf("Channel.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("Buffer.marshalData() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Channel.MarshalJSON() = %v, want %v", string(got), string(tt.want)) + t.Errorf("Buffer.marshalData() = %v, want %v", got, tt.want) } }) } } -func TestTexture_MarshalJSON(t *testing.T) { +func TestCamera_MarshalJSON(t *testing.T) { tests := []struct { name string - t *Texture + c *Camera want []byte wantErr bool }{ - {"default", &Texture{Sampler: -1, Source: -1}, []byte(`{}`), false}, - {"empty", &Texture{Sampler: 0, Source: 0}, []byte(`{"sampler":0,"source":0}`), false}, - {"nodefault", &Texture{Sampler: 1, Source: 1}, []byte(`{"sampler":1,"source":1}`), false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.t.MarshalJSON() - if (err != nil) != tt.wantErr { - t.Errorf("Texture.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Texture.MarshalJSON() = %v, want %v", string(got), string(tt.want)) - } - }) - } -} - -func TestBuffer_marshalData(t *testing.T) { - tests := []struct { - name string - b *Buffer - want []uint8 - wantErr bool - }{ - {"error", &Buffer{URI: "data:application/octet-stream;base64,_"}, []uint8{}, true}, - {"external", &Buffer{URI: "http://web.com"}, []uint8{}, false}, - {"empty", &Buffer{URI: "data:application/octet-stream;base64,"}, []uint8{}, false}, - {"test", &Buffer{URI: "data:application/octet-stream;base64,TEST"}, []uint8{76, 68, 147}, false}, - {"complex", &Buffer{URI: "data:application/octet-stream;base64,YW55IGNhcm5hbCBwbGVhcw=="}, []uint8{97, 110, 121, 32, 99, 97, 114, 110, 97, 108, 32, 112, 108, 101, 97, 115}, false}, + {"empty", &Camera{}, nil, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := tt.b.marshalData() + got, err := tt.c.MarshalJSON() if (err != nil) != tt.wantErr { - t.Errorf("Buffer.marshalData() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("Camera.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Buffer.marshalData() = %v, want %v", got, tt.want) + t.Errorf("Camera.MarshalJSON() = %v, want %v", got, tt.want) } }) } diff --git a/validator_test.go b/validator_test.go index 335bfca..7d7c7f8 100644 --- a/validator_test.go +++ b/validator_test.go @@ -14,20 +14,20 @@ func TestValidateDocument(t *testing.T) { }{ {"Document.Asset.Version", new(Document), true}, {"Document.Accessors[0].ComponentType", &Document{Asset: Asset{Version: "1.0"}, - Accessors: []Accessor{{ComponentType: 1, Count: 1, Type: "SCALAR"}}}, true}, + Accessors: []Accessor{{ComponentType: 10, Count: 1}}}, true}, {"Document.Accessors[0].Count", &Document{Asset: Asset{Version: "1.0"}, - Accessors: []Accessor{{ComponentType: Byte, Count: 0, Type: "SCALAR"}}}, true}, + Accessors: []Accessor{{ComponentType: Byte, Count: 0}}}, true}, {"Document.Accessors[0].Type", &Document{Asset: Asset{Version: "1.0"}, - Accessors: []Accessor{{ComponentType: Byte, Count: 1, Type: "OTHER"}}}, true}, + Accessors: []Accessor{{ComponentType: Byte, Count: 1, Type: 10}}}, true}, {"Document.Accessors[0].Max", &Document{Asset: Asset{Version: "1.0"}, - Accessors: []Accessor{{ComponentType: Byte, Count: 1, Type: "SCALAR", Max: make([]float64, 17)}}}, true}, + Accessors: []Accessor{{ComponentType: Byte, Count: 1, Max: make([]float64, 17)}}}, true}, {"Document.Accessors[0].Min", &Document{Asset: Asset{Version: "1.0"}, - Accessors: []Accessor{{ComponentType: Byte, Count: 1, Type: "SCALAR", Min: make([]float64, 17)}}}, true}, + Accessors: []Accessor{{ComponentType: Byte, Count: 1, Min: make([]float64, 17)}}}, true}, {"Document.Accessors[0].Sparse.Count", &Document{Asset: Asset{Version: "1.0"}, - Accessors: []Accessor{{ComponentType: Byte, Count: 1, Type: "SCALAR", + Accessors: []Accessor{{ComponentType: Byte, Count: 1, Sparse: &Sparse{Count: 0}}}}, true}, {"Document.Accessors[0].Sparse.Indices.ComponentType", &Document{Asset: Asset{Version: "1.0"}, - Accessors: []Accessor{{ComponentType: Byte, Count: 1, Type: "SCALAR", + Accessors: []Accessor{{ComponentType: Byte, Count: 1, Sparse: &Sparse{Count: 1, Indices: SparseIndices{ComponentType: 1}}}}}, true}, {"Document.Buffers[0].URI", &Document{Asset: Asset{Version: "1.0"}, Buffers: []Buffer{{ByteLength: 1, URI: "a.bin"}}}, false}, @@ -51,12 +51,10 @@ func TestValidateDocument(t *testing.T) { Nodes: []Node{{Rotation: [4]float64{1, -2, 1, 1}}}}, true}, {"Document.Skins[0].Joints", &Document{Asset: Asset{Version: "1.0"}, Skins: []Skin{{Joints: []uint32{1, 1}}}}, true}, - {"Document.Cameras[0].Type", &Document{Asset: Asset{Version: "1.0"}, - Cameras: []Camera{{Type: "incorrect"}}}, true}, {"Document.Cameras[0].Orthographic.Znear", &Document{Asset: Asset{Version: "1.0"}, - Cameras: []Camera{{Type: OrthographicType, Orthographic: &Orthographic{Znear: -1, Zfar: 1}}}}, true}, + Cameras: []Camera{{Orthographic: &Orthographic{Znear: -1, Zfar: 1}}}}, true}, {"Document.Cameras[0].Orthographic.Zfar", &Document{Asset: Asset{Version: "1.0"}, - Cameras: []Camera{{Type: OrthographicType, Orthographic: &Orthographic{Znear: 1, Zfar: 1}}}}, true}, + Cameras: []Camera{{Orthographic: &Orthographic{Znear: 1, Zfar: 1}}}}, true}, {"Document.Meshes[0].Primitives", &Document{Asset: Asset{Version: "1.0"}, Meshes: []Mesh{{Primitives: make([]Primitive, 0)}}}, true}, {"Document.Meshes[0].Primitives[0].Mode", &Document{Asset: Asset{Version: "1.0"}, @@ -68,7 +66,7 @@ func TestValidateDocument(t *testing.T) { {"Document.Materials[0].EmissiveFactor[1]", &Document{Asset: Asset{Version: "1.0"}, Materials: []Material{{AlphaMode: Opaque, EmissiveFactor: [3]float64{1, 2, 1}}}}, true}, {"Document.Materials[0].AlphaMode", &Document{Asset: Asset{Version: "1.0"}, - Materials: []Material{{AlphaMode: "OTHER"}}}, true}, + Materials: []Material{{AlphaMode: 5}}}, true}, {"Document.Materials[0].PBRMetallicRoughness.BaseColorFactor[0]", &Document{Asset: Asset{Version: "1.0"}, Materials: []Material{{AlphaMode: Opaque, PBRMetallicRoughness: &PBRMetallicRoughness{BaseColorFactor: [4]float64{-1, 0, 0, 0}}}}}, true}, @@ -88,13 +86,13 @@ func TestValidateDocument(t *testing.T) { Materials: []Material{{AlphaMode: Opaque, PBRMetallicRoughness: &PBRMetallicRoughness{RoughnessFactor: -1}}}}, true}, {"Document.Samplers[0].MagFilter", &Document{Asset: Asset{Version: "1.0"}, - Samplers: []Sampler{{MagFilter: 1, MinFilter: MinLinear, WrapS: ClampToEdge, WrapT: ClampToEdge}}}, true}, + Samplers: []Sampler{{MagFilter: 10, MinFilter: MinLinear, WrapS: ClampToEdge, WrapT: ClampToEdge}}}, true}, {"Document.Samplers[0].MinFilter", &Document{Asset: Asset{Version: "1.0"}, - Samplers: []Sampler{{MagFilter: MagLinear, MinFilter: 1, WrapS: ClampToEdge, WrapT: ClampToEdge}}}, true}, + Samplers: []Sampler{{MagFilter: MagLinear, MinFilter: 10, WrapS: ClampToEdge, WrapT: ClampToEdge}}}, true}, {"Document.Samplers[0].WrapS", &Document{Asset: Asset{Version: "1.0"}, - Samplers: []Sampler{{MagFilter: MagLinear, MinFilter: MinLinear, WrapS: 1, WrapT: ClampToEdge}}}, true}, + Samplers: []Sampler{{MagFilter: MagLinear, MinFilter: MinLinear, WrapS: 10, WrapT: ClampToEdge}}}, true}, {"Document.Samplers[0].WrapT", &Document{Asset: Asset{Version: "1.0"}, - Samplers: []Sampler{{MagFilter: MagLinear, MinFilter: MinLinear, WrapS: ClampToEdge, WrapT: 1}}}, true}, + Samplers: []Sampler{{MagFilter: MagLinear, MinFilter: MinLinear, WrapS: ClampToEdge, WrapT: 10}}}, true}, {"Document.Images[0].URI", &Document{Asset: Asset{Version: "1.0"}, Images: []Image{{URI: "a.png"}}}, false}, {"Document.Images[0].MimeType", &Document{Asset: Asset{Version: "1.0"}, @@ -102,10 +100,10 @@ func TestValidateDocument(t *testing.T) { {"Document.Animations[0].Channels", &Document{Asset: Asset{Version: "1.0"}, Animations: []Animation{{Samplers: []AnimationSampler{{}}}}}, true}, {"Document.Animations[0].Channels[0].Target.Path", &Document{Asset: Asset{Version: "1.0"}, - Animations: []Animation{{Channels: []Channel{{Target: ChannelTarget{Path: "other"}}}, Samplers: []AnimationSampler{{}}}}}, true}, + Animations: []Animation{{Channels: []Channel{{Target: ChannelTarget{Path: 10}}}, Samplers: []AnimationSampler{{}}}}}, true}, {"Document.Animations[0].Samplers[0].Interpolation", &Document{Asset: Asset{Version: "1.0"}, - Animations: []Animation{{Channels: []Channel{{Target: ChannelTarget{Path: "translation"}}}, - Samplers: []AnimationSampler{{Interpolation: "OTHER"}}}}}, true}, + Animations: []Animation{{Channels: []Channel{{Target: ChannelTarget{Path: Translation}}}, + Samplers: []AnimationSampler{{Interpolation: 10}}}}}, true}, {"ok", &Document{ ExtensionsUsed: []string{"one", "another"}, ExtensionsRequired: []string{"that", "this"}, From eed3107fb08f78f08c79f80bfbcaf9eff5434e6a Mon Sep 17 00:00:00 2001 From: Quim Muntal Date: Mon, 25 Mar 2019 07:20:51 +0100 Subject: [PATCH 2/8] node correctly manages zeros array --- struct.go | 23 ++++++++++++++--------- struct_test.go | 7 ++++++- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/struct.go b/struct.go index 30cbea9..fee6641 100644 --- a/struct.go +++ b/struct.go @@ -156,19 +156,15 @@ type Node struct { Weights []float64 `json:"weights,omitempty"` // The weights of the instantiated Morph Target. } -// NewNode returns a default Node. -func NewNode() *Node { - return &Node{ +// UnmarshalJSON unmarshal the node with the correct default values. +func (n *Node) UnmarshalJSON(data []byte) error { + type alias Node + def := Node{ 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}, } -} - -// UnmarshalJSON unmarshal the node with the correct default values. -func (n *Node) UnmarshalJSON(data []byte) error { - type alias Node - tmp := alias(*NewNode()) + tmp := alias(def) err := json.Unmarshal(data, &tmp) if err == nil { *n = Node(tmp) @@ -183,13 +179,22 @@ func (n *Node) MarshalJSON() ([]byte, error) { if err == nil { if n.Matrix == [16]float64{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1} { out = removeProperty([]byte(`"matrix":[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]`), out) + } else if n.Matrix == [16]float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} { + out = removeProperty([]byte(`"matrix":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]`), out) } + if n.Rotation == [4]float64{0, 0, 0, 1} { out = removeProperty([]byte(`"rotation":[0,0,0,1]`), out) + } else if n.Rotation == [4]float64{0, 0, 0, 0} { + out = removeProperty([]byte(`"rotation":[0,0,0,0]`), out) } + if n.Scale == [3]float64{1, 1, 1} { out = removeProperty([]byte(`"scale":[1,1,1]`), out) + } else if n.Scale == [3]float64{0, 0, 0} { + out = removeProperty([]byte(`"scale":[0,0,0]`), out) } + if n.Translation == [3]float64{0, 0, 0} { out = removeProperty([]byte(`"translation":[0,0,0]`), out) } diff --git a/struct_test.go b/struct_test.go index 67a90ad..88f2d52 100644 --- a/struct_test.go +++ b/struct_test.go @@ -258,6 +258,11 @@ func TestNode_MarshalJSON(t *testing.T) { Rotation: [4]float64{0, 0, 0, 1}, Scale: [3]float64{1, 1, 1}, }, []byte("{}"), false}, + {"default2", &Node{ + Matrix: [16]float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + Rotation: [4]float64{0, 0, 0, 0}, + Scale: [3]float64{0, 0, 0}, + }, []byte("{}"), false}, {"empty", &Node{ Matrix: [16]float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Rotation: [4]float64{0, 0, 0, 0}, @@ -265,7 +270,7 @@ func TestNode_MarshalJSON(t *testing.T) { Camera: Index(0), Skin: Index(0), Mesh: Index(0), - }, []byte(`{"camera":0,"skin":0,"matrix":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"mesh":0,"rotation":[0,0,0,0],"scale":[0,0,0]}`), false}, + }, []byte(`{"camera":0,"skin":0,"mesh":0}`), false}, {"nodefault", &Node{ Matrix: [16]float64{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Rotation: [4]float64{1, 0, 0, 0}, From 18c01464ff4e1c750f2c89ce2d34386a2cbe17a6 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 25 Mar 2019 09:26:37 +0100 Subject: [PATCH 3/8] improve node and pbr defaults --- const.go | 17 ++++++++ decoder_test.go | 4 +- doc.go | 4 +- encode_test.go | 2 +- struct.go | 89 +++++++++++++++++++++++++++++++++------ struct_test.go | 103 +++++++++++++++++++++++++++++++++++++++++++--- validator_test.go | 8 ++-- 7 files changed, 200 insertions(+), 27 deletions(-) diff --git a/const.go b/const.go index 5747ac3..0f2e47d 100644 --- a/const.go +++ b/const.go @@ -2,6 +2,23 @@ package gltf import "encoding/json" +var ( + // DefaultMatrix defines an identity matrix. + DefaultMatrix = [16]float64{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1} + // DefaultRotation defines a quaternion without rotation. + DefaultRotation = [4]float64{0, 0, 0, 1} + // DefaultScale defines a scaling that does not modify the size of the object. + DefaultScale = [3]float64{1, 1, 1} + // DefaultTranslation defines a translation that does not move the object. + DefaultTranslation = [3]float64{0, 0, 0} +) + +var ( + emptyMatrix = [16]float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + emptyRotation = [4]float64{0, 0, 0, 0} + emptyScale = [3]float64{0, 0, 0} +) + // The ComponentType is the datatype of components in the attribute. All valid values correspond to WebGL enums. // 5125 (UNSIGNED_INT) is only allowed when the accessor contains indices. type ComponentType uint16 diff --git a/decoder_test.go b/decoder_test.go index e05c4f9..5802224 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: 0.5, PBRMetallicRoughness: &PBRMetallicRoughness{BaseColorFactor: [4]float64{1, 1, 1, 1}, MetallicFactor: 1, RoughnessFactor: 1, BaseColorTexture: &TextureInfo{Index: Index(0)}, MetallicRoughnessTexture: &TextureInfo{Index: Index(1)}}}}, + Materials: []Material{{Name: "Cube", AlphaMode: Opaque, AlphaCutoff: 0.5, PBRMetallicRoughness: &PBRMetallicRoughness{BaseColorFactor: NewRGBA(), MetallicFactor: 1, RoughnessFactor: 1, BaseColorTexture: &TextureInfo{Index: Index(0)}, MetallicRoughnessTexture: &TextureInfo{Index: 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}}, @@ -95,7 +95,7 @@ func TestOpen(t *testing.T) { {Buffer: 0, ByteLength: 192, ByteOffset: 1032, Target: ArrayBuffer}, }, Buffers: []Buffer{{ByteLength: 1224, Data: readFile("testdata/BoxVertexColors/glTF-Binary/BoxVertexColors.glb")[1628+20+8:]}}, - Materials: []Material{{Name: "Default", AlphaMode: Opaque, AlphaCutoff: 0.5, PBRMetallicRoughness: &PBRMetallicRoughness{BaseColorFactor: [4]float64{0.8, 0.8, 0.8, 1}, MetallicFactor: 0.1, RoughnessFactor: 0.99}}}, + Materials: []Material{{Name: "Default", AlphaMode: Opaque, AlphaCutoff: 0.5, PBRMetallicRoughness: &PBRMetallicRoughness{BaseColorFactor: &RGBA{R: 0.8, G: 0.8, B: 0.8, A: 1}, MetallicFactor: 0.1, RoughnessFactor: 0.99}}}, Meshes: []Mesh{{Name: "Cube", Primitives: []Primitive{{Indices: Index(0), Material: Index(0), Mode: Triangles, Attributes: map[string]uint32{"POSITION": 1, "COLOR_0": 3, "NORMAL": 2, "TEXCOORD_0": 4}}}}}, Nodes: []Node{ {Name: "RootNode", Children: []uint32{1, 2, 3}, 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}}, diff --git a/doc.go b/doc.go index 1eda2e9..743fdd9 100644 --- a/doc.go +++ b/doc.go @@ -1,6 +1,6 @@ /* -Package gltf implements a glTF 2.0 file decoder, encoder and validator. + Package gltf implements a glTF 2.0 file decoder, encoder and validator. -The glTF 2.0 specification is at https://github.com/KhronosGroup/glTF/tree/master/specification/2.0/. + The glTF 2.0 specification is at https://github.com/KhronosGroup/glTF/tree/master/specification/2.0/. */ package gltf diff --git a/encode_test.go b/encode_test.go index 09bc974..40f3987 100644 --- a/encode_test.go +++ b/encode_test.go @@ -87,7 +87,7 @@ func TestEncoder_Encode(t *testing.T) { {Extras: 8.0, Name: "base", EmissiveFactor: [3]float64{1.0, 1.0, 1.0}, DoubleSided: true, AlphaCutoff: 0.5, AlphaMode: Opaque}, {Extras: 8.0, Name: "pbr", AlphaCutoff: 0.5, AlphaMode: Opaque, PBRMetallicRoughness: &PBRMetallicRoughness{ - Extras: 8.0, MetallicFactor: 1, RoughnessFactor: 2, BaseColorFactor: [4]float64{1, 2, 3, 4}, + Extras: 8.0, MetallicFactor: 1, RoughnessFactor: 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}, }, diff --git a/struct.go b/struct.go index fee6641..66c562e 100644 --- a/struct.go +++ b/struct.go @@ -156,13 +156,42 @@ type Node struct { Weights []float64 `json:"weights,omitempty"` // The weights of the instantiated Morph Target. } +// MatrixOrDefault returns the node matrix if it represents a valid affine matrix, else return the default one. +func (n *Node) MatrixOrDefault() [16]float64 { + if n.Matrix == emptyMatrix { + return DefaultMatrix + } + return n.Matrix +} + +// RotationOrDefault returns the node rotation if it represents a valid quaternion, else return the default one. +func (n *Node) RotationOrDefault() [4]float64 { + if n.Rotation == emptyRotation { + return DefaultRotation + } + return n.Rotation +} + +// ScaleOrDefault returns the node scale if it represents a valid scale factor, else return the default one. +func (n *Node) ScaleOrDefault() [3]float64 { + if n.Scale == emptyScale { + return DefaultScale + } + return n.Scale +} + +// TranslationOrDefault returns the node translation. +func (n *Node) TranslationOrDefault() [3]float64 { + return n.Translation +} + // UnmarshalJSON unmarshal the node with the correct default values. func (n *Node) UnmarshalJSON(data []byte) error { type alias Node def := Node{ - 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}, + Matrix: DefaultMatrix, + Rotation: DefaultRotation, + Scale: DefaultScale, } tmp := alias(def) err := json.Unmarshal(data, &tmp) @@ -177,25 +206,25 @@ func (n *Node) MarshalJSON() ([]byte, error) { type alias Node out, err := json.Marshal(&struct{ *alias }{alias: (*alias)(n)}) if err == nil { - if n.Matrix == [16]float64{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1} { + if n.Matrix == DefaultMatrix { out = removeProperty([]byte(`"matrix":[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]`), out) - } else if n.Matrix == [16]float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} { + } else if n.Matrix == emptyMatrix { out = removeProperty([]byte(`"matrix":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]`), out) } - if n.Rotation == [4]float64{0, 0, 0, 1} { + if n.Rotation == DefaultRotation { out = removeProperty([]byte(`"rotation":[0,0,0,1]`), out) - } else if n.Rotation == [4]float64{0, 0, 0, 0} { + } else if n.Rotation == emptyRotation { out = removeProperty([]byte(`"rotation":[0,0,0,0]`), out) } - if n.Scale == [3]float64{1, 1, 1} { + if n.Scale == DefaultScale { out = removeProperty([]byte(`"scale":[1,1,1]`), out) - } else if n.Scale == [3]float64{0, 0, 0} { + } else if n.Scale == emptyScale { out = removeProperty([]byte(`"scale":[0,0,0]`), out) } - if n.Translation == [3]float64{0, 0, 0} { + if n.Translation == DefaultTranslation { out = removeProperty([]byte(`"translation":[0,0,0]`), out) } out = sanitizeJSON(out) @@ -412,11 +441,37 @@ func (o *OcclusionTexture) MarshalJSON() ([]byte, error) { return json.Marshal(&struct{ *alias }{alias: (*alias)(o)}) } +// The RGBA components of the base color of the material. +// Each element must be greater than or equal to 0 and less than or equal to 1. +type RGBA struct { + R, G, B, A float64 `validate:"gte=0,lte=1"` +} + +// NewRGBA returns a default RGBA color. +func NewRGBA() *RGBA { + return &RGBA{1, 1, 1, 1} +} + +// UnmarshalJSON unmarshal the color with the correct default values. +func (c *RGBA) UnmarshalJSON(data []byte) error { + tmp := [4]float64{1, 1, 1, 1} + err := json.Unmarshal(data, &tmp) + if err == nil { + c.R, c.G, c.B, c.A = tmp[0], tmp[1], tmp[2], tmp[3] + } + return err +} + +// MarshalJSON marshal the color with the correct default values. +func (c *RGBA) MarshalJSON() ([]byte, error) { + return json.Marshal([4]float64{c.R, c.G, c.B, c.A}) +} + // 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"` Extras interface{} `json:"extras,omitempty"` - BaseColorFactor [4]float64 `json:"baseColorFactor" validate:"dive,gte=0,lte=1"` + BaseColorFactor *RGBA `json:"baseColorFactor,omitempty"` BaseColorTexture *TextureInfo `json:"baseColorTexture,omitempty"` MetallicFactor float64 `json:"metallicFactor" validate:"gte=0,lte=1"` RoughnessFactor float64 `json:"roughnessFactor" validate:"gte=0,lte=1"` @@ -425,7 +480,15 @@ type PBRMetallicRoughness struct { // NewPBRMetallicRoughness returns a default PBRMetallicRoughness. func NewPBRMetallicRoughness() *PBRMetallicRoughness { - return &PBRMetallicRoughness{BaseColorFactor: [4]float64{1, 1, 1, 1}, MetallicFactor: 1, RoughnessFactor: 1} + return &PBRMetallicRoughness{BaseColorFactor: NewRGBA(), MetallicFactor: 1, RoughnessFactor: 1} +} + +// BaseColorFactorOrDefault returns the base color factor if it is not nil, else return the default one. +func (p *PBRMetallicRoughness) BaseColorFactorOrDefault() RGBA { + if p.BaseColorFactor == nil { + return *NewRGBA() + } + return *p.BaseColorFactor } // UnmarshalJSON unmarshal the pbr with the correct default values. @@ -450,7 +513,7 @@ func (p *PBRMetallicRoughness) MarshalJSON() ([]byte, error) { if p.RoughnessFactor == 1 { out = removeProperty([]byte(`"roughnessFactor":1`), out) } - if p.BaseColorFactor == [4]float64{1, 1, 1, 1} { + if p.BaseColorFactor != nil && *p.BaseColorFactor == *NewRGBA() { out = removeProperty([]byte(`"baseColorFactor":[1,1,1,1]`), out) } out = sanitizeJSON(out) diff --git a/struct_test.go b/struct_test.go index 88f2d52..35d6a57 100644 --- a/struct_test.go +++ b/struct_test.go @@ -228,9 +228,9 @@ func TestPBRMetallicRoughness_UnmarshalJSON(t *testing.T) { want *PBRMetallicRoughness wantErr bool }{ - {"default", new(PBRMetallicRoughness), args{[]byte("{}")}, &PBRMetallicRoughness{BaseColorFactor: [4]float64{1, 1, 1, 1}, MetallicFactor: 1, RoughnessFactor: 1}, false}, + {"default", new(PBRMetallicRoughness), args{[]byte("{}")}, &PBRMetallicRoughness{BaseColorFactor: NewRGBA(), MetallicFactor: 1, RoughnessFactor: 1}, false}, {"nodefault", new(PBRMetallicRoughness), args{[]byte(`{"baseColorFactor": [0.1,0.2,0.6,0.7],"metallicFactor":0.5,"roughnessFactor":0.1}`)}, &PBRMetallicRoughness{ - BaseColorFactor: [4]float64{0.1, 0.2, 0.6, 0.7}, MetallicFactor: 0.5, RoughnessFactor: 0.1, + BaseColorFactor: &RGBA{R: 0.1, G: 0.2, B: 0.6, A: 0.7}, MetallicFactor: 0.5, RoughnessFactor: 0.1, }, false}, } for _, tt := range tests { @@ -377,9 +377,9 @@ func TestPBRMetallicRoughness_MarshalJSON(t *testing.T) { want []byte wantErr bool }{ - {"default", &PBRMetallicRoughness{MetallicFactor: 1, RoughnessFactor: 1, BaseColorFactor: [4]float64{1, 1, 1, 1}}, []byte(`{}`), false}, - {"empty", &PBRMetallicRoughness{MetallicFactor: 0, RoughnessFactor: 0, BaseColorFactor: [4]float64{0, 0, 0, 0}}, []byte(`{"baseColorFactor":[0,0,0,0],"metallicFactor":0,"roughnessFactor":0}`), false}, - {"nodefault", &PBRMetallicRoughness{MetallicFactor: 0.5, RoughnessFactor: 0.5, BaseColorFactor: [4]float64{1, 0.5, 1, 1}}, []byte(`{"baseColorFactor":[1,0.5,1,1],"metallicFactor":0.5,"roughnessFactor":0.5}`), false}, + {"default", &PBRMetallicRoughness{MetallicFactor: 1, RoughnessFactor: 1, BaseColorFactor: NewRGBA()}, []byte(`{}`), false}, + {"empty", &PBRMetallicRoughness{MetallicFactor: 0, RoughnessFactor: 0}, []byte(`{"metallicFactor":0,"roughnessFactor":0}`), false}, + {"nodefault", &PBRMetallicRoughness{MetallicFactor: 0.5, RoughnessFactor: 0.5, BaseColorFactor: &RGBA{R: 1, G: 0.5, B: 1, A: 1}}, []byte(`{"baseColorFactor":[1,0.5,1,1],"metallicFactor":0.5,"roughnessFactor":0.5}`), false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -444,3 +444,96 @@ func TestCamera_MarshalJSON(t *testing.T) { }) } } + +func TestNode_MatrixOrDefault(t *testing.T) { + tests := []struct { + name string + n *Node + want [16]float64 + }{ + {"default", &Node{Matrix: DefaultMatrix}, DefaultMatrix}, + {"zeros", &Node{Matrix: emptyMatrix}, DefaultMatrix}, + {"other", &Node{Matrix: [16]float64{2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2}}, [16]float64{2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.n.MatrixOrDefault(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Node.MatrixOrDefault() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNode_RotationOrDefault(t *testing.T) { + tests := []struct { + name string + n *Node + want [4]float64 + }{ + {"default", &Node{Rotation: DefaultRotation}, DefaultRotation}, + {"zeros", &Node{Rotation: emptyRotation}, DefaultRotation}, + {"other", &Node{Rotation: [4]float64{1, 2, 3, 4}}, [4]float64{1, 2, 3, 4}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.n.RotationOrDefault(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Node.RotationOrDefault() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNode_ScaleOrDefault(t *testing.T) { + tests := []struct { + name string + n *Node + want [3]float64 + }{ + {"default", &Node{Scale: DefaultScale}, DefaultScale}, + {"zeros", &Node{Scale: emptyScale}, DefaultScale}, + {"other", &Node{Scale: [3]float64{1, 2, 3}}, [3]float64{1, 2, 3}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.n.ScaleOrDefault(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Node.ScaleOrDefault() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNode_TranslationOrDefault(t *testing.T) { + tests := []struct { + name string + n *Node + want [3]float64 + }{ + {"default", &Node{Translation: DefaultTranslation}, DefaultTranslation}, + {"other", &Node{Translation: [3]float64{1, 2, 3}}, [3]float64{1, 2, 3}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.n.TranslationOrDefault(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Node.TranslationOrDefault() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestPBRMetallicRoughness_BaseColorFactorOrDefault(t *testing.T) { + tests := []struct { + name string + p *PBRMetallicRoughness + want RGBA + }{ + {"empty", &PBRMetallicRoughness{}, *NewRGBA()}, + {"other", &PBRMetallicRoughness{BaseColorFactor: &RGBA{0.8, 0.8, 0.8, 0.5}}, RGBA{0.8, 0.8, 0.8, 0.5}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.p.BaseColorFactorOrDefault(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("PBRMetallicRoughness.BaseColorFactorOrDefault() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/validator_test.go b/validator_test.go index 7d7c7f8..de1deda 100644 --- a/validator_test.go +++ b/validator_test.go @@ -67,12 +67,12 @@ func TestValidateDocument(t *testing.T) { Materials: []Material{{AlphaMode: Opaque, EmissiveFactor: [3]float64{1, 2, 1}}}}, true}, {"Document.Materials[0].AlphaMode", &Document{Asset: Asset{Version: "1.0"}, Materials: []Material{{AlphaMode: 5}}}, true}, - {"Document.Materials[0].PBRMetallicRoughness.BaseColorFactor[0]", &Document{Asset: Asset{Version: "1.0"}, + {"Document.Materials[0].PBRMetallicRoughness.BaseColorFactor.R", &Document{Asset: Asset{Version: "1.0"}, Materials: []Material{{AlphaMode: Opaque, - PBRMetallicRoughness: &PBRMetallicRoughness{BaseColorFactor: [4]float64{-1, 0, 0, 0}}}}}, true}, - {"Document.Materials[0].PBRMetallicRoughness.BaseColorFactor[1]", &Document{Asset: Asset{Version: "1.0"}, + PBRMetallicRoughness: &PBRMetallicRoughness{BaseColorFactor: &RGBA{R: -0.1, G: 0.8, B: 0.8, A: 1}}}}}, true}, + {"Document.Materials[0].PBRMetallicRoughness.BaseColorFactor.G", &Document{Asset: Asset{Version: "1.0"}, Materials: []Material{{AlphaMode: Opaque, - PBRMetallicRoughness: &PBRMetallicRoughness{BaseColorFactor: [4]float64{1, 2, 0, 0}}}}}, true}, + PBRMetallicRoughness: &PBRMetallicRoughness{BaseColorFactor: &RGBA{R: 1, G: 1.1, B: 0, A: 0}}}}}, true}, {"Document.Materials[0].PBRMetallicRoughness.MetallicFactor", &Document{Asset: Asset{Version: "1.0"}, Materials: []Material{{AlphaMode: Opaque, PBRMetallicRoughness: &PBRMetallicRoughness{MetallicFactor: 2}}}}, true}, From 2395b9dc660597f987fd9b76cf68d45442c76529 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 25 Mar 2019 09:55:42 +0100 Subject: [PATCH 4/8] improve texture defaults --- decoder_test.go | 4 +-- encode_test.go | 16 +++++----- struct.go | 79 ++++++++++++++++++++++++++++++---------------- struct_test.go | 80 ++++++++++++++++++++++++++++++++++------------- validator_test.go | 8 ++--- 5 files changed, 124 insertions(+), 63 deletions(-) diff --git a/decoder_test.go b/decoder_test.go index 5802224..d0d9758 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: 0.5, PBRMetallicRoughness: &PBRMetallicRoughness{BaseColorFactor: NewRGBA(), MetallicFactor: 1, RoughnessFactor: 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: Index(0)}, MetallicRoughnessTexture: &TextureInfo{Index: 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}}, @@ -95,7 +95,7 @@ func TestOpen(t *testing.T) { {Buffer: 0, ByteLength: 192, ByteOffset: 1032, Target: ArrayBuffer}, }, Buffers: []Buffer{{ByteLength: 1224, Data: readFile("testdata/BoxVertexColors/glTF-Binary/BoxVertexColors.glb")[1628+20+8:]}}, - Materials: []Material{{Name: "Default", AlphaMode: Opaque, AlphaCutoff: 0.5, PBRMetallicRoughness: &PBRMetallicRoughness{BaseColorFactor: &RGBA{R: 0.8, G: 0.8, B: 0.8, A: 1}, MetallicFactor: 0.1, RoughnessFactor: 0.99}}}, + Materials: []Material{{Name: "Default", AlphaMode: Opaque, AlphaCutoff: Float64(0.5), PBRMetallicRoughness: &PBRMetallicRoughness{BaseColorFactor: &RGBA{R: 0.8, G: 0.8, B: 0.8, A: 1}, MetallicFactor: Float64(0.1), RoughnessFactor: Float64(0.99)}}}, Meshes: []Mesh{{Name: "Cube", Primitives: []Primitive{{Indices: Index(0), Material: Index(0), Mode: Triangles, Attributes: map[string]uint32{"POSITION": 1, "COLOR_0": 3, "NORMAL": 2, "TEXCOORD_0": 4}}}}}, Nodes: []Node{ {Name: "RootNode", Children: []uint32{1, 2, 3}, 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}}, diff --git a/encode_test.go b/encode_test.go index 40f3987..bb83643 100644 --- a/encode_test.go +++ b/encode_test.go @@ -84,21 +84,21 @@ func TestEncoder_Encode(t *testing.T) { {Extras: 8.0, Name: "external", URI: "https://web.com/a", MimeType: "data:image/png"}, }}}, false}, {"withMaterials", args{&Document{Materials: []Material{ - {Extras: 8.0, Name: "base", EmissiveFactor: [3]float64{1.0, 1.0, 1.0}, DoubleSided: true, AlphaCutoff: 0.5, AlphaMode: Opaque}, - {Extras: 8.0, Name: "pbr", AlphaCutoff: 0.5, AlphaMode: Opaque, + {Extras: 8.0, Name: "base", EmissiveFactor: [3]float64{1.0, 1.0, 1.0}, DoubleSided: true, AlphaCutoff: Float64(0.5), AlphaMode: Opaque}, + {Extras: 8.0, Name: "pbr", AlphaCutoff: Float64(0.5), AlphaMode: Opaque, PBRMetallicRoughness: &PBRMetallicRoughness{ - Extras: 8.0, MetallicFactor: 1, RoughnessFactor: 2, BaseColorFactor: &RGBA{R: 0.8, G: 0.8, B: 0.8, A: 1}, + 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}, }, }, - {Extras: 8.0, Name: "normal", AlphaCutoff: 0.7, AlphaMode: Blend, - NormalTexture: &NormalTexture{Extras: 8.0, Index: Index(1), TexCoord: 2, Scale: 2.0}, + {Extras: 8.0, Name: "normal", AlphaCutoff: Float64(0.7), AlphaMode: Blend, + NormalTexture: &NormalTexture{Extras: 8.0, Index: Index(1), TexCoord: 2, Scale: Float64(2.0)}, }, - {Extras: 8.0, Name: "occlusion", AlphaCutoff: 0.5, AlphaMode: Mask, - OcclusionTexture: &OcclusionTexture{Extras: 8.0, Index: Index(1), TexCoord: 2, Strength: 2.0}, + {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: 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: Index(4), TexCoord: 50}}, }}}, false}, {"withMeshes", args{&Document{Meshes: []Mesh{ {Extras: 8.0, Name: "mesh_1", Weights: []float64{1.2, 2}}, diff --git a/struct.go b/struct.go index 66c562e..d4df437 100644 --- a/struct.go +++ b/struct.go @@ -9,11 +9,16 @@ import ( "strings" ) -// Index is an utility function that returns a pointer to a uin32. +// Index is an utility function that returns a pointer to a uint32. func Index(i uint32) *uint32 { return &i } +// Float64 is an utility function that returns a pointer to a float64. +func Float64(val float64) *float64 { + return &val +} + // An Asset is metadata about the glTF asset. type Asset struct { Extensions Extensions `json:"extensions,omitempty"` @@ -325,19 +330,22 @@ type Material struct { EmissiveTexture *TextureInfo `json:"emissiveTexture,omitempty"` EmissiveFactor [3]float64 `json:"emissiveFactor,omitempty" validate:"dive,gte=0,lte=1"` AlphaMode AlphaMode `json:"alphaMode,omitempty" validate:"lte=2"` - AlphaCutoff float64 `json:"alphaCutoff" validate:"gte=0"` + AlphaCutoff *float64 `json:"alphaCutoff,omitempty" validate:"omitempty,gte=0"` DoubleSided bool `json:"doubleSided,omitempty"` } -// NewMaterial create a default Material. -func NewMaterial() *Material { - return &Material{AlphaCutoff: 0.5} +// AlphaCutoffOrDefault returns the scale if it is not nil, else return the default one. +func (m *Material) AlphaCutoffOrDefault() float64 { + if m.AlphaCutoff == nil { + return 0.5 + } + return *m.AlphaCutoff } // UnmarshalJSON unmarshal the material with the correct default values. func (m *Material) UnmarshalJSON(data []byte) error { type alias Material - tmp := alias(*NewMaterial()) + tmp := alias(Material{AlphaCutoff: Float64(0.5)}) err := json.Unmarshal(data, &tmp) if err == nil { *m = Material(tmp) @@ -350,7 +358,7 @@ func (m *Material) MarshalJSON() ([]byte, error) { type alias Material out, err := json.Marshal(&struct{ *alias }{alias: (*alias)(m)}) if err == nil { - if m.AlphaCutoff == 0.5 { + if m.AlphaCutoff != nil && *m.AlphaCutoff == 0.5 { out = removeProperty([]byte(`"alphaCutoff":0.5`), out) } if m.EmissiveFactor == [3]float64{0, 0, 0} { @@ -367,18 +375,21 @@ type NormalTexture struct { Extras interface{} `json:"extras,omitempty"` Index *uint32 `json:"index,omitempty"` TexCoord uint32 `json:"texCoord,omitempty"` // The index of texture's TEXCOORD attribute used for texture coordinate mapping. - Scale float64 `json:"scale"` + Scale *float64 `json:"scale,omitempty"` } -// NewNormalTexture returns a default NormalTexture. -func NewNormalTexture() *NormalTexture { - return &NormalTexture{Scale: 1} +// ScaleOrDefault returns the scale if it is not nil, else return the default one. +func (n *NormalTexture) ScaleOrDefault() float64 { + if n.Scale == nil { + return 1 + } + return *n.Scale } // UnmarshalJSON unmarshal the texture info with the correct default values. func (n *NormalTexture) UnmarshalJSON(data []byte) error { type alias NormalTexture - tmp := alias(*NewNormalTexture()) + tmp := alias(NormalTexture{Scale: Float64(1)}) err := json.Unmarshal(data, &tmp) if err == nil { *n = NormalTexture(tmp) @@ -389,7 +400,7 @@ func (n *NormalTexture) UnmarshalJSON(data []byte) error { // MarshalJSON marshal the texture info with the correct default values. func (n *NormalTexture) MarshalJSON() ([]byte, error) { type alias NormalTexture - if n.Scale == 1 { + if n.Scale != nil && *n.Scale == 1 { return json.Marshal(&struct { Scale float64 `json:"scale,omitempty"` *alias @@ -407,18 +418,21 @@ type OcclusionTexture struct { Extras interface{} `json:"extras,omitempty"` Index *uint32 `json:"index,omitempty"` TexCoord uint32 `json:"texCoord,omitempty"` // The index of texture's TEXCOORD attribute used for texture coordinate mapping. - Strength float64 `json:"strength" validate:"gte=0,lte=1"` + Strength *float64 `json:"strength,omitempty" validate:"omitempty,gte=0,lte=1"` } -// NewOcclusionTexture returns a default OcclusionTexture. -func NewOcclusionTexture() *OcclusionTexture { - return &OcclusionTexture{Strength: 1} +// StrengthOrDefault returns the strength if it is not nil, else return the default one. +func (o *OcclusionTexture) StrengthOrDefault() float64 { + if o.Strength == nil { + return 1 + } + return *o.Strength } // UnmarshalJSON unmarshal the texture info with the correct default values. func (o *OcclusionTexture) UnmarshalJSON(data []byte) error { type alias OcclusionTexture - tmp := alias(*NewOcclusionTexture()) + tmp := alias(OcclusionTexture{Strength: Float64(1)}) err := json.Unmarshal(data, &tmp) if err == nil { *o = OcclusionTexture(tmp) @@ -429,7 +443,7 @@ func (o *OcclusionTexture) UnmarshalJSON(data []byte) error { // MarshalJSON marshal the texture info with the correct default values. func (o *OcclusionTexture) MarshalJSON() ([]byte, error) { type alias OcclusionTexture - if o.Strength == 1 { + if o.Strength != nil && *o.Strength == 1 { return json.Marshal(&struct { Strength float64 `json:"strength,omitempty"` *alias @@ -473,14 +487,25 @@ type PBRMetallicRoughness struct { Extras interface{} `json:"extras,omitempty"` BaseColorFactor *RGBA `json:"baseColorFactor,omitempty"` BaseColorTexture *TextureInfo `json:"baseColorTexture,omitempty"` - MetallicFactor float64 `json:"metallicFactor" validate:"gte=0,lte=1"` - RoughnessFactor float64 `json:"roughnessFactor" validate:"gte=0,lte=1"` + MetallicFactor *float64 `json:"metallicFactor,omitempty" validate:"omitempty,gte=0,lte=1"` + RoughnessFactor *float64 `json:"roughnessFactor,omitempty" validate:"omitempty,gte=0,lte=1"` MetallicRoughnessTexture *TextureInfo `json:"metallicRoughnessTexture,omitempty"` } -// NewPBRMetallicRoughness returns a default PBRMetallicRoughness. -func NewPBRMetallicRoughness() *PBRMetallicRoughness { - return &PBRMetallicRoughness{BaseColorFactor: NewRGBA(), MetallicFactor: 1, RoughnessFactor: 1} +// MetallicFactorOrDefault returns the metallic factor if it is not nil, else return the default one. +func (p *PBRMetallicRoughness) MetallicFactorOrDefault() float64 { + if p.MetallicFactor == nil { + return 1 + } + return *p.MetallicFactor +} + +// RoughnessFactorOrDefault returns the roughness factor if it is not nil, else return the default one. +func (p *PBRMetallicRoughness) RoughnessFactorOrDefault() float64 { + if p.RoughnessFactor == nil { + return 1 + } + return *p.RoughnessFactor } // BaseColorFactorOrDefault returns the base color factor if it is not nil, else return the default one. @@ -494,7 +519,7 @@ func (p *PBRMetallicRoughness) BaseColorFactorOrDefault() RGBA { // UnmarshalJSON unmarshal the pbr with the correct default values. func (p *PBRMetallicRoughness) UnmarshalJSON(data []byte) error { type alias PBRMetallicRoughness - tmp := alias(*NewPBRMetallicRoughness()) + tmp := alias(PBRMetallicRoughness{BaseColorFactor: NewRGBA(), MetallicFactor: Float64(1), RoughnessFactor: Float64(1)}) err := json.Unmarshal(data, &tmp) if err == nil { *p = PBRMetallicRoughness(tmp) @@ -507,10 +532,10 @@ func (p *PBRMetallicRoughness) MarshalJSON() ([]byte, error) { type alias PBRMetallicRoughness out, err := json.Marshal(&struct{ *alias }{alias: (*alias)(p)}) if err == nil { - if p.MetallicFactor == 1 { + if p.MetallicFactor != nil && *p.MetallicFactor == 1 { out = removeProperty([]byte(`"metallicFactor":1`), out) } - if p.RoughnessFactor == 1 { + if p.RoughnessFactor != nil && *p.RoughnessFactor == 1 { out = removeProperty([]byte(`"roughnessFactor":1`), out) } if p.BaseColorFactor != nil && *p.BaseColorFactor == *NewRGBA() { diff --git a/struct_test.go b/struct_test.go index 35d6a57..3702af4 100644 --- a/struct_test.go +++ b/struct_test.go @@ -145,8 +145,8 @@ func TestMaterial_UnmarshalJSON(t *testing.T) { want *Material wantErr bool }{ - {"default", new(Material), args{[]byte("{}")}, &Material{AlphaCutoff: 0.5, AlphaMode: Opaque}, false}, - {"nodefault", new(Material), args{[]byte(`{"alphaCutoff": 0.2, "alphaMode": "MASK"}`)}, &Material{AlphaCutoff: 0.2, AlphaMode: Mask}, false}, + {"default", new(Material), args{[]byte("{}")}, &Material{AlphaCutoff: Float64(0.5), AlphaMode: Opaque}, false}, + {"nodefault", new(Material), args{[]byte(`{"alphaCutoff": 0.2, "alphaMode": "MASK"}`)}, &Material{AlphaCutoff: Float64(0.2), AlphaMode: Mask}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -172,9 +172,9 @@ func TestNormalTexture_UnmarshalJSON(t *testing.T) { want *NormalTexture wantErr bool }{ - {"default", new(NormalTexture), args{[]byte("{}")}, &NormalTexture{Scale: 1}, false}, - {"empty", new(NormalTexture), args{[]byte(`{"scale": 0, "index": 0}`)}, &NormalTexture{Scale: 0, Index: Index(0)}, false}, - {"nodefault", new(NormalTexture), args{[]byte(`{"scale": 0.5, "index":2}`)}, &NormalTexture{Scale: 0.5, Index: Index(2)}, false}, + {"default", new(NormalTexture), args{[]byte("{}")}, &NormalTexture{Scale: Float64(1)}, false}, + {"empty", new(NormalTexture), args{[]byte(`{"scale": 0, "index": 0}`)}, &NormalTexture{Scale: Float64(0), Index: Index(0)}, false}, + {"nodefault", new(NormalTexture), args{[]byte(`{"scale": 0.5, "index":2}`)}, &NormalTexture{Scale: Float64(0.5), Index: Index(2)}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -200,9 +200,9 @@ func TestOcclusionTexture_UnmarshalJSON(t *testing.T) { want *OcclusionTexture wantErr bool }{ - {"default", new(OcclusionTexture), args{[]byte("{}")}, &OcclusionTexture{Strength: 1}, false}, - {"empty", new(OcclusionTexture), args{[]byte(`{"strength": 0, "index": 0}`)}, &OcclusionTexture{Strength: 0, Index: Index(0)}, false}, - {"nodefault", new(OcclusionTexture), args{[]byte(`{"strength": 0.5, "index":2}`)}, &OcclusionTexture{Strength: 0.5, Index: Index(2)}, false}, + {"default", new(OcclusionTexture), args{[]byte("{}")}, &OcclusionTexture{Strength: Float64(1)}, false}, + {"empty", new(OcclusionTexture), args{[]byte(`{"strength": 0, "index": 0}`)}, &OcclusionTexture{Strength: Float64(0), Index: Index(0)}, false}, + {"nodefault", new(OcclusionTexture), args{[]byte(`{"strength": 0.5, "index":2}`)}, &OcclusionTexture{Strength: Float64(0.5), Index: Index(2)}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -228,9 +228,9 @@ func TestPBRMetallicRoughness_UnmarshalJSON(t *testing.T) { want *PBRMetallicRoughness wantErr bool }{ - {"default", new(PBRMetallicRoughness), args{[]byte("{}")}, &PBRMetallicRoughness{BaseColorFactor: NewRGBA(), MetallicFactor: 1, RoughnessFactor: 1}, false}, + {"default", new(PBRMetallicRoughness), args{[]byte("{}")}, &PBRMetallicRoughness{BaseColorFactor: NewRGBA(), MetallicFactor: Float64(1), RoughnessFactor: Float64(1)}, false}, {"nodefault", new(PBRMetallicRoughness), args{[]byte(`{"baseColorFactor": [0.1,0.2,0.6,0.7],"metallicFactor":0.5,"roughnessFactor":0.1}`)}, &PBRMetallicRoughness{ - BaseColorFactor: &RGBA{R: 0.1, G: 0.2, B: 0.6, A: 0.7}, MetallicFactor: 0.5, RoughnessFactor: 0.1, + BaseColorFactor: &RGBA{R: 0.1, G: 0.2, B: 0.6, A: 0.7}, MetallicFactor: Float64(0.5), RoughnessFactor: Float64(0.1), }, false}, } for _, tt := range tests { @@ -302,9 +302,9 @@ func TestMaterial_MarshalJSON(t *testing.T) { want []byte wantErr bool }{ - {"default", &Material{AlphaCutoff: 0.5, AlphaMode: Opaque}, []byte(`{}`), false}, - {"empty", &Material{AlphaCutoff: 0, AlphaMode: Blend}, []byte(`{"alphaMode":"BLEND","alphaCutoff":0}`), false}, - {"nodefault", &Material{AlphaCutoff: 1, AlphaMode: Blend}, []byte(`{"alphaMode":"BLEND","alphaCutoff":1}`), false}, + {"default", &Material{AlphaCutoff: Float64(0.5), AlphaMode: Opaque}, []byte(`{}`), false}, + {"empty", &Material{AlphaMode: Blend}, []byte(`{"alphaMode":"BLEND"}`), false}, + {"nodefault", &Material{AlphaCutoff: Float64(1), AlphaMode: Blend}, []byte(`{"alphaMode":"BLEND","alphaCutoff":1}`), false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -327,9 +327,9 @@ func TestNormalTexture_MarshalJSON(t *testing.T) { want []byte wantErr bool }{ - {"default", &NormalTexture{Scale: 1}, []byte(`{}`), false}, - {"empty", &NormalTexture{Index: Index(0), Scale: 0}, []byte(`{"index":0,"scale":0}`), false}, - {"nodefault", &NormalTexture{Index: Index(1), Scale: 0.5}, []byte(`{"index":1,"scale":0.5}`), false}, + {"default", &NormalTexture{Scale: Float64(1)}, []byte(`{}`), false}, + {"empty", &NormalTexture{Index: Index(0)}, []byte(`{"index":0}`), false}, + {"nodefault", &NormalTexture{Index: Index(1), Scale: Float64(0.5)}, []byte(`{"index":1,"scale":0.5}`), false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -352,9 +352,9 @@ func TestOcclusionTexture_MarshalJSON(t *testing.T) { want []byte wantErr bool }{ - {"default", &OcclusionTexture{Strength: 1}, []byte(`{}`), false}, - {"empty", &OcclusionTexture{Index: Index(0), Strength: 0}, []byte(`{"index":0,"strength":0}`), false}, - {"nodefault", &OcclusionTexture{Index: Index(1), Strength: 0.5}, []byte(`{"index":1,"strength":0.5}`), false}, + {"default", &OcclusionTexture{Strength: Float64(1)}, []byte(`{}`), false}, + {"empty", &OcclusionTexture{Index: Index(0)}, []byte(`{"index":0}`), false}, + {"nodefault", &OcclusionTexture{Index: Index(1), Strength: Float64(0.5)}, []byte(`{"index":1,"strength":0.5}`), false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -377,9 +377,9 @@ func TestPBRMetallicRoughness_MarshalJSON(t *testing.T) { want []byte wantErr bool }{ - {"default", &PBRMetallicRoughness{MetallicFactor: 1, RoughnessFactor: 1, BaseColorFactor: NewRGBA()}, []byte(`{}`), false}, - {"empty", &PBRMetallicRoughness{MetallicFactor: 0, RoughnessFactor: 0}, []byte(`{"metallicFactor":0,"roughnessFactor":0}`), false}, - {"nodefault", &PBRMetallicRoughness{MetallicFactor: 0.5, RoughnessFactor: 0.5, BaseColorFactor: &RGBA{R: 1, G: 0.5, B: 1, A: 1}}, []byte(`{"baseColorFactor":[1,0.5,1,1],"metallicFactor":0.5,"roughnessFactor":0.5}`), false}, + {"default", &PBRMetallicRoughness{MetallicFactor: Float64(1), RoughnessFactor: Float64(1), BaseColorFactor: NewRGBA()}, []byte(`{}`), false}, + {"empty", &PBRMetallicRoughness{MetallicFactor: Float64(0), RoughnessFactor: Float64(0)}, []byte(`{"metallicFactor":0,"roughnessFactor":0}`), false}, + {"nodefault", &PBRMetallicRoughness{MetallicFactor: Float64(0.5), RoughnessFactor: Float64(0.5), BaseColorFactor: &RGBA{R: 1, G: 0.5, B: 1, A: 1}}, []byte(`{"baseColorFactor":[1,0.5,1,1],"metallicFactor":0.5,"roughnessFactor":0.5}`), false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -537,3 +537,39 @@ func TestPBRMetallicRoughness_BaseColorFactorOrDefault(t *testing.T) { }) } } + +func TestOcclusionTexture_StrengthOrDefault(t *testing.T) { + tests := []struct { + name string + o *OcclusionTexture + want float64 + }{ + {"empty", &OcclusionTexture{}, 1}, + {"other", &OcclusionTexture{Strength: Float64(2)}, 2}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.o.StrengthOrDefault(); got != tt.want { + t.Errorf("OcclusionTexture.StrengthOrDefault() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNormalTexture_ScaleOrDefault(t *testing.T) { + tests := []struct { + name string + n *NormalTexture + want float64 + }{ + {"empty", &NormalTexture{}, 1}, + {"other", &NormalTexture{Scale: Float64(2)}, 2}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.n.ScaleOrDefault(); got != tt.want { + t.Errorf("NormalTexture.ScaleOrDefault() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/validator_test.go b/validator_test.go index de1deda..a8ad5a9 100644 --- a/validator_test.go +++ b/validator_test.go @@ -75,16 +75,16 @@ func TestValidateDocument(t *testing.T) { PBRMetallicRoughness: &PBRMetallicRoughness{BaseColorFactor: &RGBA{R: 1, G: 1.1, B: 0, A: 0}}}}}, true}, {"Document.Materials[0].PBRMetallicRoughness.MetallicFactor", &Document{Asset: Asset{Version: "1.0"}, Materials: []Material{{AlphaMode: Opaque, - PBRMetallicRoughness: &PBRMetallicRoughness{MetallicFactor: 2}}}}, true}, + PBRMetallicRoughness: &PBRMetallicRoughness{MetallicFactor: Float64(2)}}}}, true}, {"Document.Materials[0].PBRMetallicRoughness.MetallicFactor", &Document{Asset: Asset{Version: "1.0"}, Materials: []Material{{AlphaMode: Opaque, - PBRMetallicRoughness: &PBRMetallicRoughness{MetallicFactor: -1}}}}, true}, + PBRMetallicRoughness: &PBRMetallicRoughness{MetallicFactor: Float64(-1)}}}}, true}, {"Document.Materials[0].PBRMetallicRoughness.RoughnessFactor", &Document{Asset: Asset{Version: "1.0"}, Materials: []Material{{AlphaMode: Opaque, - PBRMetallicRoughness: &PBRMetallicRoughness{RoughnessFactor: 2}}}}, true}, + PBRMetallicRoughness: &PBRMetallicRoughness{RoughnessFactor: Float64(2)}}}}, true}, {"Document.Materials[0].PBRMetallicRoughness.RoughnessFactor", &Document{Asset: Asset{Version: "1.0"}, Materials: []Material{{AlphaMode: Opaque, - PBRMetallicRoughness: &PBRMetallicRoughness{RoughnessFactor: -1}}}}, true}, + PBRMetallicRoughness: &PBRMetallicRoughness{RoughnessFactor: Float64(-1)}}}}, true}, {"Document.Samplers[0].MagFilter", &Document{Asset: Asset{Version: "1.0"}, Samplers: []Sampler{{MagFilter: 10, MinFilter: MinLinear, WrapS: ClampToEdge, WrapT: ClampToEdge}}}, true}, {"Document.Samplers[0].MinFilter", &Document{Asset: Asset{Version: "1.0"}, From a6f0ddf96f25b58dd43a618ece1e4546260a995b Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 25 Mar 2019 10:04:36 +0100 Subject: [PATCH 5/8] fix readme --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 1957174..060c049 100644 --- a/README.md +++ b/README.md @@ -66,11 +66,11 @@ if err := gltf.Save(doc, "./a.gltf", true); err != nil { ```go doc := &gltf.Document{ Accessors: []gltf.Accessor{ - {BufferView: 0, ByteOffset: 0, ComponentType: gltf.UnsignedShort, Count: 36, Type: gltf.Scalar}, - {BufferView: 1, ByteOffset: 0, 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: 2, ByteOffset: 0, ComponentType: gltf.Float, Count: 24, Type: gltf.Vec3}, - {BufferView: 3, ByteOffset: 0, ComponentType: gltf.Float, Count: 24, Type: gltf.Vec4}, - {BufferView: 4, ByteOffset: 0, ComponentType: gltf.Float, Count: 24, Type: gltf.Vec2}, + {BufferView: gltf.Index(0), ByteOffset: 0, ComponentType: gltf.UnsignedShort, Count: 36, Type: gltf.Scalar}, + {BufferView: gltf.Index(1), ByteOffset: 0, 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), ByteOffset: 0, ComponentType: gltf.Float, Count: 24, Type: gltf.Vec3}, + {BufferView: gltf.Index(3), ByteOffset: 0, ComponentType: gltf.Float, Count: 24, Type: gltf.Vec4}, + {BufferView: gltf.Index(4), ByteOffset: 0, ComponentType: gltf.Float, Count: 24, Type: gltf.Vec2}, }, Asset: gltf.Asset{Version: "2.0", Generator: "FBX2glTF"}, BufferViews: []gltf.BufferView{ @@ -81,18 +81,18 @@ doc := &gltf.Document{ {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: 0.5, - PBRMetallicRoughness: &gltf.PBRMetallicRoughness{BaseColorFactor: [4]float64{0.8, 0.8, 0.8, 1}, MetallicFactor: 0.1, RoughnessFactor: 0.99}} + 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)}} }, - Meshes: []gltf.Mesh{{Name: "Cube", Primitives: []gltf.Primitive{{Indices: 0, Material: 0, Mode: gltf.Triangles, Attributes: map[string]uint32{"POSITION": 1, "COLOR_0": 3, "NORMAL": 2, "TEXCOORD_0": 4}}}}}, + 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", Mesh: -1, Camera: -1, Skin: -1, Children: []uint32{1, 2, 3}, 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}}, - {Name: "Mesh", Mesh: -1, Camera: -1, Skin: -1, 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}}, - {Name: "Cube", Mesh: 0, Camera: -1, Skin: -1, 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}}, - {Name: "Texture Group", Mesh: -1, Camera: -1, Skin: -1, 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}}, + {Name: "RootNode", Children: []uint32{1, 2, 3}}, + {Name: "Mesh"}, + {Name: "Cube", Mesh: 0}, + {Name: "Texture Group"}, }, Samplers: []gltf.Sampler{{WrapS: gltf.Repeat, WrapT: gltf.Repeat}}, - Scene: 0, + Scene: gltf.Index(0), Scenes: []gltf.Scene{{Name: "Root Scene", Nodes: []uint32{0}}}, } if err := gltf.Save(doc, "./a.gltf", true); err != nil { From 2b0af6a52cfac91473730b0c290e56ec93397ffc Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 25 Mar 2019 10:05:51 +0100 Subject: [PATCH 6/8] fix readme --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 060c049..50b1bb8 100644 --- a/README.md +++ b/README.md @@ -66,11 +66,11 @@ if err := gltf.Save(doc, "./a.gltf", true); err != nil { ```go doc := &gltf.Document{ Accessors: []gltf.Accessor{ - {BufferView: gltf.Index(0), ByteOffset: 0, ComponentType: gltf.UnsignedShort, Count: 36, Type: gltf.Scalar}, - {BufferView: gltf.Index(1), ByteOffset: 0, 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), ByteOffset: 0, ComponentType: gltf.Float, Count: 24, Type: gltf.Vec3}, - {BufferView: gltf.Index(3), ByteOffset: 0, ComponentType: gltf.Float, Count: 24, Type: gltf.Vec4}, - {BufferView: gltf.Index(4), ByteOffset: 0, ComponentType: gltf.Float, Count: 24, Type: gltf.Vec2}, + {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{ @@ -88,7 +88,7 @@ doc := &gltf.Document{ Nodes: []gltf.Node{ {Name: "RootNode", Children: []uint32{1, 2, 3}}, {Name: "Mesh"}, - {Name: "Cube", Mesh: 0}, + {Name: "Cube", Mesh: gltf.Index(0)}, {Name: "Texture Group"}, }, Samplers: []gltf.Sampler{{WrapS: gltf.Repeat, WrapT: gltf.Repeat}}, From 5677dbcc46d535158b20a67ff9081b38c47c08d7 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 25 Mar 2019 10:15:04 +0100 Subject: [PATCH 7/8] add tests --- struct_test.go | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/struct_test.go b/struct_test.go index 3702af4..533b86f 100644 --- a/struct_test.go +++ b/struct_test.go @@ -573,3 +573,57 @@ func TestNormalTexture_ScaleOrDefault(t *testing.T) { }) } } + +func TestMaterial_AlphaCutoffOrDefault(t *testing.T) { + tests := []struct { + name string + m *Material + want float64 + }{ + {"empty", &Material{}, 0.5}, + {"other", &Material{AlphaCutoff: Float64(2)}, 2}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.m.AlphaCutoffOrDefault(); got != tt.want { + t.Errorf("Material.AlphaCutoffOrDefault() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestPBRMetallicRoughness_MetallicFactorOrDefault(t *testing.T) { + tests := []struct { + name string + p *PBRMetallicRoughness + want float64 + }{ + {"empty", &PBRMetallicRoughness{}, 1}, + {"other", &PBRMetallicRoughness{MetallicFactor: Float64(2)}, 2}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.p.MetallicFactorOrDefault(); got != tt.want { + t.Errorf("PBRMetallicRoughness.MetallicFactorOrDefault() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestPBRMetallicRoughness_RoughnessFactorOrDefault(t *testing.T) { + tests := []struct { + name string + p *PBRMetallicRoughness + want float64 + }{ + {"empty", &PBRMetallicRoughness{}, 1}, + {"other", &PBRMetallicRoughness{RoughnessFactor: Float64(2)}, 2}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.p.RoughnessFactorOrDefault(); got != tt.want { + t.Errorf("PBRMetallicRoughness.RoughnessFactorOrDefault() = %v, want %v", got, tt.want) + } + }) + } +} From 56f29e0c77c88234a15e355c53550461a0da5bd5 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 25 Mar 2019 10:21:06 +0100 Subject: [PATCH 8/8] add tests --- encode_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/encode_test.go b/encode_test.go index bb83643..aa72529 100644 --- a/encode_test.go +++ b/encode_test.go @@ -64,6 +64,9 @@ func TestEncoder_Encode(t *testing.T) { {Extras: 8.0, Sampler: Index(1), Target: ChannelTarget{Extras: 8.0, Node: Index(3), Path: Weights}}, {Extras: 8.0, Sampler: Index(2), Target: ChannelTarget{Extras: 8.0, Node: Index(5), Path: Translation}}, }}, + {Extras: 8.0, Name: "an_3", Samplers: []AnimationSampler{ + {Extras: 8.0, Input: Index(1), Output: Index(1), Interpolation: CubicSpline}, + }}, }}}, false}, {"withBuffer", args{&Document{Buffers: []Buffer{ {Extras: 8.0, Name: "binary", ByteLength: 3, URI: "a.bin", Data: []uint8{1, 2, 3}},