• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

qmuntal / gltf / 26519180179

27 May 2026 02:56PM UTC coverage: 88.601% (-2.7%) from 91.298%
26519180179

push

github

web-flow
Merge pull request #100 from qmuntal/copilot/core-spec-compliance

Tighten core glTF spec compliance

259 of 342 new or added lines in 6 files covered. (75.73%)

20 existing lines in 2 files now uncovered.

2705 of 3053 relevant lines covered (88.6%)

82.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.55
/encode.go
1
package gltf
2

3
import (
4
        "encoding/binary"
5
        "encoding/json"
6
        "errors"
7
        "fmt"
8
        "io"
9
        "io/fs"
10
        "os"
11
        "path/filepath"
12
)
13

14
// A CreateFS provides access to a hierarchical file system.
15
// Must follow the same naming convention as io/fs.FS.
16
type CreateFS interface {
17
        fs.FS
18
        Create(name string) (io.WriteCloser, error)
19
}
20

21
// dirFS implements a file system (an fs.FS) for the tree of files rooted at the directory dir.
22
type dirFS struct {
23
        fs.FS
24
        dir string
25
}
26

27
// Create creates or truncates the named file.
28
func (d dirFS) Create(name string) (io.WriteCloser, error) {
×
29
        return os.Create(d.dir + "/" + name)
×
30
}
×
31

32
// Save will save a document as a glTF with the specified by name.
33
func Save(doc *Document, name string) error {
×
34
        return save(doc, name, false)
×
35
}
×
36

37
// SaveBinary will save a document as a GLB file with the specified by name.
38
func SaveBinary(doc *Document, name string) error {
6✔
39
        return save(doc, name, true)
6✔
40
}
6✔
41

42
func save(doc *Document, name string, asBinary bool) error {
6✔
43
        f, err := os.Create(name)
6✔
44
        if err != nil {
6✔
45
                return err
×
46
        }
×
47
        dir := filepath.Dir(name)
6✔
48
        e := NewEncoderFS(f, dirFS{os.DirFS(dir), dir})
6✔
49
        e.AsBinary = asBinary
6✔
50
        if err := e.Encode(doc); err != nil {
6✔
51
                f.Close()
×
52
                return err
×
53
        }
×
54
        return f.Close()
6✔
55
}
56

57
// An Encoder writes a glTF to an output stream.
58
//
59
// Only buffers with relative URIs will be written to Fsys.
60
type Encoder struct {
61
        AsBinary bool
62
        Fsys     CreateFS
63
        w        io.Writer
64
        indent   string
65
        prefix   string
66
}
67

68
// NewEncoder returns a new encoder that writes to w as a normal glTF file.
69
func NewEncoder(w io.Writer) *Encoder {
18✔
70
        return &Encoder{
18✔
71
                AsBinary: true,
18✔
72
                w:        w,
18✔
73
        }
18✔
74
}
18✔
75

76
// NewEncoder returns a new encoder that writes to w as a normal glTF file.
77
func NewEncoderFS(w io.Writer, fsys CreateFS) *Encoder {
420✔
78
        return &Encoder{
420✔
79
                AsBinary: true,
420✔
80
                Fsys:     fsys,
420✔
81
                w:        w,
420✔
82
        }
420✔
83
}
420✔
84

85
// SetJSONIndent sets json encoded data to have provided prefix and indent settings
86
func (e *Encoder) SetJSONIndent(prefix string, indent string) {
204✔
87
        e.prefix = prefix
204✔
88
        e.indent = indent
204✔
89
}
204✔
90

91
// Encode writes the encoding of doc to the stream.
92
func (e *Encoder) Encode(doc *Document) error {
444✔
93
        var err error
444✔
94
        var externalBufferIndex = 0
444✔
95
        if e.AsBinary {
678✔
96
                var hasBinChunk bool
234✔
97
                hasBinChunk, err = e.encodeBinary(doc)
234✔
98
                if hasBinChunk {
246✔
99
                        externalBufferIndex = 1
12✔
100
                }
12✔
101
        } else {
210✔
102
                var jsonData []byte
210✔
103
                jsonData, err = e.marshalJSONDoc(doc)
210✔
104
                if err != nil {
210✔
105
                        return err
×
106
                }
×
107
                _, err = e.w.Write(jsonData)
210✔
108
        }
109
        if err != nil {
444✔
110
                return err
×
111
        }
×
112

113
        for i := externalBufferIndex; i < len(doc.Buffers); i++ {
534✔
114
                buf := doc.Buffers[i]
90✔
115
                if len(buf.Data) == 0 || buf.URI == "" || buf.IsEmbeddedResource() {
120✔
116
                        continue
30✔
117
                }
118
                if err = e.encodeBuffer(buf); err != nil {
84✔
119
                        return err
24✔
120
                }
24✔
121
        }
122

123
        return err
420✔
124
}
125

126
func (e *Encoder) encodeBuffer(buffer *Buffer) error {
60✔
127
        if err := validateBufferURI(buffer.URI); err != nil {
84✔
128
                return err
24✔
129
        }
24✔
130
        if e.Fsys == nil {
36✔
131
                return nil
×
132
        }
×
133
        uri, ok := sanitizeURI(buffer.URI)
36✔
134
        if !ok {
36✔
135
                return nil
×
136
        }
×
137
        w, err := e.Fsys.Create(uri)
36✔
138
        if err != nil {
36✔
139
                return err
×
140
        }
×
141
        _, err = w.Write(buffer.Data)
36✔
142
        if err1 := w.Close(); err == nil {
72✔
143
                err = err1
36✔
144
        }
36✔
145
        return err
36✔
146
}
147

148
func (e *Encoder) encodeBinary(doc *Document) (bool, error) {
234✔
149
        jsonText, err := e.marshalJSONDoc(doc)
234✔
150
        if err != nil {
234✔
151
                return false, err
×
152
        }
×
153
        jsonHeader := chunkHeader{
234✔
154
                Length: uint32(((len(jsonText) + 3) / 4) * 4),
234✔
155
                Type:   glbChunkJSON,
234✔
156
        }
234✔
157
        header := glbHeader{
234✔
158
                Magic:      glbHeaderMagic,
234✔
159
                Version:    2,
234✔
160
                Length:     12 + 8 + jsonHeader.Length, // 12-byte glb header + 8-byte json chunk header
234✔
161
                JSONHeader: jsonHeader,
234✔
162
        }
234✔
163
        headerPadding := make([]byte, header.JSONHeader.Length-uint32(len(jsonText)))
234✔
164
        for i := range headerPadding {
528✔
165
                headerPadding[i] = ' '
294✔
166
        }
294✔
167

168
        hasBinChunk := len(doc.Buffers) > 0 && doc.Buffers[0].URI == ""
234✔
169
        var binPaddedLength int
234✔
170
        if hasBinChunk {
246✔
171
                if len(doc.Buffers[0].Data) < doc.Buffers[0].ByteLength {
12✔
NEW
172
                        return false, io.ErrShortBuffer
×
NEW
173
                }
×
174
                binPaddedLength = ((doc.Buffers[0].ByteLength + 3) / 4) * 4
12✔
175
                header.Length += uint32(8 + binPaddedLength)
12✔
176
        }
177

178
        err = binary.Write(e.w, binary.LittleEndian, &header)
234✔
179
        if err != nil {
234✔
180
                return false, err
×
181
        }
×
182
        e.w.Write(jsonText)
234✔
183
        e.w.Write(headerPadding)
234✔
184

234✔
185
        if hasBinChunk {
246✔
186
                binBuffer := doc.Buffers[0]
12✔
187
                binPadding := make([]byte, binPaddedLength-binBuffer.ByteLength)
12✔
188
                for i := range binPadding {
24✔
189
                        binPadding[i] = 0
12✔
190
                }
12✔
191
                binHeader := chunkHeader{Length: uint32(binPaddedLength), Type: glbChunkBIN}
12✔
192
                binary.Write(e.w, binary.LittleEndian, &binHeader)
12✔
193
                e.w.Write(binBuffer.Data[:binBuffer.ByteLength])
12✔
194
                _, err = e.w.Write(binPadding)
12✔
195
        }
196

197
        return hasBinChunk, err
234✔
198
}
199

200
// MarshalJSON marshal the document with the correct default values.
201
func (e *Encoder) marshalJSONDoc(doc *Document) ([]byte, error) {
444✔
202
        type alias Document
444✔
203
        tmp := &struct {
444✔
204
                CustomBuffers []*Buffer `json:"buffers,omitempty"`
444✔
205
                Buffers       []*Buffer `json:"-"`
444✔
206
                *alias
444✔
207
        }{
444✔
208
                CustomBuffers: make([]*Buffer, len(doc.Buffers)),
444✔
209
                alias:         (*alias)(doc),
444✔
210
        }
444✔
211
        // Embed buffers without URI.
444✔
212
        for i, buf := range doc.Buffers {
546✔
213
                if i == 0 && e.AsBinary && buf.URI == "" {
114✔
214
                        // First buffer will be encoded in the binary chunk.
12✔
215
                        tmp.CustomBuffers[i] = buf
12✔
216
                        continue
12✔
217
                }
218
                if len(buf.Data) > 0 && buf.URI == "" && !buf.IsEmbeddedResource() {
108✔
219
                        tmpBuf := &Buffer{
18✔
220
                                Extensions: buf.Extensions,
18✔
221
                                Extras:     buf.Extras,
18✔
222
                                Name:       buf.Name,
18✔
223
                                ByteLength: buf.ByteLength,
18✔
224
                                Data:       buf.Data,
18✔
225
                        }
18✔
226
                        tmpBuf.EmbeddedResource()
18✔
227
                        tmp.CustomBuffers[i] = tmpBuf
18✔
228
                } else {
90✔
229
                        tmp.CustomBuffers[i] = buf
72✔
230
                }
72✔
231
        }
232
        if len(e.prefix) > 0 || len(e.indent) > 0 {
648✔
233
                return json.MarshalIndent(tmp, e.prefix, e.indent)
204✔
234
        }
204✔
235
        return json.Marshal(tmp)
240✔
236
}
237

238
// UnmarshalJSON unmarshals the document and validates root-level required fields.
239
func (doc *Document) UnmarshalJSON(data []byte) error {
438✔
240
        type alias Document
438✔
241
        var required struct {
438✔
242
                Asset *json.RawMessage `json:"asset"`
438✔
243
        }
438✔
244
        if err := json.Unmarshal(data, &required); err != nil {
438✔
NEW
245
                return err
×
NEW
246
        }
×
247
        if required.Asset == nil {
444✔
248
                return errors.New("gltf: asset is required")
6✔
249
        }
6✔
250
        var tmp alias
432✔
251
        if err := json.Unmarshal(data, &tmp); err != nil {
432✔
NEW
252
                return err
×
NEW
253
        }
×
254
        *doc = Document(tmp)
432✔
255
        return nil
432✔
256
}
257

258
// UnmarshalJSON unmarshal the asset with the correct default values.
259
func (as *Asset) UnmarshalJSON(data []byte) error {
432✔
260
        type alias Asset
432✔
261
        var required struct {
432✔
262
                Version *string `json:"version"`
432✔
263
        }
432✔
264
        if err := json.Unmarshal(data, &required); err != nil {
432✔
NEW
265
                return err
×
NEW
266
        }
×
267
        if required.Version == nil || *required.Version == "" {
432✔
NEW
268
                return errors.New("gltf: asset.version is required")
×
NEW
269
        }
×
270
        var tmp alias
432✔
271
        err := json.Unmarshal(data, &tmp)
432✔
272
        if err == nil {
864✔
273
                *as = Asset(tmp)
432✔
274
        }
432✔
275
        return err
432✔
276
}
277

278
// MarshalJSON marshal the asset with the correct default values.
279
func (as *Asset) MarshalJSON() ([]byte, error) {
456✔
280
        type alias Asset
456✔
281
        tmp := alias(*as)
456✔
282
        if tmp.Version == "" {
468✔
283
                tmp.Version = "2.0"
12✔
284
        }
12✔
285
        return json.Marshal(tmp)
456✔
286
}
287

288
// UnmarshalJSON unmarshal the accessor and validates required fields.
289
func (a *Accessor) UnmarshalJSON(data []byte) error {
192✔
290
        type alias Accessor
192✔
291
        var required struct {
192✔
292
                ComponentType *json.RawMessage `json:"componentType"`
192✔
293
                Count         *int             `json:"count"`
192✔
294
                Type          *json.RawMessage `json:"type"`
192✔
295
        }
192✔
296
        if err := json.Unmarshal(data, &required); err != nil {
192✔
NEW
297
                return err
×
NEW
298
        }
×
299
        if required.ComponentType == nil {
192✔
NEW
300
                return errors.New("gltf: accessor.componentType is required")
×
NEW
301
        }
×
302
        if required.Count == nil || *required.Count < 1 {
192✔
NEW
303
                return errors.New("gltf: accessor.count must be greater than zero")
×
NEW
304
        }
×
305
        if required.Type == nil {
192✔
NEW
306
                return errors.New("gltf: accessor.type is required")
×
UNCOV
307
        }
×
308
        var tmp alias
192✔
309
        if err := json.Unmarshal(data, &tmp); err != nil {
192✔
NEW
310
                return err
×
NEW
311
        }
×
312
        *a = Accessor(tmp)
192✔
313
        return nil
192✔
314
}
315

316
// UnmarshalJSON unmarshal the node with the correct default values.
317
func (n *Node) UnmarshalJSON(data []byte) error {
192✔
318
        type alias Node
192✔
319
        var raw struct {
192✔
320
                Matrix      *json.RawMessage `json:"matrix"`
192✔
321
                Rotation    *json.RawMessage `json:"rotation"`
192✔
322
                Scale       *json.RawMessage `json:"scale"`
192✔
323
                Translation *json.RawMessage `json:"translation"`
192✔
324
        }
192✔
325
        if err := json.Unmarshal(data, &raw); err != nil {
192✔
NEW
326
                return err
×
NEW
327
        }
×
328
        if raw.Matrix != nil && (raw.Rotation != nil || raw.Scale != nil || raw.Translation != nil) {
198✔
329
                return errors.New("gltf: node must not define both matrix and TRS properties")
6✔
330
        }
6✔
331
        tmp := alias(Node{
186✔
332
                Matrix:   DefaultMatrix,
186✔
333
                Rotation: DefaultRotation,
186✔
334
                Scale:    DefaultScale,
186✔
335
        })
186✔
336
        err := json.Unmarshal(data, &tmp)
186✔
337
        if err == nil {
372✔
338
                *n = Node(tmp)
186✔
339
        }
186✔
340
        return err
186✔
341
}
342

343
// MarshalJSON marshal the node with the correct default values.
344
func (n *Node) MarshalJSON() ([]byte, error) {
120✔
345
        type alias Node
120✔
346
        tmp := &struct {
120✔
347
                Matrix      *[16]float64 `json:"matrix,omitempty"`   // A 4x4 transformation matrix stored in column-major order.
120✔
348
                Rotation    *[4]float64  `json:"rotation,omitempty"` // The node's unit quaternion rotation in the order (x, y, z, w), where w is the scalar.
120✔
349
                Scale       *[3]float64  `json:"scale,omitempty"`
120✔
350
                Translation *[3]float64  `json:"translation,omitempty"`
120✔
351
                *alias
120✔
352
        }{
120✔
353
                alias: (*alias)(n),
120✔
354
        }
120✔
355
        hasMatrix := n.Matrix != DefaultMatrix && n.Matrix != emptyMatrix
120✔
356
        hasRotation := n.Rotation != DefaultRotation && n.Rotation != emptyRotation
120✔
357
        hasScale := n.Scale != DefaultScale && n.Scale != emptyScale
120✔
358
        hasTranslation := n.Translation != DefaultTranslation
120✔
359
        if hasMatrix && (hasRotation || hasScale || hasTranslation) {
126✔
360
                return nil, errors.New("gltf: node must not define both matrix and TRS properties")
6✔
361
        }
6✔
362
        if hasMatrix {
150✔
363
                tmp.Matrix = &n.Matrix
36✔
364
        }
36✔
365
        if hasRotation {
144✔
366
                tmp.Rotation = &n.Rotation
30✔
367
        }
30✔
368
        if hasScale {
144✔
369
                tmp.Scale = &n.Scale
30✔
370
        }
30✔
371
        if hasTranslation {
144✔
372
                tmp.Translation = &n.Translation
30✔
373
        }
30✔
374
        return json.Marshal(tmp)
114✔
375
}
376

377
// UnmarshalJSON unmarshal the skin and validates required fields.
378
func (s *Skin) UnmarshalJSON(data []byte) error {
48✔
379
        type alias Skin
48✔
380
        var tmp alias
48✔
381
        if err := json.Unmarshal(data, &tmp); err != nil {
48✔
NEW
382
                return err
×
NEW
383
        }
×
384
        if len(tmp.Joints) == 0 {
48✔
NEW
385
                return errors.New("gltf: skin.joints is required")
×
NEW
386
        }
×
387
        *s = Skin(tmp)
48✔
388
        return nil
48✔
389
}
390

391
// UnmarshalJSON unmarshal the camera and validates its declared type.
392
func (c *Camera) UnmarshalJSON(data []byte) error {
108✔
393
        type alias Camera
108✔
394
        var tmp alias
108✔
395
        if err := json.Unmarshal(data, &tmp); err != nil {
108✔
NEW
396
                return err
×
NEW
397
        }
×
398
        if tmp.Perspective != nil && tmp.Orthographic != nil {
114✔
399
                return errors.New("gltf: camera must not define both perspective and orthographic properties")
6✔
400
        }
6✔
401
        switch tmp.Type {
102✔
402
        case "perspective":
48✔
403
                if tmp.Perspective == nil {
54✔
404
                        return errors.New("gltf: perspective camera must define the perspective property")
6✔
405
                }
6✔
406
        case "orthographic":
42✔
407
                if tmp.Orthographic == nil {
42✔
NEW
408
                        return errors.New("gltf: orthographic camera must define the orthographic property")
×
NEW
409
                }
×
410
        case "":
6✔
411
                return errors.New("gltf: camera.type is required")
6✔
412
        default:
6✔
413
                return fmt.Errorf("gltf: unknown camera type: %s", tmp.Type)
6✔
414
        }
415
        *c = Camera(tmp)
84✔
416
        return nil
84✔
417
}
418

419
// MarshalJSON marshal the camera with the correct default values.
420
func (c *Camera) MarshalJSON() ([]byte, error) {
84✔
421
        type alias Camera
84✔
422
        if c.Perspective != nil && c.Orthographic != nil {
84✔
NEW
423
                return nil, errors.New("gltf: camera must not define both perspective and orthographic properties")
×
NEW
424
        }
×
425
        tmp := alias(*c)
84✔
426
        var cameraType string
84✔
427
        if c.Perspective != nil {
120✔
428
                cameraType = "perspective"
36✔
429
        } else if c.Orthographic != nil {
126✔
430
                cameraType = "orthographic"
42✔
431
        } else {
48✔
432
                return nil, errors.New("gltf: camera must define either the perspective or orthographic property")
6✔
433
        }
6✔
434
        if tmp.Type == "" {
114✔
435
                c.Type = cameraType
36✔
436
                tmp.Type = cameraType
36✔
437
        } else if tmp.Type != cameraType {
84✔
438
                return nil, fmt.Errorf("gltf: camera type %q does not match %s property", tmp.Type, cameraType)
6✔
439
        }
6✔
440
        return json.Marshal(tmp)
72✔
441
}
442

443
// UnmarshalJSON unmarshal the mesh and validates required fields.
444
func (m *Mesh) UnmarshalJSON(data []byte) error {
90✔
445
        type alias Mesh
90✔
446
        var tmp alias
90✔
447
        if err := json.Unmarshal(data, &tmp); err != nil {
90✔
NEW
448
                return err
×
NEW
449
        }
×
450
        if len(tmp.Primitives) == 0 {
90✔
NEW
451
                return errors.New("gltf: mesh.primitives is required")
×
UNCOV
452
        }
×
453
        *m = Mesh(tmp)
90✔
454
        return nil
90✔
455
}
456

457
// UnmarshalJSON unmarshal the primitive and validates required fields.
458
func (p *Primitive) UnmarshalJSON(data []byte) error {
114✔
459
        type alias Primitive
114✔
460
        var tmp alias
114✔
461
        if err := json.Unmarshal(data, &tmp); err != nil {
114✔
NEW
462
                return err
×
NEW
463
        }
×
464
        if len(tmp.Attributes) == 0 {
114✔
NEW
465
                return errors.New("gltf: primitive.attributes is required")
×
NEW
466
        }
×
467
        *p = Primitive(tmp)
114✔
468
        return nil
114✔
469
}
470

471
// UnmarshalJSON unmarshal the material with the correct default values.
472
func (m *Material) UnmarshalJSON(data []byte) error {
156✔
473
        type alias Material
156✔
474
        var raw struct {
156✔
475
                AlphaMode   *json.RawMessage `json:"alphaMode"`
156✔
476
                AlphaCutoff *json.RawMessage `json:"alphaCutoff"`
156✔
477
        }
156✔
478
        if err := json.Unmarshal(data, &raw); err != nil {
156✔
NEW
479
                return err
×
NEW
480
        }
×
481
        if raw.AlphaCutoff != nil && raw.AlphaMode == nil {
162✔
482
                return errors.New("gltf: material.alphaCutoff requires alphaMode")
6✔
483
        }
6✔
484
        tmp := alias(Material{AlphaCutoff: Float(0.5)})
150✔
485
        err := json.Unmarshal(data, &tmp)
150✔
486
        if err == nil {
300✔
487
                *m = Material(tmp)
150✔
488
        }
150✔
489
        return err
150✔
490
}
491

492
// MarshalJSON marshal the material with the correct default values.
493
func (m *Material) MarshalJSON() ([]byte, error) {
144✔
494
        type alias Material
144✔
495
        tmp := &struct {
144✔
496
                EmissiveFactor *[3]float64 `json:"emissiveFactor,omitempty"`
144✔
497
                AlphaCutoff    *float64    `json:"alphaCutoff,omitempty"`
144✔
498
                *alias
144✔
499
        }{
144✔
500
                alias: (*alias)(m),
144✔
501
        }
144✔
502
        if m.AlphaCutoff != nil && *m.AlphaCutoff != 0.5 {
180✔
503
                if m.AlphaMode == AlphaOpaque {
42✔
504
                        return nil, errors.New("gltf: material.alphaCutoff requires alphaMode")
6✔
505
                }
6✔
506
                tmp.AlphaCutoff = m.AlphaCutoff
30✔
507
        }
508
        if m.EmissiveFactor != [3]float64{0, 0, 0} {
162✔
509
                tmp.EmissiveFactor = &m.EmissiveFactor
24✔
510
        }
24✔
511
        return json.Marshal(tmp)
138✔
512
}
513

514
// UnmarshalJSON unmarshal the texture info with the correct default values.
515
func (n *NormalTexture) UnmarshalJSON(data []byte) error {
48✔
516
        type alias NormalTexture
48✔
517
        tmp := alias(NormalTexture{Scale: Float(1)})
48✔
518
        err := json.Unmarshal(data, &tmp)
48✔
519
        if err == nil {
96✔
520
                if tmp.Index == nil {
54✔
521
                        return errors.New("gltf: normalTexture.index is required")
6✔
522
                }
6✔
523
                *n = NormalTexture(tmp)
42✔
524
        }
525
        return err
42✔
526
}
527

528
// MarshalJSON marshal the texture info with the correct default values.
529
func (n *NormalTexture) MarshalJSON() ([]byte, error) {
42✔
530
        type alias NormalTexture
42✔
531
        if n.Index == nil {
48✔
532
                return nil, errors.New("gltf: normalTexture.index is required")
6✔
533
        }
6✔
534
        if n.Scale != nil && *n.Scale == 1 {
36✔
UNCOV
535
                return json.Marshal(&struct {
×
UNCOV
536
                        Scale float64 `json:"scale,omitempty"`
×
UNCOV
537
                        *alias
×
UNCOV
538
                }{
×
UNCOV
539
                        Scale: 0,
×
UNCOV
540
                        alias: (*alias)(n),
×
UNCOV
541
                })
×
UNCOV
542
        }
×
543
        return json.Marshal((*alias)(n))
36✔
544
}
545

546
// UnmarshalJSON unmarshal the texture info with the correct default values.
547
func (o *OcclusionTexture) UnmarshalJSON(data []byte) error {
42✔
548
        type alias OcclusionTexture
42✔
549
        tmp := alias(OcclusionTexture{Strength: Float(1)})
42✔
550
        err := json.Unmarshal(data, &tmp)
42✔
551
        if err == nil {
84✔
552
                if tmp.Index == nil {
48✔
553
                        return errors.New("gltf: occlusionTexture.index is required")
6✔
554
                }
6✔
555
                *o = OcclusionTexture(tmp)
36✔
556
        }
557
        return err
36✔
558
}
559

560
// MarshalJSON marshal the texture info with the correct default values.
561
func (o *OcclusionTexture) MarshalJSON() ([]byte, error) {
42✔
562
        type alias OcclusionTexture
42✔
563
        if o.Index == nil {
48✔
564
                return nil, errors.New("gltf: occlusionTexture.index is required")
6✔
565
        }
6✔
566
        if o.Strength != nil && *o.Strength == 1 {
36✔
UNCOV
567
                return json.Marshal(&struct {
×
UNCOV
568
                        Strength float64 `json:"strength,omitempty"`
×
UNCOV
569
                        *alias
×
UNCOV
570
                }{
×
UNCOV
571
                        Strength: 0,
×
UNCOV
572
                        alias:    (*alias)(o),
×
UNCOV
573
                })
×
UNCOV
574
        }
×
575
        return json.Marshal((*alias)(o))
36✔
576
}
577

578
// UnmarshalJSON unmarshal the texture info and validates required fields.
579
func (t *TextureInfo) UnmarshalJSON(data []byte) error {
96✔
580
        type alias TextureInfo
96✔
581
        var required struct {
96✔
582
                Index *int `json:"index"`
96✔
583
        }
96✔
584
        if err := json.Unmarshal(data, &required); err != nil {
96✔
NEW
585
                return err
×
NEW
586
        }
×
587
        if required.Index == nil {
96✔
NEW
588
                return errors.New("gltf: textureInfo.index is required")
×
NEW
589
        }
×
590
        var tmp alias
96✔
591
        if err := json.Unmarshal(data, &tmp); err != nil {
96✔
NEW
592
                return err
×
NEW
593
        }
×
594
        *t = TextureInfo(tmp)
96✔
595
        return nil
96✔
596
}
597

598
// UnmarshalJSON unmarshal the pbr with the correct default values.
599
func (p *PBRMetallicRoughness) UnmarshalJSON(data []byte) error {
54✔
600
        type alias PBRMetallicRoughness
54✔
601
        tmp := alias(PBRMetallicRoughness{BaseColorFactor: &[4]float64{1, 1, 1, 1}, MetallicFactor: Float(1), RoughnessFactor: Float(1)})
54✔
602
        err := json.Unmarshal(data, &tmp)
54✔
603
        if err == nil {
108✔
604
                *p = PBRMetallicRoughness(tmp)
54✔
605
        }
54✔
606
        return err
54✔
607
}
608

609
// MarshalJSON marshal the pbr with the correct default values.
610
func (p *PBRMetallicRoughness) MarshalJSON() ([]byte, error) {
42✔
611
        type alias PBRMetallicRoughness
42✔
612
        tmp := &struct {
42✔
613
                alias
42✔
614
        }{
42✔
615
                alias: (alias)(*p),
42✔
616
        }
42✔
617
        if p.MetallicFactor != nil && *p.MetallicFactor == 1 {
72✔
618
                tmp.MetallicFactor = nil
30✔
619
        }
30✔
620
        if p.RoughnessFactor != nil && *p.RoughnessFactor == 1 {
48✔
621
                tmp.RoughnessFactor = nil
6✔
622
        }
6✔
623
        if p.BaseColorFactor != nil && *p.BaseColorFactor == [4]float64{1, 1, 1, 1} {
48✔
624
                tmp.BaseColorFactor = nil
6✔
625
        }
6✔
626
        return json.Marshal(tmp)
42✔
627
}
628

629
// UnmarshalJSON unmarshal the extensions with the supported extensions initialized.
630
func (ext *Extensions) UnmarshalJSON(data []byte) error {
24✔
631
        if len(*ext) == 0 {
48✔
632
                *ext = make(Extensions)
24✔
633
        }
24✔
634
        var raw map[string]json.RawMessage
24✔
635
        err := json.Unmarshal(data, &raw)
24✔
636
        if err == nil {
42✔
637
                for key, value := range raw {
36✔
638
                        if extFactory, ok := queryExtension(key); ok {
30✔
639
                                n, err := extFactory(value)
12✔
640
                                if err != nil {
18✔
641
                                        (*ext)[key] = value
6✔
642
                                } else {
12✔
643
                                        (*ext)[key] = n
6✔
644
                                }
6✔
645
                        } else {
6✔
646
                                (*ext)[key] = value
6✔
647
                        }
6✔
648
                }
649
        }
650

651
        return err
24✔
652
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc