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

code4game / libgltf / 58

pending completion
58

push

github-action

Xing Ji
[action] remove neon support again

1642 of 6052 relevant lines covered (27.13%)

2593.3 hits per line

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

88.21
/source/libgltf/gltf_loader.cpp
1
/*
2
 * This software is released under the MIT license.
3
 *
4
 * Copyright (c) 2017-2023 Code 4 Game, Org. All Rights Reserved.
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
7
 * this software and associated documentation files (the "Software"), to deal in
8
 * the Software without restriction, including without limitation the rights to
9
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10
 * of the Software, and to permit persons to whom the Software is furnished to do
11
 * so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in all
14
 * copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
 * SOFTWARE.
23
 */
24

25
#include "gltf_loader.h"
26

27
#include "common.h"
28
#include "utility.h"
29

30
#if defined(LIBGLTF_WITH_GOOGLE_DRACO)
31
#    include "extensions/google_draco.h"
32
#endif
33

34
namespace libgltf
35
{
36
    class CBufferViewBufferStream : public IBufferStream
37
    {
38
    public:
39
        explicit CBufferViewBufferStream(const std::shared_ptr<SBufferView>&       _buffer_view,
61✔
40
                                         const std::shared_ptr<IBufferViewStream>& _buffer_view_stream)
41
            : m_pBufferView(_buffer_view)
122✔
42
            , m_pBufferViewStream(_buffer_view_stream)
61✔
43
        {
44
            //
45
        }
61✔
46

47
    public:
48
        virtual bool operator<<(const SBufferData& _buffer_data) override
61✔
49
        {
50
            if (_buffer_data.buffer == nullptr || _buffer_data.bufferSize == 0)
61✔
51
                return false;
×
52
            if (!m_pBufferView || !m_pBufferViewStream)
61✔
53
                return false;
×
54
            Verify(_buffer_data.bufferSize >= static_cast<std::size_t>(m_pBufferView->byteOffset));
61✔
55
            SBufferData buffer_data_temp;
61✔
56
            buffer_data_temp = _buffer_data;
61✔
57
            buffer_data_temp.buffer += m_pBufferView->byteOffset;
61✔
58
            buffer_data_temp.bufferSize -= m_pBufferView->byteOffset;
61✔
59
            if (m_pBufferView->byteLength < static_cast<std::size_t>(buffer_data_temp.bufferSize))
61✔
60
            {
61
                buffer_data_temp.bufferSize = m_pBufferView->byteLength;
47✔
62
            }
63
            buffer_data_temp.bufferStride = static_cast<std::size_t>(m_pBufferView->byteStride);
61✔
64
            return (*m_pBufferViewStream << buffer_data_temp);
61✔
65
        }
66

67
    private:
68
        const std::shared_ptr<SBufferView>& m_pBufferView;
69
        std::shared_ptr<IBufferViewStream>  m_pBufferViewStream;
70
    };
71

72
    class CBufferViewStream : public IBufferViewStream
73
    {
74
    public:
75
        explicit CBufferViewStream(std::vector<uint8_t>& _data)
1✔
76
            : m_Data(_data)
1✔
77
        {
78
            //
79
        }
1✔
80

81
    public:
82
        virtual bool operator<<(const SBufferData& _buffer_data)
1✔
83
        {
84
            m_Data.resize(_buffer_data.bufferSize);
1✔
85
            ::memcpy(&m_Data[0], _buffer_data.buffer, m_Data.size());
1✔
86
            return true;
1✔
87
        }
88

89
    private:
90
        std::vector<uint8_t>& m_Data;
91
    };
92

93
    class CAccessorBufferViewStream : public IBufferViewStream
94
    {
95
    public:
96
        explicit CAccessorBufferViewStream(const std::shared_ptr<SAccessor>& _accessor, const std::shared_ptr<IAccessorStream>& _accessor_stream)
56✔
97
            : m_pAccessor(_accessor)
112✔
98
            , m_pAccessorStream(_accessor_stream)
56✔
99
        {
100
            //
101
        }
56✔
102

103
    public:
104
        virtual bool operator<<(const SBufferData& _buffer_data) override
56✔
105
        {
106
            if (!m_pAccessor || !m_pAccessorStream)
56✔
107
                return false;
×
108
            Verify(_buffer_data.bufferSize >= static_cast<std::size_t>(m_pAccessor->byteOffset));
56✔
109
            SAccessorData accessor_data;
56✔
110
            accessor_data.componentType       = Int32ToAccessorComponentType(m_pAccessor->componentType);
56✔
111
            accessor_data.count               = static_cast<std::size_t>(m_pAccessor->count);
56✔
112
            accessor_data.type                = TextToAccessorType(m_pAccessor->type);
56✔
113
            accessor_data.bufferStride        = _buffer_data.bufferStride;
56✔
114
            const std::size_t sizeof_accessor = SizeOfAccessor(accessor_data.componentType, accessor_data.count, accessor_data.type);
56✔
115
            if (sizeof_accessor > (_buffer_data.bufferSize - m_pAccessor->byteOffset))
56✔
116
                return false;
×
117
            accessor_data.bufferData = _buffer_data;
56✔
118
            accessor_data.bufferData.buffer += m_pAccessor->byteOffset;
56✔
119
            accessor_data.bufferData.bufferSize -= m_pAccessor->byteOffset;
56✔
120
            return (*m_pAccessorStream << accessor_data);
56✔
121
        }
122

123
    private:
124
        const std::shared_ptr<SAccessor>& m_pAccessor;
125
        std::shared_ptr<IAccessorStream>  m_pAccessorStream;
126
    };
127

128
#if defined(LIBGLTF_WITH_GOOGLE_DRACO)
129
    class CDracoBufferViewStream : public IBufferViewStream
130
    {
131
    public:
132
        explicit CDracoBufferViewStream(std::unique_ptr<CGoogleDraco>&        _google_draco,
4✔
133
                                        SKHR_draco_mesh_compressionextension* _draco_extension,
134
                                        std::shared_ptr<IAccessorStream>&     _accessor_stream,
135
                                        bool                                  _use_attribute_id = false,
136
                                        std::size_t                           _attribute_id     = 0)
137
            : m_pGoogleDraco(_google_draco)
8✔
138
            , m_pDracoExtension(_draco_extension)
4✔
139
            , m_pAccessorStream(_accessor_stream)
4✔
140
            , m_bUseAttributeID(_use_attribute_id)
4✔
141
            , m_AttributeID(_attribute_id)
4✔
142
        {
143
            //
144
        }
4✔
145

146
    public:
147
        virtual bool operator<<(const SBufferData& _buffer_data) override
4✔
148
        {
149
            if (!m_pGoogleDraco || !m_pDracoExtension || !m_pAccessorStream)
4✔
150
                return false;
×
151

152
            std::size_t                         bufferview_index    = static_cast<std::size_t>(int32_t(*m_pDracoExtension->bufferView));
4✔
153
            std::shared_ptr<IGoogleDracoStream> google_draco_stream = IGoogleDracoStream::Create(m_pAccessorStream, m_bUseAttributeID, m_AttributeID);
4✔
154
            return m_pGoogleDraco->GetOrDecode(bufferview_index, _buffer_data, google_draco_stream);
4✔
155
        }
4✔
156

157
    private:
158
        std::unique_ptr<CGoogleDraco>&        m_pGoogleDraco;
159
        SKHR_draco_mesh_compressionextension* m_pDracoExtension;
160
        std::shared_ptr<IAccessorStream>      m_pAccessorStream;
161
        bool                                  m_bUseAttributeID;
162
        std::size_t                           m_AttributeID;
163
    };
164
#endif
165

166
    CglTFLoader::SGLBHeader::SGLBHeader()
×
167
        : magic(0)
×
168
        , version(0)
×
169
        , length(0)
×
170
    {
171
        //
172
    }
173

174
    CglTFLoader::SGLBChunk::SGLBChunk()
×
175
        : length(0)
×
176
        , type(0)
×
177
    {
178
        //
179
    }
180

181
    CglTFLoader::CglTFLoader(std::function<std::shared_ptr<std::istream>(const std::string&)> _reader)
19✔
182
        : m_glTF(nullptr)
19✔
183
        , m_Reader(_reader)
19✔
184
        , m_CacheDatas()
19✔
185
#if defined(LIBGLTF_WITH_GOOGLE_DRACO)
186
        , m_pGoogleDraco(std::make_unique<CGoogleDraco>())
38✔
187
#endif
188
    {
189
        std::shared_ptr<std::istream> reader_ptr = m_Reader("");
38✔
190
        if (!reader_ptr)
19✔
191
            return;
1✔
192

193
        m_CacheDatas[""] = std::make_pair<std::vector<uint8_t>, std::string>(std::vector<uint8_t>(), "glb");
18✔
194

195
        std::vector<uint8_t> data;
18✔
196
        {
197
            reader_ptr->seekg(0, std::ios::end);
18✔
198
            data.resize(reader_ptr->tellg());
18✔
199
            reader_ptr->seekg(0, std::ios::beg);
18✔
200
            if (!data.empty())
18✔
201
                reader_ptr->read((std::istream::char_type*)&data[0], data.size());
17✔
202
        }
203

204
        /// check the format. gltf or glb?
205
        const SGLBHeader* glb_header_ptr = nullptr;
18✔
206
        if (data.size() > sizeof(SGLBHeader))
18✔
207
        {
208
            glb_header_ptr = (SGLBHeader*)&data[0];
17✔
209
            if (glb_header_ptr->magic != ms_GLBMagicEntry)
17✔
210
                glb_header_ptr = nullptr;
16✔
211
        }
212

213
        std::string txt_json;
18✔
214
        if (glb_header_ptr)
18✔
215
        {
216
            /// collect all chunks
217
            std::vector<const SGLBChunk*> glb_chunk_ptrs;
1✔
218

219
            std::size_t offset = sizeof(SGLBHeader);
1✔
220
            while (offset < data.size())
3✔
221
            {
222
                const SGLBChunk* glb_chunk_ptr = (SGLBChunk*)&data[offset];
2✔
223
                glb_chunk_ptrs.push_back(glb_chunk_ptr);
2✔
224

225
                offset += glb_chunk_ptr->length + sizeof(SGLBChunk);
2✔
226
            }
227

228
            /// find the json chunk
229
            const SGLBChunk* glb_chunk_json_ptr = nullptr;
1✔
230
            for (const SGLBChunk* glb_chunk_ptr : glb_chunk_ptrs)
1✔
231
            {
232
                if (glb_chunk_ptr->type != ms_GLBChunkTypeJSON)
1✔
233
                    continue;
×
234

235
                txt_json.resize(glb_chunk_ptr->length);
1✔
236
                if (!txt_json.empty())
1✔
237
                    ::memcpy(&txt_json[0], (const uint8_t*)glb_chunk_ptr + sizeof(SGLBChunk), glb_chunk_ptr->length);
1✔
238
                break;
1✔
239
            }
240

241
            /// find the binary chunk
242
            for (const SGLBChunk* glb_chunk_ptr : glb_chunk_ptrs)
2✔
243
            {
244
                if (glb_chunk_ptr->type != ms_GLBChunkTypeBIN)
2✔
245
                    continue;
1✔
246

247
                std::pair<std::vector<uint8_t>, std::string>& data = m_CacheDatas[""];
1✔
248
                data.first.resize(glb_chunk_ptr->length);
1✔
249
                if (!data.first.empty())
1✔
250
                    ::memcpy(&data.first[0], (const uint8_t*)glb_chunk_ptr + sizeof(SGLBChunk), glb_chunk_ptr->length);
1✔
251
                break;
1✔
252
            }
253
        }
1✔
254
        else
255
        {
256
            txt_json.resize(data.size());
17✔
257
            if (!txt_json.empty())
17✔
258
                ::memcpy(&txt_json[0], &data[0], data.size());
16✔
259
        }
260

261
        if (txt_json.empty())
18✔
262
            return;
1✔
263

264
        m_glTF = std::make_unique<SGlTF>();
17✔
265
        if (!(*m_glTF << txt_json))
17✔
266
            m_glTF = nullptr;
×
267
        else
268
        {
269
            // TODO: check version?
270
        }
271
    }
21✔
272

273
    bool CglTFLoader::LoadByUri(const std::string& _uri, const uint8_t*& _data_ptr, std::size_t& _data_size, std::string& _data_type)
73✔
274
    {
275
        std::map<std::string, std::pair<std::vector<uint8_t>, std::string>>::const_iterator it_found = m_CacheDatas.find(_uri);
73✔
276
        if (it_found != m_CacheDatas.end())
73✔
277
        {
278
            const std::pair<std::vector<uint8_t>, std::string>& cache_data = it_found->second;
45✔
279
            _data_ptr                                                      = cache_data.first.data();
45✔
280
            _data_size                                                     = cache_data.first.size();
45✔
281
            _data_type                                                     = cache_data.second;
45✔
282
            return true;
45✔
283
        }
284

285
        std::pair<std::vector<uint8_t>, std::string>& cache_data = m_CacheDatas[_uri];
28✔
286

287
        bool is_loaded = false;
28✔
288
        {
289
            /// support embedded
290
            std::string data_encode;
28✔
291
            std::size_t data_index = 0;
28✔
292
            if (UriParse(_uri, _data_type, data_encode, data_index))
28✔
293
            {
294
                if (!StringEqual(data_encode, "base64", false) || !base64::Decode(_uri.substr(data_index), cache_data.first))
2✔
295
                    return false;
×
296

297
                cache_data.second = "base64";
2✔
298

299
                is_loaded = true;
2✔
300
            }
301
        }
28✔
302

303
        if (!is_loaded)
28✔
304
        {
305
            /// try to load from file
306
            if (std::shared_ptr<std::istream> reader_ptr = m_Reader(_uri))
26✔
307
            {
308
                reader_ptr->seekg(0, std::ios::end);
15✔
309
                cache_data.first.resize(reader_ptr->tellg());
15✔
310
                reader_ptr->seekg(0, std::ios::beg);
15✔
311
                if (!cache_data.first.empty())
15✔
312
                    reader_ptr->read((std::istream::char_type*)&cache_data.first[0], cache_data.first.size());
15✔
313

314
                std::string file_type;
15✔
315
                std::size_t dot_index = _uri.find_last_of('.');
15✔
316
                if (_uri.size() > (dot_index + 1))
15✔
317
                    cache_data.second = "file/" + _uri.substr(dot_index + 1);
15✔
318

319
                is_loaded = true;
15✔
320
            }
41✔
321
        }
322

323
        _data_ptr  = cache_data.first.data();
28✔
324
        _data_size = cache_data.first.size();
28✔
325
        _data_type = cache_data.second;
28✔
326
        return is_loaded;
28✔
327
    }
328

329
    bool CglTFLoader::LoadBuffer(const std::shared_ptr<SBuffer>& _buffer, const uint8_t*& _data_ptr, std::size_t& _data_size)
61✔
330
    {
331
        if (!_buffer)
61✔
332
            return false;
×
333
        std::string data_type;
61✔
334
        if (!LoadByUri(_buffer->uri, _data_ptr, _data_size, data_type))
61✔
335
            return false;
×
336
        return (_data_size == _buffer->byteLength);
61✔
337
    }
61✔
338

339
    bool CglTFLoader::LoadImage(const std::shared_ptr<SImage>& _image, std::vector<uint8_t>& _data, std::string& _type)
13✔
340
    {
341
        if (!_image)
13✔
342
            return false;
×
343

344
        _type = _image->mimeType;
13✔
345
        if (!_image->uri.empty())
13✔
346
        {
347
            const uint8_t* data_ptr  = nullptr;
12✔
348
            std::size_t    data_size = 0;
12✔
349
            if (!LoadByUri(_image->uri, data_ptr, data_size, _type))
12✔
350
                return false;
11✔
351

352
            _data.resize(data_size);
1✔
353
            ::memcpy(&_data[0], data_ptr, data_size);
1✔
354
            return true;
1✔
355
        }
356
        if (!_image->bufferView)
1✔
357
            return false;
×
358

359
        std::shared_ptr<CBufferViewStream> image_stream = std::make_shared<CBufferViewStream>(_data);
1✔
360
        return LoadBufferViewData(static_cast<std::size_t>(int32_t(*_image->bufferView)), image_stream);
1✔
361
    }
1✔
362

363
    bool CglTFLoader::LoadBufferData(std::size_t _index, std::shared_ptr<IBufferStream>& _buffer_stream)
61✔
364
    {
365
        SBufferData buffer_data;
61✔
366
        LoadBuffer(m_glTF->buffers[_index], buffer_data.buffer, buffer_data.bufferSize);
61✔
367
        return (*_buffer_stream << buffer_data);
122✔
368
    }
369

370
    bool CglTFLoader::LoadBufferViewData(std::size_t _index, std::shared_ptr<IBufferViewStream> _buffer_view_stream)
61✔
371
    {
372
        if (m_glTF->bufferViews.size() <= _index)
61✔
373
            return false;
×
374
        const std::shared_ptr<SBufferView>& buffer_view = m_glTF->bufferViews[_index];
61✔
375
        Verify(!!buffer_view);
61✔
376
        std::shared_ptr<IBufferStream> buffer_stream = std::make_shared<CBufferViewBufferStream>(buffer_view, _buffer_view_stream);
61✔
377
        return LoadBufferData(static_cast<std::size_t>(int32_t(*buffer_view->buffer)), buffer_stream);
61✔
378
    }
61✔
379

380
    bool CglTFLoader::LoadAccessorData(std::size_t _index, std::shared_ptr<IAccessorStream> _accessor_stream)
56✔
381
    {
382
        if (!_accessor_stream)
56✔
383
            return false;
×
384
        if (m_glTF->accessors.size() <= _index)
56✔
385
            return false;
×
386
        const std::shared_ptr<SAccessor>& accessor = m_glTF->accessors[_index];
56✔
387
        Verify(!!accessor);
56✔
388
        return LoadBufferViewData(static_cast<std::size_t>(int32_t(*accessor->bufferView)),
112✔
389
                                       std::make_shared<CAccessorBufferViewStream>(accessor, _accessor_stream));
168✔
390
    }
391

392
    const std::unique_ptr<SGlTF>& CglTFLoader::glTF() const
19✔
393
    {
394
        return m_glTF;
19✔
395
    }
396

397
    bool CglTFLoader::LoadMeshPrimitiveIndicesData(std::size_t                      _mesh_index, //
17✔
398
                                                   std::size_t                      _primitive_index,
399
                                                   std::shared_ptr<IAccessorStream> _accessor_stream)
400
    {
401
        if (!m_glTF)
17✔
402
            return false;
×
403
        if (m_glTF->meshes.size() <= _mesh_index)
17✔
404
            return false;
×
405
        const std::shared_ptr<SMesh>& mesh = m_glTF->meshes[_mesh_index];
17✔
406
        if (!mesh || mesh->primitives.size() <= _primitive_index)
17✔
407
            return false;
×
408
        const std::shared_ptr<SMeshPrimitive>& primitive = mesh->primitives[_primitive_index];
17✔
409
        if (!primitive || !primitive->indices)
17✔
410
            return false;
1✔
411

412
        if (primitive->extensions)
16✔
413
        {
414
            const std::shared_ptr<SExtension>& extensions = primitive->extensions;
2✔
415
            std::shared_ptr<SObject>           extension;
2✔
416
            if (StringMapFind<std::shared_ptr<SObject>>(extensions->properties, "KHR_draco_mesh_compression", extension))
2✔
417
            {
418
#if defined(LIBGLTF_WITH_GOOGLE_DRACO)
419
                SKHR_draco_mesh_compressionextension* extension_draco  = reinterpret_cast<SKHR_draco_mesh_compressionextension*>(extension.get());
1✔
420
                std::size_t                           bufferview_index = static_cast<std::size_t>(int32_t(*extension_draco->bufferView));
1✔
421
                std::shared_ptr<IBufferViewStream>    buffer_view_stream =
422
                    std::make_shared<CDracoBufferViewStream>(m_pGoogleDraco, extension_draco, _accessor_stream);
1✔
423
                return LoadBufferViewData(bufferview_index, buffer_view_stream);
1✔
424
#else
425
                return false;
426
#endif
427
            }
1✔
428
        }
2✔
429

430
        return LoadAccessorData(static_cast<std::size_t>(int32_t(*primitive->indices)), _accessor_stream);
15✔
431
    }
432

433
    bool CglTFLoader::LoadMeshPrimitiveAttributeData(std::size_t                      _mesh_index,
51✔
434
                                                     std::size_t                      _primitive_index,
435
                                                     const std::string&               _attribute,
436
                                                     std::shared_ptr<IAccessorStream> _accessor_stream)
437
    {
438
        if (!m_glTF)
51✔
439
            return false;
×
440
        if (m_glTF->meshes.size() <= _mesh_index)
51✔
441
            return false;
×
442
        const std::shared_ptr<SMesh>& mesh = m_glTF->meshes[_mesh_index];
51✔
443
        if (!mesh || mesh->primitives.size() <= _primitive_index)
51✔
444
            return false;
×
445
        const std::shared_ptr<SMeshPrimitive>& primitive = mesh->primitives[_primitive_index];
51✔
446
        if (!primitive)
51✔
447
            return false;
×
448

449
        if (primitive->extensions)
51✔
450
        {
451
            const std::shared_ptr<SExtension>& extensions = primitive->extensions;
6✔
452
            std::shared_ptr<SObject>           extension;
6✔
453
            if (StringMapFind<std::shared_ptr<SObject>>(extensions->properties, "KHR_draco_mesh_compression", extension))
6✔
454
            {
455
#if defined(LIBGLTF_WITH_GOOGLE_DRACO)
456
                SKHR_draco_mesh_compressionextension* extension_draco  = reinterpret_cast<SKHR_draco_mesh_compressionextension*>(extension.get());
3✔
457
                std::size_t                           bufferview_index = static_cast<std::size_t>(int32_t(*extension_draco->bufferView));
3✔
458
                std::shared_ptr<SGlTFId>              attribute_id;
3✔
459
                if (!StringMapFind<std::shared_ptr<SGlTFId>>(extension_draco->attributes, _attribute, attribute_id, false))
3✔
460
                    return false;
×
461
                std::shared_ptr<IBufferViewStream> buffer_view_stream = std::make_shared<CDracoBufferViewStream>(
6✔
462
                    m_pGoogleDraco, extension_draco, _accessor_stream, true, static_cast<std::size_t>(int32_t(*attribute_id)));
3✔
463
                return LoadBufferViewData(bufferview_index, buffer_view_stream);
3✔
464
#else
465
                return false;
466
#endif
467
            }
3✔
468
        }
6✔
469

470
        const std::map<std::string, std::shared_ptr<SGlTFId>>& primitive_attributes = primitive->attributes;
48✔
471
        std::shared_ptr<SGlTFId>                               attribute_access;
48✔
472
        if (!StringMapFind<std::shared_ptr<SGlTFId>>(primitive_attributes, _attribute, attribute_access, false))
48✔
473
            return false;
7✔
474
        return LoadAccessorData(static_cast<std::size_t>(int32_t(*attribute_access)), _accessor_stream);
41✔
475
    }
48✔
476

477
    bool CglTFLoader::LoadImageData(std::size_t _index, std::vector<uint8_t>& _data, std::string& _type)
17✔
478
    {
479
        if (!m_glTF)
17✔
480
            return false;
×
481
        if (m_glTF->images.size() <= _index)
17✔
482
            return false;
4✔
483

484
        const std::shared_ptr<SImage>& image = m_glTF->images[_index];
13✔
485
        return LoadImage(image, _data, _type);
13✔
486
    }
487

488
    const uint32_t CglTFLoader::ms_GLBMagicEntry    = 0x46546C67; // 'glTF'
489
    const uint32_t CglTFLoader::ms_GLBChunkTypeJSON = 0x4E4F534A; // 'JSON'
490
    const uint32_t CglTFLoader::ms_GLBChunkTypeBIN  = 0x004E4942; // 'BIN\0'
491
} // namespace libgltf
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