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

qmuntal / gltf / 26565021446

28 May 2026 08:56AM UTC coverage: 88.365%. First build
26565021446

push

github

qmuntal
Allow extension-defined animation targets and cameras

15 of 17 new or added lines in 1 file covered. (88.24%)

2795 of 3163 relevant lines covered (88.37%)

101.67 hits per line

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

82.23
/encode.go
1
package gltf
2

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

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

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

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

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

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

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

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

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

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

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

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

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

122
        return err
420✔
123
}
124

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

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

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

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

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

196
        return hasBinChunk, err
234✔
197
}
198

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

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

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

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

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

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

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

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

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

416
// MarshalJSON marshal the camera with the correct default values.
417
func (c *Camera) MarshalJSON() ([]byte, error) {
90✔
418
        type alias Camera
90✔
419
        if c.Perspective != nil && c.Orthographic != nil {
90✔
420
                return nil, errors.New("gltf: camera must not define both perspective and orthographic properties")
×
421
        }
×
422
        tmp := alias(*c)
90✔
423
        if tmp.Type == "" {
120✔
424
                switch {
30✔
425
                case c.Perspective != nil:
12✔
426
                        tmp.Type = "perspective"
12✔
427
                case c.Orthographic != nil:
12✔
428
                        tmp.Type = "orthographic"
12✔
429
                default:
6✔
430
                        return nil, errors.New("gltf: camera.type is required")
6✔
431
                }
432
        } else {
60✔
433
                switch tmp.Type {
60✔
434
                case "perspective":
30✔
435
                        if c.Perspective == nil {
36✔
436
                                return nil, errors.New("gltf: perspective camera must define the perspective property")
6✔
437
                        }
6✔
438
                case "orthographic":
24✔
439
                        if c.Orthographic == nil {
24✔
NEW
440
                                return nil, errors.New("gltf: orthographic camera must define the orthographic property")
×
NEW
441
                        }
×
442
                }
443
        }
444
        return json.Marshal(tmp)
78✔
445
}
446

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

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

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

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

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

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

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

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

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

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

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

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

655
        return err
30✔
656
}
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