aboutsummaryrefslogtreecommitdiff
path: root/contrib/cgltf
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-06-27 10:18:39 -0700
committer3gg <3gg@shellblade.net>2025-06-27 10:18:39 -0700
commitbd57f345ed9dbed1d81683e48199626de2ea9044 (patch)
tree4221f2f2a7ad2244d2e93052bd68187ec91b8ea9 /contrib/cgltf
parent9a82ce0083437a4f9f58108b2c23b957d2249ad8 (diff)
Restructure projectHEADmain
Diffstat (limited to 'contrib/cgltf')
-rw-r--r--contrib/cgltf/CMakeLists.txt8
-rw-r--r--contrib/cgltf/LICENSE7
-rw-r--r--contrib/cgltf/README.md154
-rw-r--r--contrib/cgltf/cgltf.h5746
-rw-r--r--contrib/cgltf/cgltf_write.h1173
5 files changed, 7088 insertions, 0 deletions
diff --git a/contrib/cgltf/CMakeLists.txt b/contrib/cgltf/CMakeLists.txt
new file mode 100644
index 0000000..0ac840a
--- /dev/null
+++ b/contrib/cgltf/CMakeLists.txt
@@ -0,0 +1,8 @@
1cmake_minimum_required(VERSION 3.16)
2
3project(cgltf)
4
5add_library(cgltf INTERFACE)
6
7target_include_directories(cgltf INTERFACE
8 ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/contrib/cgltf/LICENSE b/contrib/cgltf/LICENSE
new file mode 100644
index 0000000..0afe8c7
--- /dev/null
+++ b/contrib/cgltf/LICENSE
@@ -0,0 +1,7 @@
1Copyright (c) 2018 Johannes Kuhlmann
2
3Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
5The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
7THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/contrib/cgltf/README.md b/contrib/cgltf/README.md
new file mode 100644
index 0000000..3b49d52
--- /dev/null
+++ b/contrib/cgltf/README.md
@@ -0,0 +1,154 @@
1# :diamond_shape_with_a_dot_inside: cgltf
2**Single-file/stb-style C glTF loader and writer**
3
4[![Build Status](https://travis-ci.org/jkuhlmann/cgltf.svg?branch=master)](https://travis-ci.org/jkuhlmann/cgltf)
5
6Used in: [bgfx](https://github.com/bkaradzic/bgfx), [Filament](https://github.com/google/filament), [meshoptimizer](https://github.com/zeux/meshoptimizer), [raylib](https://github.com/raysan5/raylib), and more!
7
8## Usage: Loading
9Loading from file:
10```c
11#define CGLTF_IMPLEMENTATION
12#include "cgltf.h"
13
14cgltf_options options = {0};
15cgltf_data* data = NULL;
16cgltf_result result = cgltf_parse_file(&options, "scene.gltf", &data);
17if (result == cgltf_result_success)
18{
19 /* TODO make awesome stuff */
20 cgltf_free(data);
21}
22```
23
24Loading from memory:
25```c
26#define CGLTF_IMPLEMENTATION
27#include "cgltf.h"
28
29void* buf; /* Pointer to glb or gltf file data */
30size_t size; /* Size of the file data */
31
32cgltf_options options = {0};
33cgltf_data* data = NULL;
34cgltf_result result = cgltf_parse(&options, buf, size, &data);
35if (result == cgltf_result_success)
36{
37 /* TODO make awesome stuff */
38 cgltf_free(data);
39}
40```
41
42Note that cgltf does not load the contents of extra files such as buffers or images into memory by default. You'll need to read these files yourself using URIs from `data.buffers[]` or `data.images[]` respectively.
43For buffer data, you can alternatively call `cgltf_load_buffers`, which will use `FILE*` APIs to open and read buffer files.
44
45**For more in-depth documentation and a description of the public interface refer to the top of the `cgltf.h` file.**
46
47## Usage: Writing
48When writing glTF data, you need a valid `cgltf_data` structure that represents a valid glTF document. You can construct such a structure yourself or load it using the loader functions described above. The writer functions do not deallocate any memory. So, you either have to do it manually or call `cgltf_free()` if you got the data by loading it from a glTF document.
49
50Writing to file:
51```c
52#define CGLTF_IMPLEMENTATION
53#define CGLTF_WRITE_IMPLEMENTATION
54#include "cgltf_write.h"
55
56cgltf_options options = {0};
57cgltf_data* data = /* TODO must be valid data */;
58cgltf_result result = cgltf_write_file(&options, "out.gltf", data);
59if (result != cgltf_result_success)
60{
61 /* TODO handle error */
62}
63```
64
65Writing to memory:
66```c
67#define CGLTF_IMPLEMENTATION
68#define CGLTF_WRITE_IMPLEMENTATION
69#include "cgltf_write.h"
70cgltf_options options = {0};
71cgltf_data* data = /* TODO must be valid data */;
72
73cgltf_size size = cgltf_write(&options, NULL, 0, data);
74
75char* buf = malloc(size);
76
77cgltf_size written = cgltf_write(&options, buf, size, data);
78if (written != size)
79{
80 /* TODO handle error */
81}
82```
83
84Note that cgltf does not write the contents of extra files such as buffers or images. You'll need to write this data yourself.
85
86Writing does not yet support "extras" data.
87
88**For more in-depth documentation and a description of the public interface refer to the top of the `cgltf_write.h` file.**
89
90
91## Features
92cgltf supports core glTF 2.0:
93- glb (binary files) and gltf (JSON files)
94- meshes (including accessors, buffer views, buffers)
95- materials (including textures, samplers, images)
96- scenes and nodes
97- skins
98- animations
99- cameras
100- morph targets
101- extras data
102
103cgltf also supports some glTF extensions:
104- KHR_draco_mesh_compression (requires a library like [Google's Draco](https://github.com/google/draco) for decompression though)
105- KHR_lights_punctual
106- KHR_materials_clearcoat
107- KHR_materials_ior
108- KHR_materials_pbrSpecularGlossiness
109- KHR_materials_specular
110- KHR_materials_transmission
111- KHR_materials_unlit
112- KHR_texture_transform
113
114cgltf does **not** yet support unlisted extensions. However, unlisted extensions can be accessed via "extensions" member on objects.
115
116## Building
117The easiest approach is to integrate the `cgltf.h` header file into your project. If you are unfamiliar with single-file C libraries (also known as stb-style libraries), this is how it goes:
118
1191. Include `cgltf.h` where you need the functionality.
1201. Have exactly one source file that defines `CGLTF_IMPLEMENTATION` before including `cgltf.h`.
1211. Use the cgltf functions as described above.
122
123Support for writing can be found in a separate file called `cgltf_write.h` (which includes `cgltf.h`). Building it works analogously using the `CGLTF_WRITE_IMPLEMENTATION` define.
124
125## Contributing
126Everyone is welcome to contribute to the library. If you find any problems, you can submit them using [GitHub's issue system](https://github.com/jkuhlmann/cgltf/issues). If you want to contribute code, you should fork the project and then send a pull request.
127
128
129## Dependencies
130None.
131
132C headers being used by implementation:
133```
134#include <stddef.h>
135#include <stdint.h>
136#include <string.h>
137#include <stdlib.h>
138#include <stdio.h>
139#include <limits.h>
140```
141
142Note, this library has a copy of the [JSMN JSON parser](https://github.com/zserge/jsmn) embedded in its source.
143
144## Testing
145There is a Python script in the `test/` folder that retrieves the glTF 2.0 sample files from the glTF-Sample-Models repository (https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0) and runs the library against all gltf and glb files.
146
147Here's one way to build and run the test:
148
149 cd test ; mkdir build ; cd build ; cmake .. -DCMAKE_BUILD_TYPE=Debug
150 make -j
151 cd ..
152 ./test_all.py
153
154There is also a llvm-fuzz test in `fuzz/`. See http://llvm.org/docs/LibFuzzer.html for more information.
diff --git a/contrib/cgltf/cgltf.h b/contrib/cgltf/cgltf.h
new file mode 100644
index 0000000..077cf36
--- /dev/null
+++ b/contrib/cgltf/cgltf.h
@@ -0,0 +1,5746 @@
1/**
2 * cgltf - a single-file glTF 2.0 parser written in C99.
3 *
4 * Version: 1.7
5 *
6 * Website: https://github.com/jkuhlmann/cgltf
7 *
8 * Distributed under the MIT License, see notice at the end of this file.
9 *
10 * Building:
11 * Include this file where you need the struct and function
12 * declarations. Have exactly one source file where you define
13 * `CGLTF_IMPLEMENTATION` before including this file to get the
14 * function definitions.
15 *
16 * Reference:
17 * `cgltf_result cgltf_parse(const cgltf_options*, const void*,
18 * cgltf_size, cgltf_data**)` parses both glTF and GLB data. If
19 * this function returns `cgltf_result_success`, you have to call
20 * `cgltf_free()` on the created `cgltf_data*` variable.
21 * Note that contents of external files for buffers and images are not
22 * automatically loaded. You'll need to read these files yourself using
23 * URIs in the `cgltf_data` structure.
24 *
25 * `cgltf_options` is the struct passed to `cgltf_parse()` to control
26 * parts of the parsing process. You can use it to force the file type
27 * and provide memory allocation as well as file operation callbacks.
28 * Should be zero-initialized to trigger default behavior.
29 *
30 * `cgltf_data` is the struct allocated and filled by `cgltf_parse()`.
31 * It generally mirrors the glTF format as described by the spec (see
32 * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0).
33 *
34 * `void cgltf_free(cgltf_data*)` frees the allocated `cgltf_data`
35 * variable.
36 *
37 * `cgltf_result cgltf_load_buffers(const cgltf_options*, cgltf_data*,
38 * const char* gltf_path)` can be optionally called to open and read buffer
39 * files using the `FILE*` APIs. The `gltf_path` argument is the path to
40 * the original glTF file, which allows the parser to resolve the path to
41 * buffer files.
42 *
43 * `cgltf_result cgltf_load_buffer_base64(const cgltf_options* options,
44 * cgltf_size size, const char* base64, void** out_data)` decodes
45 * base64-encoded data content. Used internally by `cgltf_load_buffers()`
46 * and may be useful if you're not dealing with normal files.
47 *
48 * `cgltf_result cgltf_parse_file(const cgltf_options* options, const
49 * char* path, cgltf_data** out_data)` can be used to open the given
50 * file using `FILE*` APIs and parse the data using `cgltf_parse()`.
51 *
52 * `cgltf_result cgltf_validate(cgltf_data*)` can be used to do additional
53 * checks to make sure the parsed glTF data is valid.
54 *
55 * `cgltf_node_transform_local` converts the translation / rotation / scale properties of a node
56 * into a mat4.
57 *
58 * `cgltf_node_transform_world` calls `cgltf_node_transform_local` on every ancestor in order
59 * to compute the root-to-node transformation.
60 *
61 * `cgltf_accessor_unpack_floats` reads in the data from an accessor, applies sparse data (if any),
62 * and converts them to floating point. Assumes that `cgltf_load_buffers` has already been called.
63 * By passing null for the output pointer, users can find out how many floats are required in the
64 * output buffer.
65 *
66 * `cgltf_accessor_num_components` is a tiny utility that tells you the dimensionality of
67 * a certain accessor type. This can be used before `cgltf_accessor_unpack_floats` to help allocate
68 * the necessary amount of memory.
69 *
70 * `cgltf_accessor_read_float` reads a certain element from a non-sparse accessor and converts it to
71 * floating point, assuming that `cgltf_load_buffers` has already been called. The passed-in element
72 * size is the number of floats in the output buffer, which should be in the range [1, 16]. Returns
73 * false if the passed-in element_size is too small, or if the accessor is sparse.
74 *
75 * `cgltf_accessor_read_uint` is similar to its floating-point counterpart, but limited to reading
76 * vector types and does not support matrix types. The passed-in element size is the number of uints
77 * in the output buffer, which should be in the range [1, 4]. Returns false if the passed-in
78 * element_size is too small, or if the accessor is sparse.
79 *
80 * `cgltf_accessor_read_index` is similar to its floating-point counterpart, but it returns size_t
81 * and only works with single-component data types.
82 *
83 * `cgltf_result cgltf_copy_extras_json(const cgltf_data*, const cgltf_extras*,
84 * char* dest, cgltf_size* dest_size)` allows users to retrieve the "extras" data that
85 * can be attached to many glTF objects (which can be arbitrary JSON data). The
86 * `cgltf_extras` struct stores the offsets of the start and end of the extras JSON data
87 * as it appears in the complete glTF JSON data. This function copies the extras data
88 * into the provided buffer. If `dest` is NULL, the length of the data is written into
89 * `dest_size`. You can then parse this data using your own JSON parser
90 * or, if you've included the cgltf implementation using the integrated JSMN JSON parser.
91 */
92#ifndef CGLTF_H_INCLUDED__
93#define CGLTF_H_INCLUDED__
94
95#include <stddef.h>
96
97#ifdef __cplusplus
98extern "C" {
99#endif
100
101typedef size_t cgltf_size;
102typedef float cgltf_float;
103typedef int cgltf_int;
104typedef unsigned int cgltf_uint;
105typedef int cgltf_bool;
106
107typedef enum cgltf_file_type
108{
109 cgltf_file_type_invalid,
110 cgltf_file_type_gltf,
111 cgltf_file_type_glb,
112} cgltf_file_type;
113
114typedef enum cgltf_result
115{
116 cgltf_result_success,
117 cgltf_result_data_too_short,
118 cgltf_result_unknown_format,
119 cgltf_result_invalid_json,
120 cgltf_result_invalid_gltf,
121 cgltf_result_invalid_options,
122 cgltf_result_file_not_found,
123 cgltf_result_io_error,
124 cgltf_result_out_of_memory,
125 cgltf_result_legacy_gltf,
126} cgltf_result;
127
128typedef struct cgltf_memory_options
129{
130 void* (*alloc)(void* user, cgltf_size size);
131 void (*free) (void* user, void* ptr);
132 void* user_data;
133} cgltf_memory_options;
134
135typedef struct cgltf_file_options
136{
137 cgltf_result(*read)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data);
138 void (*release)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data);
139 void* user_data;
140} cgltf_file_options;
141
142typedef struct cgltf_options
143{
144 cgltf_file_type type; /* invalid == auto detect */
145 cgltf_size json_token_count; /* 0 == auto */
146 cgltf_memory_options memory;
147 cgltf_file_options file;
148} cgltf_options;
149
150typedef enum cgltf_buffer_view_type
151{
152 cgltf_buffer_view_type_invalid,
153 cgltf_buffer_view_type_indices,
154 cgltf_buffer_view_type_vertices,
155} cgltf_buffer_view_type;
156
157typedef enum cgltf_attribute_type
158{
159 cgltf_attribute_type_invalid,
160 cgltf_attribute_type_position,
161 cgltf_attribute_type_normal,
162 cgltf_attribute_type_tangent,
163 cgltf_attribute_type_texcoord,
164 cgltf_attribute_type_color,
165 cgltf_attribute_type_joints,
166 cgltf_attribute_type_weights,
167} cgltf_attribute_type;
168
169typedef enum cgltf_component_type
170{
171 cgltf_component_type_invalid,
172 cgltf_component_type_r_8, /* BYTE */
173 cgltf_component_type_r_8u, /* UNSIGNED_BYTE */
174 cgltf_component_type_r_16, /* SHORT */
175 cgltf_component_type_r_16u, /* UNSIGNED_SHORT */
176 cgltf_component_type_r_32u, /* UNSIGNED_INT */
177 cgltf_component_type_r_32f, /* FLOAT */
178} cgltf_component_type;
179
180typedef enum cgltf_type
181{
182 cgltf_type_invalid,
183 cgltf_type_scalar,
184 cgltf_type_vec2,
185 cgltf_type_vec3,
186 cgltf_type_vec4,
187 cgltf_type_mat2,
188 cgltf_type_mat3,
189 cgltf_type_mat4,
190} cgltf_type;
191
192typedef enum cgltf_primitive_type
193{
194 cgltf_primitive_type_points,
195 cgltf_primitive_type_lines,
196 cgltf_primitive_type_line_loop,
197 cgltf_primitive_type_line_strip,
198 cgltf_primitive_type_triangles,
199 cgltf_primitive_type_triangle_strip,
200 cgltf_primitive_type_triangle_fan,
201} cgltf_primitive_type;
202
203typedef enum cgltf_alpha_mode
204{
205 cgltf_alpha_mode_opaque,
206 cgltf_alpha_mode_mask,
207 cgltf_alpha_mode_blend,
208} cgltf_alpha_mode;
209
210typedef enum cgltf_animation_path_type {
211 cgltf_animation_path_type_invalid,
212 cgltf_animation_path_type_translation,
213 cgltf_animation_path_type_rotation,
214 cgltf_animation_path_type_scale,
215 cgltf_animation_path_type_weights,
216} cgltf_animation_path_type;
217
218typedef enum cgltf_interpolation_type {
219 cgltf_interpolation_type_linear,
220 cgltf_interpolation_type_step,
221 cgltf_interpolation_type_cubic_spline,
222} cgltf_interpolation_type;
223
224typedef enum cgltf_camera_type {
225 cgltf_camera_type_invalid,
226 cgltf_camera_type_perspective,
227 cgltf_camera_type_orthographic,
228} cgltf_camera_type;
229
230typedef enum cgltf_light_type {
231 cgltf_light_type_invalid,
232 cgltf_light_type_directional,
233 cgltf_light_type_point,
234 cgltf_light_type_spot,
235} cgltf_light_type;
236
237typedef struct cgltf_extras {
238 cgltf_size start_offset;
239 cgltf_size end_offset;
240} cgltf_extras;
241
242typedef struct cgltf_extension {
243 char* name;
244 char* data;
245} cgltf_extension;
246
247typedef struct cgltf_buffer
248{
249 cgltf_size size;
250 char* uri;
251 void* data; /* loaded by cgltf_load_buffers */
252 cgltf_extras extras;
253 cgltf_size extensions_count;
254 cgltf_extension* extensions;
255} cgltf_buffer;
256
257typedef struct cgltf_buffer_view
258{
259 cgltf_buffer* buffer;
260 cgltf_size offset;
261 cgltf_size size;
262 cgltf_size stride; /* 0 == automatically determined by accessor */
263 cgltf_buffer_view_type type;
264 cgltf_extras extras;
265 cgltf_size extensions_count;
266 cgltf_extension* extensions;
267} cgltf_buffer_view;
268
269typedef struct cgltf_accessor_sparse
270{
271 cgltf_size count;
272 cgltf_buffer_view* indices_buffer_view;
273 cgltf_size indices_byte_offset;
274 cgltf_component_type indices_component_type;
275 cgltf_buffer_view* values_buffer_view;
276 cgltf_size values_byte_offset;
277 cgltf_extras extras;
278 cgltf_extras indices_extras;
279 cgltf_extras values_extras;
280 cgltf_size extensions_count;
281 cgltf_extension* extensions;
282 cgltf_size indices_extensions_count;
283 cgltf_extension* indices_extensions;
284 cgltf_size values_extensions_count;
285 cgltf_extension* values_extensions;
286} cgltf_accessor_sparse;
287
288typedef struct cgltf_accessor
289{
290 cgltf_component_type component_type;
291 cgltf_bool normalized;
292 cgltf_type type;
293 cgltf_size offset;
294 cgltf_size count;
295 cgltf_size stride;
296 cgltf_buffer_view* buffer_view;
297 cgltf_bool has_min;
298 cgltf_float min[16];
299 cgltf_bool has_max;
300 cgltf_float max[16];
301 cgltf_bool is_sparse;
302 cgltf_accessor_sparse sparse;
303 cgltf_extras extras;
304 cgltf_size extensions_count;
305 cgltf_extension* extensions;
306} cgltf_accessor;
307
308typedef struct cgltf_attribute
309{
310 char* name;
311 cgltf_attribute_type type;
312 cgltf_int index;
313 cgltf_accessor* data;
314} cgltf_attribute;
315
316typedef struct cgltf_image
317{
318 char* name;
319 char* uri;
320 cgltf_buffer_view* buffer_view;
321 char* mime_type;
322 cgltf_extras extras;
323 cgltf_size extensions_count;
324 cgltf_extension* extensions;
325} cgltf_image;
326
327typedef struct cgltf_sampler
328{
329 cgltf_int mag_filter;
330 cgltf_int min_filter;
331 cgltf_int wrap_s;
332 cgltf_int wrap_t;
333 cgltf_extras extras;
334 cgltf_size extensions_count;
335 cgltf_extension* extensions;
336} cgltf_sampler;
337
338typedef struct cgltf_texture
339{
340 char* name;
341 cgltf_image* image;
342 cgltf_sampler* sampler;
343 cgltf_extras extras;
344 cgltf_size extensions_count;
345 cgltf_extension* extensions;
346} cgltf_texture;
347
348typedef struct cgltf_texture_transform
349{
350 cgltf_float offset[2];
351 cgltf_float rotation;
352 cgltf_float scale[2];
353 cgltf_int texcoord;
354} cgltf_texture_transform;
355
356typedef struct cgltf_texture_view
357{
358 cgltf_texture* texture;
359 cgltf_int texcoord;
360 cgltf_float scale; /* equivalent to strength for occlusion_texture */
361 cgltf_bool has_transform;
362 cgltf_texture_transform transform;
363 cgltf_extras extras;
364 cgltf_size extensions_count;
365 cgltf_extension* extensions;
366} cgltf_texture_view;
367
368typedef struct cgltf_pbr_metallic_roughness
369{
370 cgltf_texture_view base_color_texture;
371 cgltf_texture_view metallic_roughness_texture;
372
373 cgltf_float base_color_factor[4];
374 cgltf_float metallic_factor;
375 cgltf_float roughness_factor;
376
377 cgltf_extras extras;
378} cgltf_pbr_metallic_roughness;
379
380typedef struct cgltf_pbr_specular_glossiness
381{
382 cgltf_texture_view diffuse_texture;
383 cgltf_texture_view specular_glossiness_texture;
384
385 cgltf_float diffuse_factor[4];
386 cgltf_float specular_factor[3];
387 cgltf_float glossiness_factor;
388} cgltf_pbr_specular_glossiness;
389
390typedef struct cgltf_clearcoat
391{
392 cgltf_texture_view clearcoat_texture;
393 cgltf_texture_view clearcoat_roughness_texture;
394 cgltf_texture_view clearcoat_normal_texture;
395
396 cgltf_float clearcoat_factor;
397 cgltf_float clearcoat_roughness_factor;
398} cgltf_clearcoat;
399
400typedef struct cgltf_transmission
401{
402 cgltf_texture_view transmission_texture;
403 cgltf_float transmission_factor;
404} cgltf_transmission;
405
406typedef struct cgltf_ior
407{
408 cgltf_float ior;
409} cgltf_ior;
410
411typedef struct cgltf_specular
412{
413 cgltf_texture_view specular_texture;
414 cgltf_float specular_color_factor[3];
415 cgltf_float specular_factor;
416} cgltf_specular;
417
418typedef struct cgltf_material
419{
420 char* name;
421 cgltf_bool has_pbr_metallic_roughness;
422 cgltf_bool has_pbr_specular_glossiness;
423 cgltf_bool has_clearcoat;
424 cgltf_bool has_transmission;
425 cgltf_bool has_ior;
426 cgltf_bool has_specular;
427 cgltf_pbr_metallic_roughness pbr_metallic_roughness;
428 cgltf_pbr_specular_glossiness pbr_specular_glossiness;
429 cgltf_clearcoat clearcoat;
430 cgltf_ior ior;
431 cgltf_specular specular;
432 cgltf_transmission transmission;
433 cgltf_texture_view normal_texture;
434 cgltf_texture_view occlusion_texture;
435 cgltf_texture_view emissive_texture;
436 cgltf_float emissive_factor[3];
437 cgltf_alpha_mode alpha_mode;
438 cgltf_float alpha_cutoff;
439 cgltf_bool double_sided;
440 cgltf_bool unlit;
441 cgltf_extras extras;
442 cgltf_size extensions_count;
443 cgltf_extension* extensions;
444} cgltf_material;
445
446typedef struct cgltf_morph_target {
447 cgltf_attribute* attributes;
448 cgltf_size attributes_count;
449} cgltf_morph_target;
450
451typedef struct cgltf_draco_mesh_compression {
452 cgltf_buffer_view* buffer_view;
453 cgltf_attribute* attributes;
454 cgltf_size attributes_count;
455} cgltf_draco_mesh_compression;
456
457typedef struct cgltf_primitive {
458 cgltf_primitive_type type;
459 cgltf_accessor* indices;
460 cgltf_material* material;
461 cgltf_attribute* attributes;
462 cgltf_size attributes_count;
463 cgltf_morph_target* targets;
464 cgltf_size targets_count;
465 cgltf_extras extras;
466 cgltf_bool has_draco_mesh_compression;
467 cgltf_draco_mesh_compression draco_mesh_compression;
468 cgltf_size extensions_count;
469 cgltf_extension* extensions;
470} cgltf_primitive;
471
472typedef struct cgltf_mesh {
473 char* name;
474 cgltf_primitive* primitives;
475 cgltf_size primitives_count;
476 cgltf_float* weights;
477 cgltf_size weights_count;
478 char** target_names;
479 cgltf_size target_names_count;
480 cgltf_extras extras;
481 cgltf_size extensions_count;
482 cgltf_extension* extensions;
483} cgltf_mesh;
484
485typedef struct cgltf_node cgltf_node;
486
487typedef struct cgltf_skin {
488 char* name;
489 cgltf_node** joints;
490 cgltf_size joints_count;
491 cgltf_node* skeleton;
492 cgltf_accessor* inverse_bind_matrices;
493 cgltf_extras extras;
494 cgltf_size extensions_count;
495 cgltf_extension* extensions;
496} cgltf_skin;
497
498typedef struct cgltf_camera_perspective {
499 cgltf_float aspect_ratio;
500 cgltf_float yfov;
501 cgltf_float zfar;
502 cgltf_float znear;
503 cgltf_extras extras;
504} cgltf_camera_perspective;
505
506typedef struct cgltf_camera_orthographic {
507 cgltf_float xmag;
508 cgltf_float ymag;
509 cgltf_float zfar;
510 cgltf_float znear;
511 cgltf_extras extras;
512} cgltf_camera_orthographic;
513
514typedef struct cgltf_camera {
515 char* name;
516 cgltf_camera_type type;
517 union {
518 cgltf_camera_perspective perspective;
519 cgltf_camera_orthographic orthographic;
520 } data;
521 cgltf_extras extras;
522 cgltf_size extensions_count;
523 cgltf_extension* extensions;
524} cgltf_camera;
525
526typedef struct cgltf_light {
527 char* name;
528 cgltf_float color[3];
529 cgltf_float intensity;
530 cgltf_light_type type;
531 cgltf_float range;
532 cgltf_float spot_inner_cone_angle;
533 cgltf_float spot_outer_cone_angle;
534} cgltf_light;
535
536struct cgltf_node {
537 char* name;
538 cgltf_node* parent;
539 cgltf_node** children;
540 cgltf_size children_count;
541 cgltf_skin* skin;
542 cgltf_mesh* mesh;
543 cgltf_camera* camera;
544 cgltf_light* light;
545 cgltf_float* weights;
546 cgltf_size weights_count;
547 cgltf_bool has_translation;
548 cgltf_bool has_rotation;
549 cgltf_bool has_scale;
550 cgltf_bool has_matrix;
551 cgltf_float translation[3];
552 cgltf_float rotation[4];
553 cgltf_float scale[3];
554 cgltf_float matrix[16];
555 cgltf_extras extras;
556 cgltf_size extensions_count;
557 cgltf_extension* extensions;
558};
559
560typedef struct cgltf_scene {
561 char* name;
562 cgltf_node** nodes;
563 cgltf_size nodes_count;
564 cgltf_extras extras;
565 cgltf_size extensions_count;
566 cgltf_extension* extensions;
567} cgltf_scene;
568
569typedef struct cgltf_animation_sampler {
570 cgltf_accessor* input;
571 cgltf_accessor* output;
572 cgltf_interpolation_type interpolation;
573 cgltf_extras extras;
574 cgltf_size extensions_count;
575 cgltf_extension* extensions;
576} cgltf_animation_sampler;
577
578typedef struct cgltf_animation_channel {
579 cgltf_animation_sampler* sampler;
580 cgltf_node* target_node;
581 cgltf_animation_path_type target_path;
582 cgltf_extras extras;
583 cgltf_size extensions_count;
584 cgltf_extension* extensions;
585} cgltf_animation_channel;
586
587typedef struct cgltf_animation {
588 char* name;
589 cgltf_animation_sampler* samplers;
590 cgltf_size samplers_count;
591 cgltf_animation_channel* channels;
592 cgltf_size channels_count;
593 cgltf_extras extras;
594 cgltf_size extensions_count;
595 cgltf_extension* extensions;
596} cgltf_animation;
597
598typedef struct cgltf_asset {
599 char* copyright;
600 char* generator;
601 char* version;
602 char* min_version;
603 cgltf_extras extras;
604 cgltf_size extensions_count;
605 cgltf_extension* extensions;
606} cgltf_asset;
607
608typedef struct cgltf_data
609{
610 cgltf_file_type file_type;
611 void* file_data;
612
613 cgltf_asset asset;
614
615 cgltf_mesh* meshes;
616 cgltf_size meshes_count;
617
618 cgltf_material* materials;
619 cgltf_size materials_count;
620
621 cgltf_accessor* accessors;
622 cgltf_size accessors_count;
623
624 cgltf_buffer_view* buffer_views;
625 cgltf_size buffer_views_count;
626
627 cgltf_buffer* buffers;
628 cgltf_size buffers_count;
629
630 cgltf_image* images;
631 cgltf_size images_count;
632
633 cgltf_texture* textures;
634 cgltf_size textures_count;
635
636 cgltf_sampler* samplers;
637 cgltf_size samplers_count;
638
639 cgltf_skin* skins;
640 cgltf_size skins_count;
641
642 cgltf_camera* cameras;
643 cgltf_size cameras_count;
644
645 cgltf_light* lights;
646 cgltf_size lights_count;
647
648 cgltf_node* nodes;
649 cgltf_size nodes_count;
650
651 cgltf_scene* scenes;
652 cgltf_size scenes_count;
653
654 cgltf_scene* scene;
655
656 cgltf_animation* animations;
657 cgltf_size animations_count;
658
659 cgltf_extras extras;
660
661 cgltf_size data_extensions_count;
662 cgltf_extension* data_extensions;
663
664 char** extensions_used;
665 cgltf_size extensions_used_count;
666
667 char** extensions_required;
668 cgltf_size extensions_required_count;
669
670 const char* json;
671 cgltf_size json_size;
672
673 const void* bin;
674 cgltf_size bin_size;
675
676 cgltf_memory_options memory;
677 cgltf_file_options file;
678} cgltf_data;
679
680cgltf_result cgltf_parse(
681 const cgltf_options* options,
682 const void* data,
683 cgltf_size size,
684 cgltf_data** out_data);
685
686cgltf_result cgltf_parse_file(
687 const cgltf_options* options,
688 const char* path,
689 cgltf_data** out_data);
690
691cgltf_result cgltf_load_buffers(
692 const cgltf_options* options,
693 cgltf_data* data,
694 const char* gltf_path);
695
696cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data);
697
698void cgltf_decode_uri(char* uri);
699
700cgltf_result cgltf_validate(cgltf_data* data);
701
702void cgltf_free(cgltf_data* data);
703
704void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix);
705void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix);
706
707cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size);
708cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size);
709cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index);
710
711cgltf_size cgltf_num_components(cgltf_type type);
712
713cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count);
714
715cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size);
716
717#ifdef __cplusplus
718}
719#endif
720
721#endif /* #ifndef CGLTF_H_INCLUDED__ */
722
723/*
724 *
725 * Stop now, if you are only interested in the API.
726 * Below, you find the implementation.
727 *
728 */
729
730#if defined(__INTELLISENSE__) || defined(__JETBRAINS_IDE__)
731/* This makes MSVC/CLion intellisense work. */
732#define CGLTF_IMPLEMENTATION
733#endif
734
735#ifdef CGLTF_IMPLEMENTATION
736
737#include <stdint.h> /* For uint8_t, uint32_t */
738#include <string.h> /* For strncpy */
739#include <stdio.h> /* For fopen */
740#include <limits.h> /* For UINT_MAX etc */
741
742#if !defined(CGLTF_MALLOC) || !defined(CGLTF_FREE) || !defined(CGLTF_ATOI) || !defined(CGLTF_ATOF)
743#include <stdlib.h> /* For malloc, free, atoi, atof */
744#endif
745
746/* JSMN_PARENT_LINKS is necessary to make parsing large structures linear in input size */
747#define JSMN_PARENT_LINKS
748
749/* JSMN_STRICT is necessary to reject invalid JSON documents */
750#define JSMN_STRICT
751
752/*
753 * -- jsmn.h start --
754 * Source: https://github.com/zserge/jsmn
755 * License: MIT
756 */
757typedef enum {
758 JSMN_UNDEFINED = 0,
759 JSMN_OBJECT = 1,
760 JSMN_ARRAY = 2,
761 JSMN_STRING = 3,
762 JSMN_PRIMITIVE = 4
763} jsmntype_t;
764enum jsmnerr {
765 /* Not enough tokens were provided */
766 JSMN_ERROR_NOMEM = -1,
767 /* Invalid character inside JSON string */
768 JSMN_ERROR_INVAL = -2,
769 /* The string is not a full JSON packet, more bytes expected */
770 JSMN_ERROR_PART = -3
771};
772typedef struct {
773 jsmntype_t type;
774 int start;
775 int end;
776 int size;
777#ifdef JSMN_PARENT_LINKS
778 int parent;
779#endif
780} jsmntok_t;
781typedef struct {
782 unsigned int pos; /* offset in the JSON string */
783 unsigned int toknext; /* next token to allocate */
784 int toksuper; /* superior token node, e.g parent object or array */
785} jsmn_parser;
786static void jsmn_init(jsmn_parser *parser);
787static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens);
788/*
789 * -- jsmn.h end --
790 */
791
792
793static const cgltf_size GlbHeaderSize = 12;
794static const cgltf_size GlbChunkHeaderSize = 8;
795static const uint32_t GlbVersion = 2;
796static const uint32_t GlbMagic = 0x46546C67;
797static const uint32_t GlbMagicJsonChunk = 0x4E4F534A;
798static const uint32_t GlbMagicBinChunk = 0x004E4942;
799
800#ifndef CGLTF_MALLOC
801#define CGLTF_MALLOC(size) malloc(size)
802#endif
803#ifndef CGLTF_FREE
804#define CGLTF_FREE(ptr) free(ptr)
805#endif
806#ifndef CGLTF_ATOI
807#define CGLTF_ATOI(str) atoi(str)
808#endif
809#ifndef CGLTF_ATOF
810#define CGLTF_ATOF(str) atof(str)
811#endif
812
813static void* cgltf_default_alloc(void* user, cgltf_size size)
814{
815 (void)user;
816 return CGLTF_MALLOC(size);
817}
818
819static void cgltf_default_free(void* user, void* ptr)
820{
821 (void)user;
822 CGLTF_FREE(ptr);
823}
824
825static void* cgltf_calloc(cgltf_options* options, size_t element_size, cgltf_size count)
826{
827 if (SIZE_MAX / element_size < count)
828 {
829 return NULL;
830 }
831 void* result = options->memory.alloc(options->memory.user_data, element_size * count);
832 if (!result)
833 {
834 return NULL;
835 }
836 memset(result, 0, element_size * count);
837 return result;
838}
839
840static cgltf_result cgltf_default_file_read(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data)
841{
842 (void)file_options;
843 void* (*memory_alloc)(void*, cgltf_size) = memory_options->alloc ? memory_options->alloc : &cgltf_default_alloc;
844 void (*memory_free)(void*, void*) = memory_options->free ? memory_options->free : &cgltf_default_free;
845
846 FILE* file = fopen(path, "rb");
847 if (!file)
848 {
849 return cgltf_result_file_not_found;
850 }
851
852 cgltf_size file_size = size ? *size : 0;
853
854 if (file_size == 0)
855 {
856 fseek(file, 0, SEEK_END);
857
858 long length = ftell(file);
859 if (length < 0)
860 {
861 fclose(file);
862 return cgltf_result_io_error;
863 }
864
865 fseek(file, 0, SEEK_SET);
866 file_size = (cgltf_size)length;
867 }
868
869 char* file_data = (char*)memory_alloc(memory_options->user_data, file_size);
870 if (!file_data)
871 {
872 fclose(file);
873 return cgltf_result_out_of_memory;
874 }
875
876 cgltf_size read_size = fread(file_data, 1, file_size, file);
877
878 fclose(file);
879
880 if (read_size != file_size)
881 {
882 memory_free(memory_options->user_data, file_data);
883 return cgltf_result_io_error;
884 }
885
886 if (size)
887 {
888 *size = file_size;
889 }
890 if (data)
891 {
892 *data = file_data;
893 }
894
895 return cgltf_result_success;
896}
897
898static void cgltf_default_file_release(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data)
899{
900 (void)file_options;
901 void (*memfree)(void*, void*) = memory_options->free ? memory_options->free : &cgltf_default_free;
902 memfree(memory_options->user_data, data);
903}
904
905static cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data** out_data);
906
907cgltf_result cgltf_parse(const cgltf_options* options, const void* data, cgltf_size size, cgltf_data** out_data)
908{
909 if (size < GlbHeaderSize)
910 {
911 return cgltf_result_data_too_short;
912 }
913
914 if (options == NULL)
915 {
916 return cgltf_result_invalid_options;
917 }
918
919 cgltf_options fixed_options = *options;
920 if (fixed_options.memory.alloc == NULL)
921 {
922 fixed_options.memory.alloc = &cgltf_default_alloc;
923 }
924 if (fixed_options.memory.free == NULL)
925 {
926 fixed_options.memory.free = &cgltf_default_free;
927 }
928
929 uint32_t tmp;
930 // Magic
931 memcpy(&tmp, data, 4);
932 if (tmp != GlbMagic)
933 {
934 if (fixed_options.type == cgltf_file_type_invalid)
935 {
936 fixed_options.type = cgltf_file_type_gltf;
937 }
938 else if (fixed_options.type == cgltf_file_type_glb)
939 {
940 return cgltf_result_unknown_format;
941 }
942 }
943
944 if (fixed_options.type == cgltf_file_type_gltf)
945 {
946 cgltf_result json_result = cgltf_parse_json(&fixed_options, (const uint8_t*)data, size, out_data);
947 if (json_result != cgltf_result_success)
948 {
949 return json_result;
950 }
951
952 (*out_data)->file_type = cgltf_file_type_gltf;
953
954 return cgltf_result_success;
955 }
956
957 const uint8_t* ptr = (const uint8_t*)data;
958 // Version
959 memcpy(&tmp, ptr + 4, 4);
960 uint32_t version = tmp;
961 if (version != GlbVersion)
962 {
963 return version < GlbVersion ? cgltf_result_legacy_gltf : cgltf_result_unknown_format;
964 }
965
966 // Total length
967 memcpy(&tmp, ptr + 8, 4);
968 if (tmp > size)
969 {
970 return cgltf_result_data_too_short;
971 }
972
973 const uint8_t* json_chunk = ptr + GlbHeaderSize;
974
975 if (GlbHeaderSize + GlbChunkHeaderSize > size)
976 {
977 return cgltf_result_data_too_short;
978 }
979
980 // JSON chunk: length
981 uint32_t json_length;
982 memcpy(&json_length, json_chunk, 4);
983 if (GlbHeaderSize + GlbChunkHeaderSize + json_length > size)
984 {
985 return cgltf_result_data_too_short;
986 }
987
988 // JSON chunk: magic
989 memcpy(&tmp, json_chunk + 4, 4);
990 if (tmp != GlbMagicJsonChunk)
991 {
992 return cgltf_result_unknown_format;
993 }
994
995 json_chunk += GlbChunkHeaderSize;
996
997 const void* bin = 0;
998 cgltf_size bin_size = 0;
999
1000 if (GlbHeaderSize + GlbChunkHeaderSize + json_length + GlbChunkHeaderSize <= size)
1001 {
1002 // We can read another chunk
1003 const uint8_t* bin_chunk = json_chunk + json_length;
1004
1005 // Bin chunk: length
1006 uint32_t bin_length;
1007 memcpy(&bin_length, bin_chunk, 4);
1008 if (GlbHeaderSize + GlbChunkHeaderSize + json_length + GlbChunkHeaderSize + bin_length > size)
1009 {
1010 return cgltf_result_data_too_short;
1011 }
1012
1013 // Bin chunk: magic
1014 memcpy(&tmp, bin_chunk + 4, 4);
1015 if (tmp != GlbMagicBinChunk)
1016 {
1017 return cgltf_result_unknown_format;
1018 }
1019
1020 bin_chunk += GlbChunkHeaderSize;
1021
1022 bin = bin_chunk;
1023 bin_size = bin_length;
1024 }
1025
1026 cgltf_result json_result = cgltf_parse_json(&fixed_options, json_chunk, json_length, out_data);
1027 if (json_result != cgltf_result_success)
1028 {
1029 return json_result;
1030 }
1031
1032 (*out_data)->file_type = cgltf_file_type_glb;
1033 (*out_data)->bin = bin;
1034 (*out_data)->bin_size = bin_size;
1035
1036 return cgltf_result_success;
1037}
1038
1039cgltf_result cgltf_parse_file(const cgltf_options* options, const char* path, cgltf_data** out_data)
1040{
1041 if (options == NULL)
1042 {
1043 return cgltf_result_invalid_options;
1044 }
1045
1046 void (*memory_free)(void*, void*) = options->memory.free ? options->memory.free : &cgltf_default_free;
1047 cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read;
1048
1049 void* file_data = NULL;
1050 cgltf_size file_size = 0;
1051 cgltf_result result = file_read(&options->memory, &options->file, path, &file_size, &file_data);
1052 if (result != cgltf_result_success)
1053 {
1054 return result;
1055 }
1056
1057 result = cgltf_parse(options, file_data, file_size, out_data);
1058
1059 if (result != cgltf_result_success)
1060 {
1061 memory_free(options->memory.user_data, file_data);
1062 return result;
1063 }
1064
1065 (*out_data)->file_data = file_data;
1066
1067 return cgltf_result_success;
1068}
1069
1070static void cgltf_combine_paths(char* path, const char* base, const char* uri)
1071{
1072 const char* s0 = strrchr(base, '/');
1073 const char* s1 = strrchr(base, '\\');
1074 const char* slash = s0 ? (s1 && s1 > s0 ? s1 : s0) : s1;
1075
1076 if (slash)
1077 {
1078 size_t prefix = slash - base + 1;
1079
1080 strncpy(path, base, prefix);
1081 strcpy(path + prefix, uri);
1082 }
1083 else
1084 {
1085 strcpy(path, uri);
1086 }
1087}
1088
1089static cgltf_result cgltf_load_buffer_file(const cgltf_options* options, cgltf_size size, const char* uri, const char* gltf_path, void** out_data)
1090{
1091 void* (*memory_alloc)(void*, cgltf_size) = options->memory.alloc ? options->memory.alloc : &cgltf_default_alloc;
1092 void (*memory_free)(void*, void*) = options->memory.free ? options->memory.free : &cgltf_default_free;
1093 cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read;
1094
1095 char* path = (char*)memory_alloc(options->memory.user_data, strlen(uri) + strlen(gltf_path) + 1);
1096 if (!path)
1097 {
1098 return cgltf_result_out_of_memory;
1099 }
1100
1101 cgltf_combine_paths(path, gltf_path, uri);
1102
1103 // after combining, the tail of the resulting path is a uri; decode_uri converts it into path
1104 cgltf_decode_uri(path + strlen(path) - strlen(uri));
1105
1106 void* file_data = NULL;
1107 cgltf_result result = file_read(&options->memory, &options->file, path, &size, &file_data);
1108
1109 memory_free(options->memory.user_data, path);
1110
1111 *out_data = (result == cgltf_result_success) ? file_data : NULL;
1112
1113 return result;
1114}
1115
1116cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data)
1117{
1118 void* (*memory_alloc)(void*, cgltf_size) = options->memory.alloc ? options->memory.alloc : &cgltf_default_alloc;
1119 void (*memory_free)(void*, void*) = options->memory.free ? options->memory.free : &cgltf_default_free;
1120
1121 unsigned char* data = (unsigned char*)memory_alloc(options->memory.user_data, size);
1122 if (!data)
1123 {
1124 return cgltf_result_out_of_memory;
1125 }
1126
1127 unsigned int buffer = 0;
1128 unsigned int buffer_bits = 0;
1129
1130 for (cgltf_size i = 0; i < size; ++i)
1131 {
1132 while (buffer_bits < 8)
1133 {
1134 char ch = *base64++;
1135
1136 int index =
1137 (unsigned)(ch - 'A') < 26 ? (ch - 'A') :
1138 (unsigned)(ch - 'a') < 26 ? (ch - 'a') + 26 :
1139 (unsigned)(ch - '0') < 10 ? (ch - '0') + 52 :
1140 ch == '+' ? 62 :
1141 ch == '/' ? 63 :
1142 -1;
1143
1144 if (index < 0)
1145 {
1146 memory_free(options->memory.user_data, data);
1147 return cgltf_result_io_error;
1148 }
1149
1150 buffer = (buffer << 6) | index;
1151 buffer_bits += 6;
1152 }
1153
1154 data[i] = (unsigned char)(buffer >> (buffer_bits - 8));
1155 buffer_bits -= 8;
1156 }
1157
1158 *out_data = data;
1159
1160 return cgltf_result_success;
1161}
1162
1163static int cgltf_unhex(char ch)
1164{
1165 return
1166 (unsigned)(ch - '0') < 10 ? (ch - '0') :
1167 (unsigned)(ch - 'A') < 6 ? (ch - 'A') + 10 :
1168 (unsigned)(ch - 'a') < 6 ? (ch - 'a') + 10 :
1169 -1;
1170}
1171
1172void cgltf_decode_uri(char* uri)
1173{
1174 char* write = uri;
1175 char* i = uri;
1176
1177 while (*i)
1178 {
1179 if (*i == '%')
1180 {
1181 int ch1 = cgltf_unhex(i[1]);
1182
1183 if (ch1 >= 0)
1184 {
1185 int ch2 = cgltf_unhex(i[2]);
1186
1187 if (ch2 >= 0)
1188 {
1189 *write++ = (char)(ch1 * 16 + ch2);
1190 i += 3;
1191 continue;
1192 }
1193 }
1194 }
1195
1196 *write++ = *i++;
1197 }
1198
1199 *write = 0;
1200}
1201
1202cgltf_result cgltf_load_buffers(const cgltf_options* options, cgltf_data* data, const char* gltf_path)
1203{
1204 if (options == NULL)
1205 {
1206 return cgltf_result_invalid_options;
1207 }
1208
1209 if (data->buffers_count && data->buffers[0].data == NULL && data->buffers[0].uri == NULL && data->bin)
1210 {
1211 if (data->bin_size < data->buffers[0].size)
1212 {
1213 return cgltf_result_data_too_short;
1214 }
1215
1216 data->buffers[0].data = (void*)data->bin;
1217 }
1218
1219 for (cgltf_size i = 0; i < data->buffers_count; ++i)
1220 {
1221 if (data->buffers[i].data)
1222 {
1223 continue;
1224 }
1225
1226 const char* uri = data->buffers[i].uri;
1227
1228 if (uri == NULL)
1229 {
1230 continue;
1231 }
1232
1233 if (strncmp(uri, "data:", 5) == 0)
1234 {
1235 const char* comma = strchr(uri, ',');
1236
1237 if (comma && comma - uri >= 7 && strncmp(comma - 7, ";base64", 7) == 0)
1238 {
1239 cgltf_result res = cgltf_load_buffer_base64(options, data->buffers[i].size, comma + 1, &data->buffers[i].data);
1240
1241 if (res != cgltf_result_success)
1242 {
1243 return res;
1244 }
1245 }
1246 else
1247 {
1248 return cgltf_result_unknown_format;
1249 }
1250 }
1251 else if (strstr(uri, "://") == NULL && gltf_path)
1252 {
1253 cgltf_result res = cgltf_load_buffer_file(options, data->buffers[i].size, uri, gltf_path, &data->buffers[i].data);
1254
1255 if (res != cgltf_result_success)
1256 {
1257 return res;
1258 }
1259 }
1260 else
1261 {
1262 return cgltf_result_unknown_format;
1263 }
1264 }
1265
1266 return cgltf_result_success;
1267}
1268
1269static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type);
1270
1271static cgltf_size cgltf_calc_index_bound(cgltf_buffer_view* buffer_view, cgltf_size offset, cgltf_component_type component_type, cgltf_size count)
1272{
1273 char* data = (char*)buffer_view->buffer->data + offset + buffer_view->offset;
1274 cgltf_size bound = 0;
1275
1276 switch (component_type)
1277 {
1278 case cgltf_component_type_r_8u:
1279 for (size_t i = 0; i < count; ++i)
1280 {
1281 cgltf_size v = ((unsigned char*)data)[i];
1282 bound = bound > v ? bound : v;
1283 }
1284 break;
1285
1286 case cgltf_component_type_r_16u:
1287 for (size_t i = 0; i < count; ++i)
1288 {
1289 cgltf_size v = ((unsigned short*)data)[i];
1290 bound = bound > v ? bound : v;
1291 }
1292 break;
1293
1294 case cgltf_component_type_r_32u:
1295 for (size_t i = 0; i < count; ++i)
1296 {
1297 cgltf_size v = ((unsigned int*)data)[i];
1298 bound = bound > v ? bound : v;
1299 }
1300 break;
1301
1302 default:
1303 ;
1304 }
1305
1306 return bound;
1307}
1308
1309cgltf_result cgltf_validate(cgltf_data* data)
1310{
1311 for (cgltf_size i = 0; i < data->accessors_count; ++i)
1312 {
1313 cgltf_accessor* accessor = &data->accessors[i];
1314
1315 cgltf_size element_size = cgltf_calc_size(accessor->type, accessor->component_type);
1316
1317 if (accessor->buffer_view)
1318 {
1319 cgltf_size req_size = accessor->offset + accessor->stride * (accessor->count - 1) + element_size;
1320
1321 if (accessor->buffer_view->size < req_size)
1322 {
1323 return cgltf_result_data_too_short;
1324 }
1325 }
1326
1327 if (accessor->is_sparse)
1328 {
1329 cgltf_accessor_sparse* sparse = &accessor->sparse;
1330
1331 cgltf_size indices_component_size = cgltf_calc_size(cgltf_type_scalar, sparse->indices_component_type);
1332 cgltf_size indices_req_size = sparse->indices_byte_offset + indices_component_size * sparse->count;
1333 cgltf_size values_req_size = sparse->values_byte_offset + element_size * sparse->count;
1334
1335 if (sparse->indices_buffer_view->size < indices_req_size ||
1336 sparse->values_buffer_view->size < values_req_size)
1337 {
1338 return cgltf_result_data_too_short;
1339 }
1340
1341 if (sparse->indices_component_type != cgltf_component_type_r_8u &&
1342 sparse->indices_component_type != cgltf_component_type_r_16u &&
1343 sparse->indices_component_type != cgltf_component_type_r_32u)
1344 {
1345 return cgltf_result_invalid_gltf;
1346 }
1347
1348 if (sparse->indices_buffer_view->buffer->data)
1349 {
1350 cgltf_size index_bound = cgltf_calc_index_bound(sparse->indices_buffer_view, sparse->indices_byte_offset, sparse->indices_component_type, sparse->count);
1351
1352 if (index_bound >= accessor->count)
1353 {
1354 return cgltf_result_data_too_short;
1355 }
1356 }
1357 }
1358 }
1359
1360 for (cgltf_size i = 0; i < data->buffer_views_count; ++i)
1361 {
1362 cgltf_size req_size = data->buffer_views[i].offset + data->buffer_views[i].size;
1363
1364 if (data->buffer_views[i].buffer && data->buffer_views[i].buffer->size < req_size)
1365 {
1366 return cgltf_result_data_too_short;
1367 }
1368 }
1369
1370 for (cgltf_size i = 0; i < data->meshes_count; ++i)
1371 {
1372 if (data->meshes[i].weights)
1373 {
1374 if (data->meshes[i].primitives_count && data->meshes[i].primitives[0].targets_count != data->meshes[i].weights_count)
1375 {
1376 return cgltf_result_invalid_gltf;
1377 }
1378 }
1379
1380 if (data->meshes[i].target_names)
1381 {
1382 if (data->meshes[i].primitives_count && data->meshes[i].primitives[0].targets_count != data->meshes[i].target_names_count)
1383 {
1384 return cgltf_result_invalid_gltf;
1385 }
1386 }
1387
1388 for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j)
1389 {
1390 if (data->meshes[i].primitives[j].targets_count != data->meshes[i].primitives[0].targets_count)
1391 {
1392 return cgltf_result_invalid_gltf;
1393 }
1394
1395 if (data->meshes[i].primitives[j].attributes_count)
1396 {
1397 cgltf_accessor* first = data->meshes[i].primitives[j].attributes[0].data;
1398
1399 for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k)
1400 {
1401 if (data->meshes[i].primitives[j].attributes[k].data->count != first->count)
1402 {
1403 return cgltf_result_invalid_gltf;
1404 }
1405 }
1406
1407 for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k)
1408 {
1409 for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m)
1410 {
1411 if (data->meshes[i].primitives[j].targets[k].attributes[m].data->count != first->count)
1412 {
1413 return cgltf_result_invalid_gltf;
1414 }
1415 }
1416 }
1417
1418 cgltf_accessor* indices = data->meshes[i].primitives[j].indices;
1419
1420 if (indices &&
1421 indices->component_type != cgltf_component_type_r_8u &&
1422 indices->component_type != cgltf_component_type_r_16u &&
1423 indices->component_type != cgltf_component_type_r_32u)
1424 {
1425 return cgltf_result_invalid_gltf;
1426 }
1427
1428 if (indices && indices->buffer_view && indices->buffer_view->buffer->data)
1429 {
1430 cgltf_size index_bound = cgltf_calc_index_bound(indices->buffer_view, indices->offset, indices->component_type, indices->count);
1431
1432 if (index_bound >= first->count)
1433 {
1434 return cgltf_result_data_too_short;
1435 }
1436 }
1437 }
1438 }
1439 }
1440
1441 for (cgltf_size i = 0; i < data->nodes_count; ++i)
1442 {
1443 if (data->nodes[i].weights && data->nodes[i].mesh)
1444 {
1445 if (data->nodes[i].mesh->primitives_count && data->nodes[i].mesh->primitives[0].targets_count != data->nodes[i].weights_count)
1446 {
1447 return cgltf_result_invalid_gltf;
1448 }
1449 }
1450 }
1451
1452 for (cgltf_size i = 0; i < data->nodes_count; ++i)
1453 {
1454 cgltf_node* p1 = data->nodes[i].parent;
1455 cgltf_node* p2 = p1 ? p1->parent : NULL;
1456
1457 while (p1 && p2)
1458 {
1459 if (p1 == p2)
1460 {
1461 return cgltf_result_invalid_gltf;
1462 }
1463
1464 p1 = p1->parent;
1465 p2 = p2->parent ? p2->parent->parent : NULL;
1466 }
1467 }
1468
1469 for (cgltf_size i = 0; i < data->scenes_count; ++i)
1470 {
1471 for (cgltf_size j = 0; j < data->scenes[i].nodes_count; ++j)
1472 {
1473 if (data->scenes[i].nodes[j]->parent)
1474 {
1475 return cgltf_result_invalid_gltf;
1476 }
1477 }
1478 }
1479
1480 for (cgltf_size i = 0; i < data->animations_count; ++i)
1481 {
1482 for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j)
1483 {
1484 cgltf_animation_channel* channel = &data->animations[i].channels[j];
1485
1486 if (!channel->target_node)
1487 {
1488 continue;
1489 }
1490
1491 cgltf_size components = 1;
1492
1493 if (channel->target_path == cgltf_animation_path_type_weights)
1494 {
1495 if (!channel->target_node->mesh || !channel->target_node->mesh->primitives_count)
1496 {
1497 return cgltf_result_invalid_gltf;
1498 }
1499
1500 components = channel->target_node->mesh->primitives[0].targets_count;
1501 }
1502
1503 cgltf_size values = channel->sampler->interpolation == cgltf_interpolation_type_cubic_spline ? 3 : 1;
1504
1505 if (channel->sampler->input->count * components * values != channel->sampler->output->count)
1506 {
1507 return cgltf_result_data_too_short;
1508 }
1509 }
1510 }
1511
1512 return cgltf_result_success;
1513}
1514
1515cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size)
1516{
1517 cgltf_size json_size = extras->end_offset - extras->start_offset;
1518
1519 if (!dest)
1520 {
1521 if (dest_size)
1522 {
1523 *dest_size = json_size + 1;
1524 return cgltf_result_success;
1525 }
1526 return cgltf_result_invalid_options;
1527 }
1528
1529 if (*dest_size + 1 < json_size)
1530 {
1531 strncpy(dest, data->json + extras->start_offset, *dest_size - 1);
1532 dest[*dest_size - 1] = 0;
1533 }
1534 else
1535 {
1536 strncpy(dest, data->json + extras->start_offset, json_size);
1537 dest[json_size] = 0;
1538 }
1539
1540 return cgltf_result_success;
1541}
1542
1543void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_size extensions_count)
1544{
1545 for (cgltf_size i = 0; i < extensions_count; ++i)
1546 {
1547 data->memory.free(data->memory.user_data, extensions[i].name);
1548 data->memory.free(data->memory.user_data, extensions[i].data);
1549 }
1550 data->memory.free(data->memory.user_data, extensions);
1551}
1552
1553void cgltf_free(cgltf_data* data)
1554{
1555 if (!data)
1556 {
1557 return;
1558 }
1559
1560 void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = data->file.release ? data->file.release : cgltf_default_file_release;
1561
1562 data->memory.free(data->memory.user_data, data->asset.copyright);
1563 data->memory.free(data->memory.user_data, data->asset.generator);
1564 data->memory.free(data->memory.user_data, data->asset.version);
1565 data->memory.free(data->memory.user_data, data->asset.min_version);
1566
1567 cgltf_free_extensions(data, data->asset.extensions, data->asset.extensions_count);
1568
1569 for (cgltf_size i = 0; i < data->accessors_count; ++i)
1570 {
1571 if(data->accessors[i].is_sparse)
1572 {
1573 cgltf_free_extensions(data, data->accessors[i].sparse.extensions, data->accessors[i].sparse.extensions_count);
1574 cgltf_free_extensions(data, data->accessors[i].sparse.indices_extensions, data->accessors[i].sparse.indices_extensions_count);
1575 cgltf_free_extensions(data, data->accessors[i].sparse.values_extensions, data->accessors[i].sparse.values_extensions_count);
1576 }
1577 cgltf_free_extensions(data, data->accessors[i].extensions, data->accessors[i].extensions_count);
1578 }
1579 data->memory.free(data->memory.user_data, data->accessors);
1580
1581 for (cgltf_size i = 0; i < data->buffer_views_count; ++i)
1582 {
1583 cgltf_free_extensions(data, data->buffer_views[i].extensions, data->buffer_views[i].extensions_count);
1584 }
1585 data->memory.free(data->memory.user_data, data->buffer_views);
1586
1587 for (cgltf_size i = 0; i < data->buffers_count; ++i)
1588 {
1589 if (data->buffers[i].data != data->bin)
1590 {
1591 file_release(&data->memory, &data->file, data->buffers[i].data);
1592 }
1593 data->memory.free(data->memory.user_data, data->buffers[i].uri);
1594
1595 cgltf_free_extensions(data, data->buffers[i].extensions, data->buffers[i].extensions_count);
1596 }
1597
1598 data->memory.free(data->memory.user_data, data->buffers);
1599
1600 for (cgltf_size i = 0; i < data->meshes_count; ++i)
1601 {
1602 data->memory.free(data->memory.user_data, data->meshes[i].name);
1603
1604 for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j)
1605 {
1606 for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k)
1607 {
1608 data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].attributes[k].name);
1609 }
1610
1611 data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].attributes);
1612
1613 for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k)
1614 {
1615 for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m)
1616 {
1617 data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].targets[k].attributes[m].name);
1618 }
1619
1620 data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].targets[k].attributes);
1621 }
1622
1623 data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].targets);
1624
1625 if (data->meshes[i].primitives[j].has_draco_mesh_compression)
1626 {
1627 for (cgltf_size k = 0; k < data->meshes[i].primitives[j].draco_mesh_compression.attributes_count; ++k)
1628 {
1629 data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes[k].name);
1630 }
1631
1632 data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes);
1633 }
1634
1635 cgltf_free_extensions(data, data->meshes[i].primitives[j].extensions, data->meshes[i].primitives[j].extensions_count);
1636 }
1637
1638 data->memory.free(data->memory.user_data, data->meshes[i].primitives);
1639 data->memory.free(data->memory.user_data, data->meshes[i].weights);
1640
1641 for (cgltf_size j = 0; j < data->meshes[i].target_names_count; ++j)
1642 {
1643 data->memory.free(data->memory.user_data, data->meshes[i].target_names[j]);
1644 }
1645
1646 cgltf_free_extensions(data, data->meshes[i].extensions, data->meshes[i].extensions_count);
1647
1648 data->memory.free(data->memory.user_data, data->meshes[i].target_names);
1649 }
1650
1651 data->memory.free(data->memory.user_data, data->meshes);
1652
1653 for (cgltf_size i = 0; i < data->materials_count; ++i)
1654 {
1655 data->memory.free(data->memory.user_data, data->materials[i].name);
1656
1657 if(data->materials[i].has_pbr_metallic_roughness)
1658 {
1659 cgltf_free_extensions(data, data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.extensions, data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.extensions_count);
1660 cgltf_free_extensions(data, data->materials[i].pbr_metallic_roughness.base_color_texture.extensions, data->materials[i].pbr_metallic_roughness.base_color_texture.extensions_count);
1661 }
1662 if(data->materials[i].has_pbr_specular_glossiness)
1663 {
1664 cgltf_free_extensions(data, data->materials[i].pbr_specular_glossiness.diffuse_texture.extensions, data->materials[i].pbr_specular_glossiness.diffuse_texture.extensions_count);
1665 cgltf_free_extensions(data, data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.extensions, data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.extensions_count);
1666 }
1667 if(data->materials[i].has_clearcoat)
1668 {
1669 cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_texture.extensions, data->materials[i].clearcoat.clearcoat_texture.extensions_count);
1670 cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_roughness_texture.extensions, data->materials[i].clearcoat.clearcoat_roughness_texture.extensions_count);
1671 cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_normal_texture.extensions, data->materials[i].clearcoat.clearcoat_normal_texture.extensions_count);
1672 }
1673 if(data->materials[i].has_specular)
1674 {
1675 cgltf_free_extensions(data, data->materials[i].specular.specular_texture.extensions, data->materials[i].specular.specular_texture.extensions_count);
1676 }
1677 if(data->materials[i].has_transmission)
1678 {
1679 cgltf_free_extensions(data, data->materials[i].transmission.transmission_texture.extensions, data->materials[i].transmission.transmission_texture.extensions_count);
1680 }
1681
1682 cgltf_free_extensions(data, data->materials[i].normal_texture.extensions, data->materials[i].normal_texture.extensions_count);
1683 cgltf_free_extensions(data, data->materials[i].occlusion_texture.extensions, data->materials[i].occlusion_texture.extensions_count);
1684 cgltf_free_extensions(data, data->materials[i].emissive_texture.extensions, data->materials[i].emissive_texture.extensions_count);
1685
1686 cgltf_free_extensions(data, data->materials[i].extensions, data->materials[i].extensions_count);
1687 }
1688
1689 data->memory.free(data->memory.user_data, data->materials);
1690
1691 for (cgltf_size i = 0; i < data->images_count; ++i)
1692 {
1693 data->memory.free(data->memory.user_data, data->images[i].name);
1694 data->memory.free(data->memory.user_data, data->images[i].uri);
1695 data->memory.free(data->memory.user_data, data->images[i].mime_type);
1696
1697 cgltf_free_extensions(data, data->images[i].extensions, data->images[i].extensions_count);
1698 }
1699
1700 data->memory.free(data->memory.user_data, data->images);
1701
1702 for (cgltf_size i = 0; i < data->textures_count; ++i)
1703 {
1704 data->memory.free(data->memory.user_data, data->textures[i].name);
1705 cgltf_free_extensions(data, data->textures[i].extensions, data->textures[i].extensions_count);
1706 }
1707
1708 data->memory.free(data->memory.user_data, data->textures);
1709
1710 for (cgltf_size i = 0; i < data->samplers_count; ++i)
1711 {
1712 cgltf_free_extensions(data, data->samplers[i].extensions, data->samplers[i].extensions_count);
1713 }
1714
1715 data->memory.free(data->memory.user_data, data->samplers);
1716
1717 for (cgltf_size i = 0; i < data->skins_count; ++i)
1718 {
1719 data->memory.free(data->memory.user_data, data->skins[i].name);
1720 data->memory.free(data->memory.user_data, data->skins[i].joints);
1721
1722 cgltf_free_extensions(data, data->skins[i].extensions, data->skins[i].extensions_count);
1723 }
1724
1725 data->memory.free(data->memory.user_data, data->skins);
1726
1727 for (cgltf_size i = 0; i < data->cameras_count; ++i)
1728 {
1729 data->memory.free(data->memory.user_data, data->cameras[i].name);
1730 cgltf_free_extensions(data, data->cameras[i].extensions, data->cameras[i].extensions_count);
1731 }
1732
1733 data->memory.free(data->memory.user_data, data->cameras);
1734
1735 for (cgltf_size i = 0; i < data->lights_count; ++i)
1736 {
1737 data->memory.free(data->memory.user_data, data->lights[i].name);
1738 }
1739
1740 data->memory.free(data->memory.user_data, data->lights);
1741
1742 for (cgltf_size i = 0; i < data->nodes_count; ++i)
1743 {
1744 data->memory.free(data->memory.user_data, data->nodes[i].name);
1745 data->memory.free(data->memory.user_data, data->nodes[i].children);
1746 data->memory.free(data->memory.user_data, data->nodes[i].weights);
1747 cgltf_free_extensions(data, data->nodes[i].extensions, data->nodes[i].extensions_count);
1748 }
1749
1750 data->memory.free(data->memory.user_data, data->nodes);
1751
1752 for (cgltf_size i = 0; i < data->scenes_count; ++i)
1753 {
1754 data->memory.free(data->memory.user_data, data->scenes[i].name);
1755 data->memory.free(data->memory.user_data, data->scenes[i].nodes);
1756
1757 cgltf_free_extensions(data, data->scenes[i].extensions, data->scenes[i].extensions_count);
1758 }
1759
1760 data->memory.free(data->memory.user_data, data->scenes);
1761
1762 for (cgltf_size i = 0; i < data->animations_count; ++i)
1763 {
1764 data->memory.free(data->memory.user_data, data->animations[i].name);
1765 for (cgltf_size j = 0; j < data->animations[i].samplers_count; ++j)
1766 {
1767 cgltf_free_extensions(data, data->animations[i].samplers[j].extensions, data->animations[i].samplers[j].extensions_count);
1768 }
1769 data->memory.free(data->memory.user_data, data->animations[i].samplers);
1770
1771 for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j)
1772 {
1773 cgltf_free_extensions(data, data->animations[i].channels[j].extensions, data->animations[i].channels[j].extensions_count);
1774 }
1775 data->memory.free(data->memory.user_data, data->animations[i].channels);
1776
1777 cgltf_free_extensions(data, data->animations[i].extensions, data->animations[i].extensions_count);
1778 }
1779
1780 data->memory.free(data->memory.user_data, data->animations);
1781
1782 cgltf_free_extensions(data, data->data_extensions, data->data_extensions_count);
1783
1784 for (cgltf_size i = 0; i < data->extensions_used_count; ++i)
1785 {
1786 data->memory.free(data->memory.user_data, data->extensions_used[i]);
1787 }
1788
1789 data->memory.free(data->memory.user_data, data->extensions_used);
1790
1791 for (cgltf_size i = 0; i < data->extensions_required_count; ++i)
1792 {
1793 data->memory.free(data->memory.user_data, data->extensions_required[i]);
1794 }
1795
1796 data->memory.free(data->memory.user_data, data->extensions_required);
1797
1798 file_release(&data->memory, &data->file, data->file_data);
1799
1800 data->memory.free(data->memory.user_data, data);
1801}
1802
1803void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix)
1804{
1805 cgltf_float* lm = out_matrix;
1806
1807 if (node->has_matrix)
1808 {
1809 memcpy(lm, node->matrix, sizeof(float) * 16);
1810 }
1811 else
1812 {
1813 float tx = node->translation[0];
1814 float ty = node->translation[1];
1815 float tz = node->translation[2];
1816
1817 float qx = node->rotation[0];
1818 float qy = node->rotation[1];
1819 float qz = node->rotation[2];
1820 float qw = node->rotation[3];
1821
1822 float sx = node->scale[0];
1823 float sy = node->scale[1];
1824 float sz = node->scale[2];
1825
1826 lm[0] = (1 - 2 * qy*qy - 2 * qz*qz) * sx;
1827 lm[1] = (2 * qx*qy + 2 * qz*qw) * sx;
1828 lm[2] = (2 * qx*qz - 2 * qy*qw) * sx;
1829 lm[3] = 0.f;
1830
1831 lm[4] = (2 * qx*qy - 2 * qz*qw) * sy;
1832 lm[5] = (1 - 2 * qx*qx - 2 * qz*qz) * sy;
1833 lm[6] = (2 * qy*qz + 2 * qx*qw) * sy;
1834 lm[7] = 0.f;
1835
1836 lm[8] = (2 * qx*qz + 2 * qy*qw) * sz;
1837 lm[9] = (2 * qy*qz - 2 * qx*qw) * sz;
1838 lm[10] = (1 - 2 * qx*qx - 2 * qy*qy) * sz;
1839 lm[11] = 0.f;
1840
1841 lm[12] = tx;
1842 lm[13] = ty;
1843 lm[14] = tz;
1844 lm[15] = 1.f;
1845 }
1846}
1847
1848void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix)
1849{
1850 cgltf_float* lm = out_matrix;
1851 cgltf_node_transform_local(node, lm);
1852
1853 const cgltf_node* parent = node->parent;
1854
1855 while (parent)
1856 {
1857 float pm[16];
1858 cgltf_node_transform_local(parent, pm);
1859
1860 for (int i = 0; i < 4; ++i)
1861 {
1862 float l0 = lm[i * 4 + 0];
1863 float l1 = lm[i * 4 + 1];
1864 float l2 = lm[i * 4 + 2];
1865
1866 float r0 = l0 * pm[0] + l1 * pm[4] + l2 * pm[8];
1867 float r1 = l0 * pm[1] + l1 * pm[5] + l2 * pm[9];
1868 float r2 = l0 * pm[2] + l1 * pm[6] + l2 * pm[10];
1869
1870 lm[i * 4 + 0] = r0;
1871 lm[i * 4 + 1] = r1;
1872 lm[i * 4 + 2] = r2;
1873 }
1874
1875 lm[12] += pm[12];
1876 lm[13] += pm[13];
1877 lm[14] += pm[14];
1878
1879 parent = parent->parent;
1880 }
1881}
1882
1883static cgltf_size cgltf_component_read_index(const void* in, cgltf_component_type component_type)
1884{
1885 switch (component_type)
1886 {
1887 case cgltf_component_type_r_16:
1888 return *((const int16_t*) in);
1889 case cgltf_component_type_r_16u:
1890 return *((const uint16_t*) in);
1891 case cgltf_component_type_r_32u:
1892 return *((const uint32_t*) in);
1893 case cgltf_component_type_r_32f:
1894 return (cgltf_size)*((const float*) in);
1895 case cgltf_component_type_r_8:
1896 return *((const int8_t*) in);
1897 case cgltf_component_type_r_8u:
1898 return *((const uint8_t*) in);
1899 default:
1900 return 0;
1901 }
1902}
1903
1904static cgltf_float cgltf_component_read_float(const void* in, cgltf_component_type component_type, cgltf_bool normalized)
1905{
1906 if (component_type == cgltf_component_type_r_32f)
1907 {
1908 return *((const float*) in);
1909 }
1910
1911 if (normalized)
1912 {
1913 switch (component_type)
1914 {
1915 // note: glTF spec doesn't currently define normalized conversions for 32-bit integers
1916 case cgltf_component_type_r_16:
1917 return *((const int16_t*) in) / (cgltf_float)32767;
1918 case cgltf_component_type_r_16u:
1919 return *((const uint16_t*) in) / (cgltf_float)65535;
1920 case cgltf_component_type_r_8:
1921 return *((const int8_t*) in) / (cgltf_float)127;
1922 case cgltf_component_type_r_8u:
1923 return *((const uint8_t*) in) / (cgltf_float)255;
1924 default:
1925 return 0;
1926 }
1927 }
1928
1929 return (cgltf_float)cgltf_component_read_index(in, component_type);
1930}
1931
1932static cgltf_size cgltf_component_size(cgltf_component_type component_type);
1933
1934static cgltf_bool cgltf_element_read_float(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_bool normalized, cgltf_float* out, cgltf_size element_size)
1935{
1936 cgltf_size num_components = cgltf_num_components(type);
1937
1938 if (element_size < num_components) {
1939 return 0;
1940 }
1941
1942 // There are three special cases for component extraction, see #data-alignment in the 2.0 spec.
1943
1944 cgltf_size component_size = cgltf_component_size(component_type);
1945
1946 if (type == cgltf_type_mat2 && component_size == 1)
1947 {
1948 out[0] = cgltf_component_read_float(element, component_type, normalized);
1949 out[1] = cgltf_component_read_float(element + 1, component_type, normalized);
1950 out[2] = cgltf_component_read_float(element + 4, component_type, normalized);
1951 out[3] = cgltf_component_read_float(element + 5, component_type, normalized);
1952 return 1;
1953 }
1954
1955 if (type == cgltf_type_mat3 && component_size == 1)
1956 {
1957 out[0] = cgltf_component_read_float(element, component_type, normalized);
1958 out[1] = cgltf_component_read_float(element + 1, component_type, normalized);
1959 out[2] = cgltf_component_read_float(element + 2, component_type, normalized);
1960 out[3] = cgltf_component_read_float(element + 4, component_type, normalized);
1961 out[4] = cgltf_component_read_float(element + 5, component_type, normalized);
1962 out[5] = cgltf_component_read_float(element + 6, component_type, normalized);
1963 out[6] = cgltf_component_read_float(element + 8, component_type, normalized);
1964 out[7] = cgltf_component_read_float(element + 9, component_type, normalized);
1965 out[8] = cgltf_component_read_float(element + 10, component_type, normalized);
1966 return 1;
1967 }
1968
1969 if (type == cgltf_type_mat3 && component_size == 2)
1970 {
1971 out[0] = cgltf_component_read_float(element, component_type, normalized);
1972 out[1] = cgltf_component_read_float(element + 2, component_type, normalized);
1973 out[2] = cgltf_component_read_float(element + 4, component_type, normalized);
1974 out[3] = cgltf_component_read_float(element + 8, component_type, normalized);
1975 out[4] = cgltf_component_read_float(element + 10, component_type, normalized);
1976 out[5] = cgltf_component_read_float(element + 12, component_type, normalized);
1977 out[6] = cgltf_component_read_float(element + 16, component_type, normalized);
1978 out[7] = cgltf_component_read_float(element + 18, component_type, normalized);
1979 out[8] = cgltf_component_read_float(element + 20, component_type, normalized);
1980 return 1;
1981 }
1982
1983 for (cgltf_size i = 0; i < num_components; ++i)
1984 {
1985 out[i] = cgltf_component_read_float(element + component_size * i, component_type, normalized);
1986 }
1987 return 1;
1988}
1989
1990cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size)
1991{
1992 if (accessor->is_sparse)
1993 {
1994 return 0;
1995 }
1996 if (accessor->buffer_view == NULL)
1997 {
1998 memset(out, 0, element_size * sizeof(cgltf_float));
1999 return 1;
2000 }
2001 if (accessor->buffer_view->buffer->data == NULL)
2002 {
2003 return 0;
2004 }
2005 cgltf_size offset = accessor->offset + accessor->buffer_view->offset;
2006 const uint8_t* element = (const uint8_t*) accessor->buffer_view->buffer->data;
2007 element += offset + accessor->stride * index;
2008 return cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, out, element_size);
2009}
2010
2011cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count)
2012{
2013 cgltf_size floats_per_element = cgltf_num_components(accessor->type);
2014 cgltf_size available_floats = accessor->count * floats_per_element;
2015 if (out == NULL)
2016 {
2017 return available_floats;
2018 }
2019
2020 float_count = available_floats < float_count ? available_floats : float_count;
2021 cgltf_size element_count = float_count / floats_per_element;
2022
2023 // First pass: convert each element in the base accessor.
2024 cgltf_float* dest = out;
2025 cgltf_accessor dense = *accessor;
2026 dense.is_sparse = 0;
2027 for (cgltf_size index = 0; index < element_count; index++, dest += floats_per_element)
2028 {
2029 if (!cgltf_accessor_read_float(&dense, index, dest, floats_per_element))
2030 {
2031 return 0;
2032 }
2033 }
2034
2035 // Second pass: write out each element in the sparse accessor.
2036 if (accessor->is_sparse)
2037 {
2038 const cgltf_accessor_sparse* sparse = &dense.sparse;
2039
2040 if (sparse->indices_buffer_view->buffer->data == NULL || sparse->values_buffer_view->buffer->data == NULL)
2041 {
2042 return 0;
2043 }
2044
2045 const uint8_t* index_data = (const uint8_t*) sparse->indices_buffer_view->buffer->data;
2046 index_data += sparse->indices_byte_offset + sparse->indices_buffer_view->offset;
2047 cgltf_size index_stride = cgltf_component_size(sparse->indices_component_type);
2048 const uint8_t* reader_head = (const uint8_t*) sparse->values_buffer_view->buffer->data;
2049 reader_head += sparse->values_byte_offset + sparse->values_buffer_view->offset;
2050 for (cgltf_size reader_index = 0; reader_index < sparse->count; reader_index++, index_data += index_stride)
2051 {
2052 size_t writer_index = cgltf_component_read_index(index_data, sparse->indices_component_type);
2053 float* writer_head = out + writer_index * floats_per_element;
2054
2055 if (!cgltf_element_read_float(reader_head, dense.type, dense.component_type, dense.normalized, writer_head, floats_per_element))
2056 {
2057 return 0;
2058 }
2059
2060 reader_head += dense.stride;
2061 }
2062 }
2063
2064 return element_count * floats_per_element;
2065}
2066
2067static cgltf_uint cgltf_component_read_uint(const void* in, cgltf_component_type component_type)
2068{
2069 switch (component_type)
2070 {
2071 case cgltf_component_type_r_8:
2072 return *((const int8_t*) in);
2073
2074 case cgltf_component_type_r_8u:
2075 return *((const uint8_t*) in);
2076
2077 case cgltf_component_type_r_16:
2078 return *((const int16_t*) in);
2079
2080 case cgltf_component_type_r_16u:
2081 return *((const uint16_t*) in);
2082
2083 case cgltf_component_type_r_32u:
2084 return *((const uint32_t*) in);
2085
2086 default:
2087 return 0;
2088 }
2089}
2090
2091static cgltf_bool cgltf_element_read_uint(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_uint* out, cgltf_size element_size)
2092{
2093 cgltf_size num_components = cgltf_num_components(type);
2094
2095 if (element_size < num_components)
2096 {
2097 return 0;
2098 }
2099
2100 // Reading integer matrices is not a valid use case
2101 if (type == cgltf_type_mat2 || type == cgltf_type_mat3 || type == cgltf_type_mat4)
2102 {
2103 return 0;
2104 }
2105
2106 cgltf_size component_size = cgltf_component_size(component_type);
2107
2108 for (cgltf_size i = 0; i < num_components; ++i)
2109 {
2110 out[i] = cgltf_component_read_uint(element + component_size * i, component_type);
2111 }
2112 return 1;
2113}
2114
2115cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size)
2116{
2117 if (accessor->is_sparse)
2118 {
2119 return 0;
2120 }
2121 if (accessor->buffer_view == NULL)
2122 {
2123 memset(out, 0, element_size * sizeof( cgltf_uint ));
2124 return 1;
2125 }
2126 if (accessor->buffer_view->buffer->data == NULL)
2127 {
2128 return 0;
2129 }
2130 cgltf_size offset = accessor->offset + accessor->buffer_view->offset;
2131 const uint8_t* element = (const uint8_t*) accessor->buffer_view->buffer->data;
2132 element += offset + accessor->stride * index;
2133 return cgltf_element_read_uint(element, accessor->type, accessor->component_type, out, element_size);
2134}
2135
2136cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index)
2137{
2138 if (accessor->is_sparse)
2139 {
2140 return 0; // This is an error case, but we can't communicate the error with existing interface.
2141 }
2142 if (accessor->buffer_view == NULL)
2143 {
2144 return 0;
2145 }
2146 if (accessor->buffer_view->buffer->data == NULL)
2147 {
2148 return 0; // This is an error case, but we can't communicate the error with existing interface.
2149 }
2150
2151 cgltf_size offset = accessor->offset + accessor->buffer_view->offset;
2152 const uint8_t* element = (const uint8_t*) accessor->buffer_view->buffer->data;
2153 element += offset + accessor->stride * index;
2154 return cgltf_component_read_index(element, accessor->component_type);
2155}
2156
2157#define CGLTF_ERROR_JSON -1
2158#define CGLTF_ERROR_NOMEM -2
2159#define CGLTF_ERROR_LEGACY -3
2160
2161#define CGLTF_CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return CGLTF_ERROR_JSON; }
2162#define CGLTF_CHECK_KEY(tok_) if ((tok_).type != JSMN_STRING || (tok_).size == 0) { return CGLTF_ERROR_JSON; } /* checking size for 0 verifies that a value follows the key */
2163
2164#define CGLTF_PTRINDEX(type, idx) (type*)((cgltf_size)idx + 1)
2165#define CGLTF_PTRFIXUP(var, data, size) if (var) { if ((cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1]; }
2166#define CGLTF_PTRFIXUP_REQ(var, data, size) if (!var || (cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1];
2167
2168static int cgltf_json_strcmp(jsmntok_t const* tok, const uint8_t* json_chunk, const char* str)
2169{
2170 CGLTF_CHECK_TOKTYPE(*tok, JSMN_STRING);
2171 size_t const str_len = strlen(str);
2172 size_t const name_length = tok->end - tok->start;
2173 return (str_len == name_length) ? strncmp((const char*)json_chunk + tok->start, str, str_len) : 128;
2174}
2175
2176static int cgltf_json_to_int(jsmntok_t const* tok, const uint8_t* json_chunk)
2177{
2178 CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE);
2179 char tmp[128];
2180 int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1);
2181 strncpy(tmp, (const char*)json_chunk + tok->start, size);
2182 tmp[size] = 0;
2183 return CGLTF_ATOI(tmp);
2184}
2185
2186static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json_chunk)
2187{
2188 CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE);
2189 char tmp[128];
2190 int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1);
2191 strncpy(tmp, (const char*)json_chunk + tok->start, size);
2192 tmp[size] = 0;
2193 return (cgltf_float)CGLTF_ATOF(tmp);
2194}
2195
2196static cgltf_bool cgltf_json_to_bool(jsmntok_t const* tok, const uint8_t* json_chunk)
2197{
2198 int size = tok->end - tok->start;
2199 return size == 4 && memcmp(json_chunk + tok->start, "true", 4) == 0;
2200}
2201
2202static int cgltf_skip_json(jsmntok_t const* tokens, int i)
2203{
2204 int end = i + 1;
2205
2206 while (i < end)
2207 {
2208 switch (tokens[i].type)
2209 {
2210 case JSMN_OBJECT:
2211 end += tokens[i].size * 2;
2212 break;
2213
2214 case JSMN_ARRAY:
2215 end += tokens[i].size;
2216 break;
2217
2218 case JSMN_PRIMITIVE:
2219 case JSMN_STRING:
2220 break;
2221
2222 default:
2223 return -1;
2224 }
2225
2226 i++;
2227 }
2228
2229 return i;
2230}
2231
2232static void cgltf_fill_float_array(float* out_array, int size, float value)
2233{
2234 for (int j = 0; j < size; ++j)
2235 {
2236 out_array[j] = value;
2237 }
2238}
2239
2240static int cgltf_parse_json_float_array(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, float* out_array, int size)
2241{
2242 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY);
2243 if (tokens[i].size != size)
2244 {
2245 return CGLTF_ERROR_JSON;
2246 }
2247 ++i;
2248 for (int j = 0; j < size; ++j)
2249 {
2250 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
2251 out_array[j] = cgltf_json_to_float(tokens + i, json_chunk);
2252 ++i;
2253 }
2254 return i;
2255}
2256
2257static int cgltf_parse_json_string(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, char** out_string)
2258{
2259 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING);
2260 if (*out_string)
2261 {
2262 return CGLTF_ERROR_JSON;
2263 }
2264 int size = tokens[i].end - tokens[i].start;
2265 char* result = (char*)options->memory.alloc(options->memory.user_data, size + 1);
2266 if (!result)
2267 {
2268 return CGLTF_ERROR_NOMEM;
2269 }
2270 strncpy(result, (const char*)json_chunk + tokens[i].start, size);
2271 result[size] = 0;
2272 *out_string = result;
2273 return i + 1;
2274}
2275
2276static int cgltf_parse_json_array(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, size_t element_size, void** out_array, cgltf_size* out_size)
2277{
2278 (void)json_chunk;
2279 if (tokens[i].type != JSMN_ARRAY)
2280 {
2281 return tokens[i].type == JSMN_OBJECT ? CGLTF_ERROR_LEGACY : CGLTF_ERROR_JSON;
2282 }
2283 if (*out_array)
2284 {
2285 return CGLTF_ERROR_JSON;
2286 }
2287 int size = tokens[i].size;
2288 void* result = cgltf_calloc(options, element_size, size);
2289 if (!result)
2290 {
2291 return CGLTF_ERROR_NOMEM;
2292 }
2293 *out_array = result;
2294 *out_size = size;
2295 return i + 1;
2296}
2297
2298static int cgltf_parse_json_string_array(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, char*** out_array, cgltf_size* out_size)
2299{
2300 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY);
2301 i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(char*), (void**)out_array, out_size);
2302 if (i < 0)
2303 {
2304 return i;
2305 }
2306
2307 for (cgltf_size j = 0; j < *out_size; ++j)
2308 {
2309 i = cgltf_parse_json_string(options, tokens, i, json_chunk, j + (*out_array));
2310 if (i < 0)
2311 {
2312 return i;
2313 }
2314 }
2315 return i;
2316}
2317
2318static void cgltf_parse_attribute_type(const char* name, cgltf_attribute_type* out_type, int* out_index)
2319{
2320 const char* us = strchr(name, '_');
2321 size_t len = us ? (size_t)(us - name) : strlen(name);
2322
2323 if (len == 8 && strncmp(name, "POSITION", 8) == 0)
2324 {
2325 *out_type = cgltf_attribute_type_position;
2326 }
2327 else if (len == 6 && strncmp(name, "NORMAL", 6) == 0)
2328 {
2329 *out_type = cgltf_attribute_type_normal;
2330 }
2331 else if (len == 7 && strncmp(name, "TANGENT", 7) == 0)
2332 {
2333 *out_type = cgltf_attribute_type_tangent;
2334 }
2335 else if (len == 8 && strncmp(name, "TEXCOORD", 8) == 0)
2336 {
2337 *out_type = cgltf_attribute_type_texcoord;
2338 }
2339 else if (len == 5 && strncmp(name, "COLOR", 5) == 0)
2340 {
2341 *out_type = cgltf_attribute_type_color;
2342 }
2343 else if (len == 6 && strncmp(name, "JOINTS", 6) == 0)
2344 {
2345 *out_type = cgltf_attribute_type_joints;
2346 }
2347 else if (len == 7 && strncmp(name, "WEIGHTS", 7) == 0)
2348 {
2349 *out_type = cgltf_attribute_type_weights;
2350 }
2351 else
2352 {
2353 *out_type = cgltf_attribute_type_invalid;
2354 }
2355
2356 if (us && *out_type != cgltf_attribute_type_invalid)
2357 {
2358 *out_index = CGLTF_ATOI(us + 1);
2359 }
2360}
2361
2362static int cgltf_parse_json_attribute_list(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_attribute** out_attributes, cgltf_size* out_attributes_count)
2363{
2364 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
2365
2366 if (*out_attributes)
2367 {
2368 return CGLTF_ERROR_JSON;
2369 }
2370
2371 *out_attributes_count = tokens[i].size;
2372 *out_attributes = (cgltf_attribute*)cgltf_calloc(options, sizeof(cgltf_attribute), *out_attributes_count);
2373 ++i;
2374
2375 if (!*out_attributes)
2376 {
2377 return CGLTF_ERROR_NOMEM;
2378 }
2379
2380 for (cgltf_size j = 0; j < *out_attributes_count; ++j)
2381 {
2382 CGLTF_CHECK_KEY(tokens[i]);
2383
2384 i = cgltf_parse_json_string(options, tokens, i, json_chunk, &(*out_attributes)[j].name);
2385 if (i < 0)
2386 {
2387 return CGLTF_ERROR_JSON;
2388 }
2389
2390 cgltf_parse_attribute_type((*out_attributes)[j].name, &(*out_attributes)[j].type, &(*out_attributes)[j].index);
2391
2392 (*out_attributes)[j].data = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk));
2393 ++i;
2394 }
2395
2396 return i;
2397}
2398
2399static int cgltf_parse_json_extras(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras)
2400{
2401 (void)json_chunk;
2402 out_extras->start_offset = tokens[i].start;
2403 out_extras->end_offset = tokens[i].end;
2404 i = cgltf_skip_json(tokens, i);
2405 return i;
2406}
2407
2408static int cgltf_parse_json_unprocessed_extension(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extension* out_extension)
2409{
2410 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING);
2411 CGLTF_CHECK_TOKTYPE(tokens[i+1], JSMN_OBJECT);
2412 if (out_extension->name)
2413 {
2414 return CGLTF_ERROR_JSON;
2415 }
2416
2417 cgltf_size name_length = tokens[i].end - tokens[i].start;
2418 out_extension->name = (char*)options->memory.alloc(options->memory.user_data, name_length + 1);
2419 if (!out_extension->name)
2420 {
2421 return CGLTF_ERROR_NOMEM;
2422 }
2423 strncpy(out_extension->name, (const char*)json_chunk + tokens[i].start, name_length);
2424 out_extension->name[name_length] = 0;
2425 i++;
2426
2427 size_t start = tokens[i].start;
2428 size_t size = tokens[i].end - start;
2429 out_extension->data = (char*)options->memory.alloc(options->memory.user_data, size + 1);
2430 if (!out_extension->data)
2431 {
2432 return CGLTF_ERROR_NOMEM;
2433 }
2434 strncpy(out_extension->data, (const char*)json_chunk + start, size);
2435 out_extension->data[size] = '\0';
2436
2437 i = cgltf_skip_json(tokens, i);
2438
2439 return i;
2440}
2441
2442static int cgltf_parse_json_unprocessed_extensions(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_size* out_extensions_count, cgltf_extension** out_extensions)
2443{
2444 ++i;
2445
2446 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
2447 if(*out_extensions)
2448 {
2449 return CGLTF_ERROR_JSON;
2450 }
2451
2452 int extensions_size = tokens[i].size;
2453 *out_extensions_count = 0;
2454 *out_extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size);
2455
2456 if (!*out_extensions)
2457 {
2458 return CGLTF_ERROR_NOMEM;
2459 }
2460
2461 ++i;
2462
2463 for (int j = 0; j < extensions_size; ++j)
2464 {
2465 CGLTF_CHECK_KEY(tokens[i]);
2466
2467 cgltf_size extension_index = (*out_extensions_count)++;
2468 cgltf_extension* extension = &((*out_extensions)[extension_index]);
2469 i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, extension);
2470
2471 if (i < 0)
2472 {
2473 return i;
2474 }
2475 }
2476 return i;
2477}
2478
2479static int cgltf_parse_json_draco_mesh_compression(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_draco_mesh_compression* out_draco_mesh_compression)
2480{
2481 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
2482
2483 int size = tokens[i].size;
2484 ++i;
2485
2486 for (int j = 0; j < size; ++j)
2487 {
2488 CGLTF_CHECK_KEY(tokens[i]);
2489
2490 if (cgltf_json_strcmp(tokens + i, json_chunk, "attributes") == 0)
2491 {
2492 i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_draco_mesh_compression->attributes, &out_draco_mesh_compression->attributes_count);
2493 }
2494 else if (cgltf_json_strcmp(tokens + i, json_chunk, "bufferView") == 0)
2495 {
2496 ++i;
2497 out_draco_mesh_compression->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk));
2498 ++i;
2499 }
2500 }
2501
2502 return i;
2503}
2504
2505static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_primitive* out_prim)
2506{
2507 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
2508
2509 out_prim->type = cgltf_primitive_type_triangles;
2510
2511 int size = tokens[i].size;
2512 ++i;
2513
2514 for (int j = 0; j < size; ++j)
2515 {
2516 CGLTF_CHECK_KEY(tokens[i]);
2517
2518 if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0)
2519 {
2520 ++i;
2521 out_prim->type
2522 = (cgltf_primitive_type)
2523 cgltf_json_to_int(tokens+i, json_chunk);
2524 ++i;
2525 }
2526 else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0)
2527 {
2528 ++i;
2529 out_prim->indices = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk));
2530 ++i;
2531 }
2532 else if (cgltf_json_strcmp(tokens+i, json_chunk, "material") == 0)
2533 {
2534 ++i;
2535 out_prim->material = CGLTF_PTRINDEX(cgltf_material, cgltf_json_to_int(tokens + i, json_chunk));
2536 ++i;
2537 }
2538 else if (cgltf_json_strcmp(tokens+i, json_chunk, "attributes") == 0)
2539 {
2540 i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_prim->attributes, &out_prim->attributes_count);
2541 }
2542 else if (cgltf_json_strcmp(tokens+i, json_chunk, "targets") == 0)
2543 {
2544 i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_morph_target), (void**)&out_prim->targets, &out_prim->targets_count);
2545 if (i < 0)
2546 {
2547 return i;
2548 }
2549
2550 for (cgltf_size k = 0; k < out_prim->targets_count; ++k)
2551 {
2552 i = cgltf_parse_json_attribute_list(options, tokens, i, json_chunk, &out_prim->targets[k].attributes, &out_prim->targets[k].attributes_count);
2553 if (i < 0)
2554 {
2555 return i;
2556 }
2557 }
2558 }
2559 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
2560 {
2561 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_prim->extras);
2562 }
2563 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
2564 {
2565 ++i;
2566
2567 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
2568 if(out_prim->extensions)
2569 {
2570 return CGLTF_ERROR_JSON;
2571 }
2572
2573 int extensions_size = tokens[i].size;
2574 out_prim->extensions_count = 0;
2575 out_prim->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size);
2576
2577 if (!out_prim->extensions)
2578 {
2579 return CGLTF_ERROR_NOMEM;
2580 }
2581
2582 ++i;
2583 for (int k = 0; k < extensions_size; ++k)
2584 {
2585 CGLTF_CHECK_KEY(tokens[i]);
2586
2587 if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_draco_mesh_compression") == 0)
2588 {
2589 out_prim->has_draco_mesh_compression = 1;
2590 i = cgltf_parse_json_draco_mesh_compression(options, tokens, i + 1, json_chunk, &out_prim->draco_mesh_compression);
2591 }
2592 else
2593 {
2594 i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_prim->extensions[out_prim->extensions_count++]));
2595 }
2596
2597 if (i < 0)
2598 {
2599 return i;
2600 }
2601 }
2602 }
2603 else
2604 {
2605 i = cgltf_skip_json(tokens, i+1);
2606 }
2607
2608 if (i < 0)
2609 {
2610 return i;
2611 }
2612 }
2613
2614 return i;
2615}
2616
2617static int cgltf_parse_json_mesh(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_mesh* out_mesh)
2618{
2619 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
2620
2621 int size = tokens[i].size;
2622 ++i;
2623
2624 for (int j = 0; j < size; ++j)
2625 {
2626 CGLTF_CHECK_KEY(tokens[i]);
2627
2628 if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
2629 {
2630 i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_mesh->name);
2631 }
2632 else if (cgltf_json_strcmp(tokens+i, json_chunk, "primitives") == 0)
2633 {
2634 i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_primitive), (void**)&out_mesh->primitives, &out_mesh->primitives_count);
2635 if (i < 0)
2636 {
2637 return i;
2638 }
2639
2640 for (cgltf_size prim_index = 0; prim_index < out_mesh->primitives_count; ++prim_index)
2641 {
2642 i = cgltf_parse_json_primitive(options, tokens, i, json_chunk, &out_mesh->primitives[prim_index]);
2643 if (i < 0)
2644 {
2645 return i;
2646 }
2647 }
2648 }
2649 else if (cgltf_json_strcmp(tokens + i, json_chunk, "weights") == 0)
2650 {
2651 i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_float), (void**)&out_mesh->weights, &out_mesh->weights_count);
2652 if (i < 0)
2653 {
2654 return i;
2655 }
2656
2657 i = cgltf_parse_json_float_array(tokens, i - 1, json_chunk, out_mesh->weights, (int)out_mesh->weights_count);
2658 }
2659 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
2660 {
2661 ++i;
2662
2663 out_mesh->extras.start_offset = tokens[i].start;
2664 out_mesh->extras.end_offset = tokens[i].end;
2665
2666 if (tokens[i].type == JSMN_OBJECT)
2667 {
2668 int extras_size = tokens[i].size;
2669 ++i;
2670
2671 for (int k = 0; k < extras_size; ++k)
2672 {
2673 CGLTF_CHECK_KEY(tokens[i]);
2674
2675 if (cgltf_json_strcmp(tokens+i, json_chunk, "targetNames") == 0)
2676 {
2677 i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_mesh->target_names, &out_mesh->target_names_count);
2678 }
2679 else
2680 {
2681 i = cgltf_skip_json(tokens, i+1);
2682 }
2683
2684 if (i < 0)
2685 {
2686 return i;
2687 }
2688 }
2689 }
2690 else
2691 {
2692 i = cgltf_skip_json(tokens, i);
2693 }
2694 }
2695 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
2696 {
2697 i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_mesh->extensions_count, &out_mesh->extensions);
2698 }
2699 else
2700 {
2701 i = cgltf_skip_json(tokens, i+1);
2702 }
2703
2704 if (i < 0)
2705 {
2706 return i;
2707 }
2708 }
2709
2710 return i;
2711}
2712
2713static int cgltf_parse_json_meshes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
2714{
2715 i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_mesh), (void**)&out_data->meshes, &out_data->meshes_count);
2716 if (i < 0)
2717 {
2718 return i;
2719 }
2720
2721 for (cgltf_size j = 0; j < out_data->meshes_count; ++j)
2722 {
2723 i = cgltf_parse_json_mesh(options, tokens, i, json_chunk, &out_data->meshes[j]);
2724 if (i < 0)
2725 {
2726 return i;
2727 }
2728 }
2729 return i;
2730}
2731
2732static cgltf_component_type cgltf_json_to_component_type(jsmntok_t const* tok, const uint8_t* json_chunk)
2733{
2734 int type = cgltf_json_to_int(tok, json_chunk);
2735
2736 switch (type)
2737 {
2738 case 5120:
2739 return cgltf_component_type_r_8;
2740 case 5121:
2741 return cgltf_component_type_r_8u;
2742 case 5122:
2743 return cgltf_component_type_r_16;
2744 case 5123:
2745 return cgltf_component_type_r_16u;
2746 case 5125:
2747 return cgltf_component_type_r_32u;
2748 case 5126:
2749 return cgltf_component_type_r_32f;
2750 default:
2751 return cgltf_component_type_invalid;
2752 }
2753}
2754
2755static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_accessor_sparse* out_sparse)
2756{
2757 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
2758
2759 int size = tokens[i].size;
2760 ++i;
2761
2762 for (int j = 0; j < size; ++j)
2763 {
2764 CGLTF_CHECK_KEY(tokens[i]);
2765
2766 if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0)
2767 {
2768 ++i;
2769 out_sparse->count = cgltf_json_to_int(tokens + i, json_chunk);
2770 ++i;
2771 }
2772 else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0)
2773 {
2774 ++i;
2775 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
2776
2777 int indices_size = tokens[i].size;
2778 ++i;
2779
2780 for (int k = 0; k < indices_size; ++k)
2781 {
2782 CGLTF_CHECK_KEY(tokens[i]);
2783
2784 if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0)
2785 {
2786 ++i;
2787 out_sparse->indices_buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk));
2788 ++i;
2789 }
2790 else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0)
2791 {
2792 ++i;
2793 out_sparse->indices_byte_offset = cgltf_json_to_int(tokens + i, json_chunk);
2794 ++i;
2795 }
2796 else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0)
2797 {
2798 ++i;
2799 out_sparse->indices_component_type = cgltf_json_to_component_type(tokens + i, json_chunk);
2800 ++i;
2801 }
2802 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
2803 {
2804 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->indices_extras);
2805 }
2806 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
2807 {
2808 i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sparse->indices_extensions_count, &out_sparse->indices_extensions);
2809 }
2810 else
2811 {
2812 i = cgltf_skip_json(tokens, i+1);
2813 }
2814
2815 if (i < 0)
2816 {
2817 return i;
2818 }
2819 }
2820 }
2821 else if (cgltf_json_strcmp(tokens+i, json_chunk, "values") == 0)
2822 {
2823 ++i;
2824 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
2825
2826 int values_size = tokens[i].size;
2827 ++i;
2828
2829 for (int k = 0; k < values_size; ++k)
2830 {
2831 CGLTF_CHECK_KEY(tokens[i]);
2832
2833 if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0)
2834 {
2835 ++i;
2836 out_sparse->values_buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk));
2837 ++i;
2838 }
2839 else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0)
2840 {
2841 ++i;
2842 out_sparse->values_byte_offset = cgltf_json_to_int(tokens + i, json_chunk);
2843 ++i;
2844 }
2845 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
2846 {
2847 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->values_extras);
2848 }
2849 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
2850 {
2851 i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sparse->values_extensions_count, &out_sparse->values_extensions);
2852 }
2853 else
2854 {
2855 i = cgltf_skip_json(tokens, i+1);
2856 }
2857
2858 if (i < 0)
2859 {
2860 return i;
2861 }
2862 }
2863 }
2864 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
2865 {
2866 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->extras);
2867 }
2868 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
2869 {
2870 i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sparse->extensions_count, &out_sparse->extensions);
2871 }
2872 else
2873 {
2874 i = cgltf_skip_json(tokens, i+1);
2875 }
2876
2877 if (i < 0)
2878 {
2879 return i;
2880 }
2881 }
2882
2883 return i;
2884}
2885
2886static int cgltf_parse_json_accessor(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_accessor* out_accessor)
2887{
2888 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
2889
2890 int size = tokens[i].size;
2891 ++i;
2892
2893 for (int j = 0; j < size; ++j)
2894 {
2895 CGLTF_CHECK_KEY(tokens[i]);
2896
2897 if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0)
2898 {
2899 ++i;
2900 out_accessor->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk));
2901 ++i;
2902 }
2903 else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0)
2904 {
2905 ++i;
2906 out_accessor->offset =
2907 cgltf_json_to_int(tokens+i, json_chunk);
2908 ++i;
2909 }
2910 else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0)
2911 {
2912 ++i;
2913 out_accessor->component_type = cgltf_json_to_component_type(tokens + i, json_chunk);
2914 ++i;
2915 }
2916 else if (cgltf_json_strcmp(tokens+i, json_chunk, "normalized") == 0)
2917 {
2918 ++i;
2919 out_accessor->normalized = cgltf_json_to_bool(tokens+i, json_chunk);
2920 ++i;
2921 }
2922 else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0)
2923 {
2924 ++i;
2925 out_accessor->count =
2926 cgltf_json_to_int(tokens+i, json_chunk);
2927 ++i;
2928 }
2929 else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0)
2930 {
2931 ++i;
2932 if (cgltf_json_strcmp(tokens+i, json_chunk, "SCALAR") == 0)
2933 {
2934 out_accessor->type = cgltf_type_scalar;
2935 }
2936 else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC2") == 0)
2937 {
2938 out_accessor->type = cgltf_type_vec2;
2939 }
2940 else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC3") == 0)
2941 {
2942 out_accessor->type = cgltf_type_vec3;
2943 }
2944 else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC4") == 0)
2945 {
2946 out_accessor->type = cgltf_type_vec4;
2947 }
2948 else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT2") == 0)
2949 {
2950 out_accessor->type = cgltf_type_mat2;
2951 }
2952 else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT3") == 0)
2953 {
2954 out_accessor->type = cgltf_type_mat3;
2955 }
2956 else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT4") == 0)
2957 {
2958 out_accessor->type = cgltf_type_mat4;
2959 }
2960 ++i;
2961 }
2962 else if (cgltf_json_strcmp(tokens + i, json_chunk, "min") == 0)
2963 {
2964 ++i;
2965 out_accessor->has_min = 1;
2966 // note: we can't parse the precise number of elements since type may not have been computed yet
2967 int min_size = tokens[i].size > 16 ? 16 : tokens[i].size;
2968 i = cgltf_parse_json_float_array(tokens, i, json_chunk, out_accessor->min, min_size);
2969 }
2970 else if (cgltf_json_strcmp(tokens + i, json_chunk, "max") == 0)
2971 {
2972 ++i;
2973 out_accessor->has_max = 1;
2974 // note: we can't parse the precise number of elements since type may not have been computed yet
2975 int max_size = tokens[i].size > 16 ? 16 : tokens[i].size;
2976 i = cgltf_parse_json_float_array(tokens, i, json_chunk, out_accessor->max, max_size);
2977 }
2978 else if (cgltf_json_strcmp(tokens + i, json_chunk, "sparse") == 0)
2979 {
2980 out_accessor->is_sparse = 1;
2981 i = cgltf_parse_json_accessor_sparse(options, tokens, i + 1, json_chunk, &out_accessor->sparse);
2982 }
2983 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
2984 {
2985 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_accessor->extras);
2986 }
2987 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
2988 {
2989 i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_accessor->extensions_count, &out_accessor->extensions);
2990 }
2991 else
2992 {
2993 i = cgltf_skip_json(tokens, i+1);
2994 }
2995
2996 if (i < 0)
2997 {
2998 return i;
2999 }
3000 }
3001
3002 return i;
3003}
3004
3005static int cgltf_parse_json_texture_transform(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_transform* out_texture_transform)
3006{
3007 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
3008
3009 int size = tokens[i].size;
3010 ++i;
3011
3012 for (int j = 0; j < size; ++j)
3013 {
3014 CGLTF_CHECK_KEY(tokens[i]);
3015
3016 if (cgltf_json_strcmp(tokens + i, json_chunk, "offset") == 0)
3017 {
3018 i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_texture_transform->offset, 2);
3019 }
3020 else if (cgltf_json_strcmp(tokens + i, json_chunk, "rotation") == 0)
3021 {
3022 ++i;
3023 out_texture_transform->rotation = cgltf_json_to_float(tokens + i, json_chunk);
3024 ++i;
3025 }
3026 else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0)
3027 {
3028 i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_texture_transform->scale, 2);
3029 }
3030 else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0)
3031 {
3032 ++i;
3033 out_texture_transform->texcoord = cgltf_json_to_int(tokens + i, json_chunk);
3034 ++i;
3035 }
3036 else
3037 {
3038 i = cgltf_skip_json(tokens, i + 1);
3039 }
3040
3041 if (i < 0)
3042 {
3043 return i;
3044 }
3045 }
3046
3047 return i;
3048}
3049
3050static int cgltf_parse_json_texture_view(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_view* out_texture_view)
3051{
3052 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
3053
3054 out_texture_view->scale = 1.0f;
3055 cgltf_fill_float_array(out_texture_view->transform.scale, 2, 1.0f);
3056
3057 int size = tokens[i].size;
3058 ++i;
3059
3060 for (int j = 0; j < size; ++j)
3061 {
3062 CGLTF_CHECK_KEY(tokens[i]);
3063
3064 if (cgltf_json_strcmp(tokens + i, json_chunk, "index") == 0)
3065 {
3066 ++i;
3067 out_texture_view->texture = CGLTF_PTRINDEX(cgltf_texture, cgltf_json_to_int(tokens + i, json_chunk));
3068 ++i;
3069 }
3070 else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0)
3071 {
3072 ++i;
3073 out_texture_view->texcoord = cgltf_json_to_int(tokens + i, json_chunk);
3074 ++i;
3075 }
3076 else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0)
3077 {
3078 ++i;
3079 out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk);
3080 ++i;
3081 }
3082 else if (cgltf_json_strcmp(tokens + i, json_chunk, "strength") == 0)
3083 {
3084 ++i;
3085 out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk);
3086 ++i;
3087 }
3088 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
3089 {
3090 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_texture_view->extras);
3091 }
3092 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
3093 {
3094 ++i;
3095
3096 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
3097 if(out_texture_view->extensions)
3098 {
3099 return CGLTF_ERROR_JSON;
3100 }
3101
3102 int extensions_size = tokens[i].size;
3103 out_texture_view->extensions_count = 0;
3104 out_texture_view->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size);
3105
3106 if (!out_texture_view->extensions)
3107 {
3108 return CGLTF_ERROR_NOMEM;
3109 }
3110
3111 ++i;
3112
3113 for (int k = 0; k < extensions_size; ++k)
3114 {
3115 CGLTF_CHECK_KEY(tokens[i]);
3116
3117 if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_texture_transform") == 0)
3118 {
3119 out_texture_view->has_transform = 1;
3120 i = cgltf_parse_json_texture_transform(tokens, i + 1, json_chunk, &out_texture_view->transform);
3121 }
3122 else
3123 {
3124 i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_texture_view->extensions[out_texture_view->extensions_count++]));
3125 }
3126
3127 if (i < 0)
3128 {
3129 return i;
3130 }
3131 }
3132 }
3133 else
3134 {
3135 i = cgltf_skip_json(tokens, i + 1);
3136 }
3137
3138 if (i < 0)
3139 {
3140 return i;
3141 }
3142 }
3143
3144 return i;
3145}
3146
3147static int cgltf_parse_json_pbr_metallic_roughness(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_pbr_metallic_roughness* out_pbr)
3148{
3149 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
3150
3151 int size = tokens[i].size;
3152 ++i;
3153
3154 for (int j = 0; j < size; ++j)
3155 {
3156 CGLTF_CHECK_KEY(tokens[i]);
3157
3158 if (cgltf_json_strcmp(tokens+i, json_chunk, "metallicFactor") == 0)
3159 {
3160 ++i;
3161 out_pbr->metallic_factor =
3162 cgltf_json_to_float(tokens + i, json_chunk);
3163 ++i;
3164 }
3165 else if (cgltf_json_strcmp(tokens+i, json_chunk, "roughnessFactor") == 0)
3166 {
3167 ++i;
3168 out_pbr->roughness_factor =
3169 cgltf_json_to_float(tokens+i, json_chunk);
3170 ++i;
3171 }
3172 else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorFactor") == 0)
3173 {
3174 i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->base_color_factor, 4);
3175 }
3176 else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorTexture") == 0)
3177 {
3178 i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk,
3179 &out_pbr->base_color_texture);
3180 }
3181 else if (cgltf_json_strcmp(tokens + i, json_chunk, "metallicRoughnessTexture") == 0)
3182 {
3183 i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk,
3184 &out_pbr->metallic_roughness_texture);
3185 }
3186 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
3187 {
3188 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_pbr->extras);
3189 }
3190 else
3191 {
3192 i = cgltf_skip_json(tokens, i+1);
3193 }
3194
3195 if (i < 0)
3196 {
3197 return i;
3198 }
3199 }
3200
3201 return i;
3202}
3203
3204static int cgltf_parse_json_pbr_specular_glossiness(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_pbr_specular_glossiness* out_pbr)
3205{
3206 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
3207 int size = tokens[i].size;
3208 ++i;
3209
3210 for (int j = 0; j < size; ++j)
3211 {
3212 CGLTF_CHECK_KEY(tokens[i]);
3213
3214 if (cgltf_json_strcmp(tokens+i, json_chunk, "diffuseFactor") == 0)
3215 {
3216 i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->diffuse_factor, 4);
3217 }
3218 else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularFactor") == 0)
3219 {
3220 i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->specular_factor, 3);
3221 }
3222 else if (cgltf_json_strcmp(tokens+i, json_chunk, "glossinessFactor") == 0)
3223 {
3224 ++i;
3225 out_pbr->glossiness_factor = cgltf_json_to_float(tokens + i, json_chunk);
3226 ++i;
3227 }
3228 else if (cgltf_json_strcmp(tokens+i, json_chunk, "diffuseTexture") == 0)
3229 {
3230 i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->diffuse_texture);
3231 }
3232 else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularGlossinessTexture") == 0)
3233 {
3234 i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->specular_glossiness_texture);
3235 }
3236 else
3237 {
3238 i = cgltf_skip_json(tokens, i+1);
3239 }
3240
3241 if (i < 0)
3242 {
3243 return i;
3244 }
3245 }
3246
3247 return i;
3248}
3249
3250static int cgltf_parse_json_clearcoat(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_clearcoat* out_clearcoat)
3251{
3252 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
3253 int size = tokens[i].size;
3254 ++i;
3255
3256 for (int j = 0; j < size; ++j)
3257 {
3258 CGLTF_CHECK_KEY(tokens[i]);
3259
3260 if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatFactor") == 0)
3261 {
3262 ++i;
3263 out_clearcoat->clearcoat_factor = cgltf_json_to_float(tokens + i, json_chunk);
3264 ++i;
3265 }
3266 else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatRoughnessFactor") == 0)
3267 {
3268 ++i;
3269 out_clearcoat->clearcoat_roughness_factor = cgltf_json_to_float(tokens + i, json_chunk);
3270 ++i;
3271 }
3272 else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatTexture") == 0)
3273 {
3274 i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_texture);
3275 }
3276 else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatRoughnessTexture") == 0)
3277 {
3278 i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_roughness_texture);
3279 }
3280 else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatNormalTexture") == 0)
3281 {
3282 i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_normal_texture);
3283 }
3284 else
3285 {
3286 i = cgltf_skip_json(tokens, i+1);
3287 }
3288
3289 if (i < 0)
3290 {
3291 return i;
3292 }
3293 }
3294
3295 return i;
3296}
3297
3298static int cgltf_parse_json_ior(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_ior* out_ior)
3299{
3300 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
3301 int size = tokens[i].size;
3302 ++i;
3303
3304 // Default values
3305 out_ior->ior = 1.5f;
3306
3307 for (int j = 0; j < size; ++j)
3308 {
3309 CGLTF_CHECK_KEY(tokens[i]);
3310
3311 if (cgltf_json_strcmp(tokens+i, json_chunk, "ior") == 0)
3312 {
3313 ++i;
3314 out_ior->ior = cgltf_json_to_float(tokens + i, json_chunk);
3315 ++i;
3316 }
3317 else
3318 {
3319 i = cgltf_skip_json(tokens, i+1);
3320 }
3321
3322 if (i < 0)
3323 {
3324 return i;
3325 }
3326 }
3327
3328 return i;
3329}
3330
3331static int cgltf_parse_json_specular(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_specular* out_specular)
3332{
3333 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
3334 int size = tokens[i].size;
3335 ++i;
3336
3337 // Default values
3338 out_specular->specular_factor = 1.0f;
3339 cgltf_fill_float_array(out_specular->specular_color_factor, 3, 1.0f);
3340
3341 for (int j = 0; j < size; ++j)
3342 {
3343 CGLTF_CHECK_KEY(tokens[i]);
3344
3345 if (cgltf_json_strcmp(tokens+i, json_chunk, "specularFactor") == 0)
3346 {
3347 ++i;
3348 out_specular->specular_factor = cgltf_json_to_float(tokens + i, json_chunk);
3349 ++i;
3350 }
3351 else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularColorFactor") == 0)
3352 {
3353 i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_specular->specular_color_factor, 3);
3354 }
3355 else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularTexture") == 0)
3356 {
3357 i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_specular->specular_texture);
3358 }
3359 else
3360 {
3361 i = cgltf_skip_json(tokens, i+1);
3362 }
3363
3364 if (i < 0)
3365 {
3366 return i;
3367 }
3368 }
3369
3370 return i;
3371}
3372
3373static int cgltf_parse_json_transmission(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_transmission* out_transmission)
3374{
3375 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
3376 int size = tokens[i].size;
3377 ++i;
3378
3379 for (int j = 0; j < size; ++j)
3380 {
3381 CGLTF_CHECK_KEY(tokens[i]);
3382
3383 if (cgltf_json_strcmp(tokens+i, json_chunk, "transmissionFactor") == 0)
3384 {
3385 ++i;
3386 out_transmission->transmission_factor = cgltf_json_to_float(tokens + i, json_chunk);
3387 ++i;
3388 }
3389 else if (cgltf_json_strcmp(tokens+i, json_chunk, "transmissionTexture") == 0)
3390 {
3391 i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_transmission->transmission_texture);
3392 }
3393 else
3394 {
3395 i = cgltf_skip_json(tokens, i+1);
3396 }
3397
3398 if (i < 0)
3399 {
3400 return i;
3401 }
3402 }
3403
3404 return i;
3405}
3406
3407static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_image* out_image)
3408{
3409 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
3410
3411 int size = tokens[i].size;
3412 ++i;
3413
3414 for (int j = 0; j < size; ++j)
3415 {
3416 CGLTF_CHECK_KEY(tokens[i]);
3417
3418 if (cgltf_json_strcmp(tokens + i, json_chunk, "uri") == 0)
3419 {
3420 i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->uri);
3421 }
3422 else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0)
3423 {
3424 ++i;
3425 out_image->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk));
3426 ++i;
3427 }
3428 else if (cgltf_json_strcmp(tokens + i, json_chunk, "mimeType") == 0)
3429 {
3430 i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->mime_type);
3431 }
3432 else if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0)
3433 {
3434 i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->name);
3435 }
3436 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
3437 {
3438 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_image->extras);
3439 }
3440 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
3441 {
3442 i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_image->extensions_count, &out_image->extensions);
3443 }
3444 else
3445 {
3446 i = cgltf_skip_json(tokens, i + 1);
3447 }
3448
3449 if (i < 0)
3450 {
3451 return i;
3452 }
3453 }
3454
3455 return i;
3456}
3457
3458static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_sampler* out_sampler)
3459{
3460 (void)options;
3461 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
3462
3463 out_sampler->wrap_s = 10497;
3464 out_sampler->wrap_t = 10497;
3465
3466 int size = tokens[i].size;
3467 ++i;
3468
3469 for (int j = 0; j < size; ++j)
3470 {
3471 CGLTF_CHECK_KEY(tokens[i]);
3472
3473 if (cgltf_json_strcmp(tokens + i, json_chunk, "magFilter") == 0)
3474 {
3475 ++i;
3476 out_sampler->mag_filter
3477 = cgltf_json_to_int(tokens + i, json_chunk);
3478 ++i;
3479 }
3480 else if (cgltf_json_strcmp(tokens + i, json_chunk, "minFilter") == 0)
3481 {
3482 ++i;
3483 out_sampler->min_filter
3484 = cgltf_json_to_int(tokens + i, json_chunk);
3485 ++i;
3486 }
3487 else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapS") == 0)
3488 {
3489 ++i;
3490 out_sampler->wrap_s
3491 = cgltf_json_to_int(tokens + i, json_chunk);
3492 ++i;
3493 }
3494 else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapT") == 0)
3495 {
3496 ++i;
3497 out_sampler->wrap_t
3498 = cgltf_json_to_int(tokens + i, json_chunk);
3499 ++i;
3500 }
3501 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
3502 {
3503 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sampler->extras);
3504 }
3505 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
3506 {
3507 i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sampler->extensions_count, &out_sampler->extensions);
3508 }
3509 else
3510 {
3511 i = cgltf_skip_json(tokens, i + 1);
3512 }
3513
3514 if (i < 0)
3515 {
3516 return i;
3517 }
3518 }
3519
3520 return i;
3521}
3522
3523static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture* out_texture)
3524{
3525 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
3526
3527 int size = tokens[i].size;
3528 ++i;
3529
3530 for (int j = 0; j < size; ++j)
3531 {
3532 CGLTF_CHECK_KEY(tokens[i]);
3533
3534 if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
3535 {
3536 i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_texture->name);
3537 }
3538 else if (cgltf_json_strcmp(tokens + i, json_chunk, "sampler") == 0)
3539 {
3540 ++i;
3541 out_texture->sampler = CGLTF_PTRINDEX(cgltf_sampler, cgltf_json_to_int(tokens + i, json_chunk));
3542 ++i;
3543 }
3544 else if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0)
3545 {
3546 ++i;
3547 out_texture->image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk));
3548 ++i;
3549 }
3550 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
3551 {
3552 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_texture->extras);
3553 }
3554 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
3555 {
3556 i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_texture->extensions_count, &out_texture->extensions);
3557 }
3558 else
3559 {
3560 i = cgltf_skip_json(tokens, i + 1);
3561 }
3562
3563 if (i < 0)
3564 {
3565 return i;
3566 }
3567 }
3568
3569 return i;
3570}
3571
3572static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material* out_material)
3573{
3574 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
3575
3576 cgltf_fill_float_array(out_material->pbr_metallic_roughness.base_color_factor, 4, 1.0f);
3577 out_material->pbr_metallic_roughness.metallic_factor = 1.0f;
3578 out_material->pbr_metallic_roughness.roughness_factor = 1.0f;
3579
3580 cgltf_fill_float_array(out_material->pbr_specular_glossiness.diffuse_factor, 4, 1.0f);
3581 cgltf_fill_float_array(out_material->pbr_specular_glossiness.specular_factor, 3, 1.0f);
3582 out_material->pbr_specular_glossiness.glossiness_factor = 1.0f;
3583
3584 out_material->alpha_cutoff = 0.5f;
3585
3586 int size = tokens[i].size;
3587 ++i;
3588
3589 for (int j = 0; j < size; ++j)
3590 {
3591 CGLTF_CHECK_KEY(tokens[i]);
3592
3593 if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
3594 {
3595 i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_material->name);
3596 }
3597 else if (cgltf_json_strcmp(tokens+i, json_chunk, "pbrMetallicRoughness") == 0)
3598 {
3599 out_material->has_pbr_metallic_roughness = 1;
3600 i = cgltf_parse_json_pbr_metallic_roughness(options, tokens, i + 1, json_chunk, &out_material->pbr_metallic_roughness);
3601 }
3602 else if (cgltf_json_strcmp(tokens+i, json_chunk, "emissiveFactor") == 0)
3603 {
3604 i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_material->emissive_factor, 3);
3605 }
3606 else if (cgltf_json_strcmp(tokens + i, json_chunk, "normalTexture") == 0)
3607 {
3608 i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk,
3609 &out_material->normal_texture);
3610 }
3611 else if (cgltf_json_strcmp(tokens + i, json_chunk, "occlusionTexture") == 0)
3612 {
3613 i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk,
3614 &out_material->occlusion_texture);
3615 }
3616 else if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveTexture") == 0)
3617 {
3618 i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk,
3619 &out_material->emissive_texture);
3620 }
3621 else if (cgltf_json_strcmp(tokens + i, json_chunk, "alphaMode") == 0)
3622 {
3623 ++i;
3624 if (cgltf_json_strcmp(tokens + i, json_chunk, "OPAQUE") == 0)
3625 {
3626 out_material->alpha_mode = cgltf_alpha_mode_opaque;
3627 }
3628 else if (cgltf_json_strcmp(tokens + i, json_chunk, "MASK") == 0)
3629 {
3630 out_material->alpha_mode = cgltf_alpha_mode_mask;
3631 }
3632 else if (cgltf_json_strcmp(tokens + i, json_chunk, "BLEND") == 0)
3633 {
3634 out_material->alpha_mode = cgltf_alpha_mode_blend;
3635 }
3636 ++i;
3637 }
3638 else if (cgltf_json_strcmp(tokens + i, json_chunk, "alphaCutoff") == 0)
3639 {
3640 ++i;
3641 out_material->alpha_cutoff = cgltf_json_to_float(tokens + i, json_chunk);
3642 ++i;
3643 }
3644 else if (cgltf_json_strcmp(tokens + i, json_chunk, "doubleSided") == 0)
3645 {
3646 ++i;
3647 out_material->double_sided =
3648 cgltf_json_to_bool(tokens + i, json_chunk);
3649 ++i;
3650 }
3651 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
3652 {
3653 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_material->extras);
3654 }
3655 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
3656 {
3657 ++i;
3658
3659 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
3660 if(out_material->extensions)
3661 {
3662 return CGLTF_ERROR_JSON;
3663 }
3664
3665 int extensions_size = tokens[i].size;
3666 ++i;
3667 out_material->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size);
3668 out_material->extensions_count= 0;
3669
3670 if (!out_material->extensions)
3671 {
3672 return CGLTF_ERROR_NOMEM;
3673 }
3674
3675 for (int k = 0; k < extensions_size; ++k)
3676 {
3677 CGLTF_CHECK_KEY(tokens[i]);
3678
3679 if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_pbrSpecularGlossiness") == 0)
3680 {
3681 out_material->has_pbr_specular_glossiness = 1;
3682 i = cgltf_parse_json_pbr_specular_glossiness(options, tokens, i + 1, json_chunk, &out_material->pbr_specular_glossiness);
3683 }
3684 else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_unlit") == 0)
3685 {
3686 out_material->unlit = 1;
3687 i = cgltf_skip_json(tokens, i+1);
3688 }
3689 else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_clearcoat") == 0)
3690 {
3691 out_material->has_clearcoat = 1;
3692 i = cgltf_parse_json_clearcoat(options, tokens, i + 1, json_chunk, &out_material->clearcoat);
3693 }
3694 else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_ior") == 0)
3695 {
3696 out_material->has_ior = 1;
3697 i = cgltf_parse_json_ior(tokens, i + 1, json_chunk, &out_material->ior);
3698 }
3699 else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_specular") == 0)
3700 {
3701 out_material->has_specular = 1;
3702 i = cgltf_parse_json_specular(options, tokens, i + 1, json_chunk, &out_material->specular);
3703 }
3704 else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_transmission") == 0)
3705 {
3706 out_material->has_transmission = 1;
3707 i = cgltf_parse_json_transmission(options, tokens, i + 1, json_chunk, &out_material->transmission);
3708 }
3709 else
3710 {
3711 i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_material->extensions[out_material->extensions_count++]));
3712 }
3713
3714 if (i < 0)
3715 {
3716 return i;
3717 }
3718 }
3719 }
3720 else
3721 {
3722 i = cgltf_skip_json(tokens, i+1);
3723 }
3724
3725 if (i < 0)
3726 {
3727 return i;
3728 }
3729 }
3730
3731 return i;
3732}
3733
3734static int cgltf_parse_json_accessors(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
3735{
3736 i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_accessor), (void**)&out_data->accessors, &out_data->accessors_count);
3737 if (i < 0)
3738 {
3739 return i;
3740 }
3741
3742 for (cgltf_size j = 0; j < out_data->accessors_count; ++j)
3743 {
3744 i = cgltf_parse_json_accessor(options, tokens, i, json_chunk, &out_data->accessors[j]);
3745 if (i < 0)
3746 {
3747 return i;
3748 }
3749 }
3750 return i;
3751}
3752
3753static int cgltf_parse_json_materials(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
3754{
3755 i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_material), (void**)&out_data->materials, &out_data->materials_count);
3756 if (i < 0)
3757 {
3758 return i;
3759 }
3760
3761 for (cgltf_size j = 0; j < out_data->materials_count; ++j)
3762 {
3763 i = cgltf_parse_json_material(options, tokens, i, json_chunk, &out_data->materials[j]);
3764 if (i < 0)
3765 {
3766 return i;
3767 }
3768 }
3769 return i;
3770}
3771
3772static int cgltf_parse_json_images(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
3773{
3774 i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_image), (void**)&out_data->images, &out_data->images_count);
3775 if (i < 0)
3776 {
3777 return i;
3778 }
3779
3780 for (cgltf_size j = 0; j < out_data->images_count; ++j)
3781 {
3782 i = cgltf_parse_json_image(options, tokens, i, json_chunk, &out_data->images[j]);
3783 if (i < 0)
3784 {
3785 return i;
3786 }
3787 }
3788 return i;
3789}
3790
3791static int cgltf_parse_json_textures(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
3792{
3793 i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_texture), (void**)&out_data->textures, &out_data->textures_count);
3794 if (i < 0)
3795 {
3796 return i;
3797 }
3798
3799 for (cgltf_size j = 0; j < out_data->textures_count; ++j)
3800 {
3801 i = cgltf_parse_json_texture(options, tokens, i, json_chunk, &out_data->textures[j]);
3802 if (i < 0)
3803 {
3804 return i;
3805 }
3806 }
3807 return i;
3808}
3809
3810static int cgltf_parse_json_samplers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
3811{
3812 i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_sampler), (void**)&out_data->samplers, &out_data->samplers_count);
3813 if (i < 0)
3814 {
3815 return i;
3816 }
3817
3818 for (cgltf_size j = 0; j < out_data->samplers_count; ++j)
3819 {
3820 i = cgltf_parse_json_sampler(options, tokens, i, json_chunk, &out_data->samplers[j]);
3821 if (i < 0)
3822 {
3823 return i;
3824 }
3825 }
3826 return i;
3827}
3828
3829static int cgltf_parse_json_buffer_view(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_buffer_view* out_buffer_view)
3830{
3831 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
3832
3833 int size = tokens[i].size;
3834 ++i;
3835
3836 for (int j = 0; j < size; ++j)
3837 {
3838 CGLTF_CHECK_KEY(tokens[i]);
3839
3840 if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0)
3841 {
3842 ++i;
3843 out_buffer_view->buffer = CGLTF_PTRINDEX(cgltf_buffer, cgltf_json_to_int(tokens + i, json_chunk));
3844 ++i;
3845 }
3846 else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0)
3847 {
3848 ++i;
3849 out_buffer_view->offset =
3850 cgltf_json_to_int(tokens+i, json_chunk);
3851 ++i;
3852 }
3853 else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0)
3854 {
3855 ++i;
3856 out_buffer_view->size =
3857 cgltf_json_to_int(tokens+i, json_chunk);
3858 ++i;
3859 }
3860 else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0)
3861 {
3862 ++i;
3863 out_buffer_view->stride =
3864 cgltf_json_to_int(tokens+i, json_chunk);
3865 ++i;
3866 }
3867 else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0)
3868 {
3869 ++i;
3870 int type = cgltf_json_to_int(tokens+i, json_chunk);
3871 switch (type)
3872 {
3873 case 34962:
3874 type = cgltf_buffer_view_type_vertices;
3875 break;
3876 case 34963:
3877 type = cgltf_buffer_view_type_indices;
3878 break;
3879 default:
3880 type = cgltf_buffer_view_type_invalid;
3881 break;
3882 }
3883 out_buffer_view->type = (cgltf_buffer_view_type)type;
3884 ++i;
3885 }
3886 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
3887 {
3888 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_buffer_view->extras);
3889 }
3890 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
3891 {
3892 i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_buffer_view->extensions_count, &out_buffer_view->extensions);
3893 }
3894 else
3895 {
3896 i = cgltf_skip_json(tokens, i+1);
3897 }
3898
3899 if (i < 0)
3900 {
3901 return i;
3902 }
3903 }
3904
3905 return i;
3906}
3907
3908static int cgltf_parse_json_buffer_views(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
3909{
3910 i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_buffer_view), (void**)&out_data->buffer_views, &out_data->buffer_views_count);
3911 if (i < 0)
3912 {
3913 return i;
3914 }
3915
3916 for (cgltf_size j = 0; j < out_data->buffer_views_count; ++j)
3917 {
3918 i = cgltf_parse_json_buffer_view(options, tokens, i, json_chunk, &out_data->buffer_views[j]);
3919 if (i < 0)
3920 {
3921 return i;
3922 }
3923 }
3924 return i;
3925}
3926
3927static int cgltf_parse_json_buffer(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_buffer* out_buffer)
3928{
3929 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
3930
3931 int size = tokens[i].size;
3932 ++i;
3933
3934 for (int j = 0; j < size; ++j)
3935 {
3936 CGLTF_CHECK_KEY(tokens[i]);
3937
3938 if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0)
3939 {
3940 ++i;
3941 out_buffer->size =
3942 cgltf_json_to_int(tokens+i, json_chunk);
3943 ++i;
3944 }
3945 else if (cgltf_json_strcmp(tokens+i, json_chunk, "uri") == 0)
3946 {
3947 i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer->uri);
3948 }
3949 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
3950 {
3951 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_buffer->extras);
3952 }
3953 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
3954 {
3955 i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_buffer->extensions_count, &out_buffer->extensions);
3956 }
3957 else
3958 {
3959 i = cgltf_skip_json(tokens, i+1);
3960 }
3961
3962 if (i < 0)
3963 {
3964 return i;
3965 }
3966 }
3967
3968 return i;
3969}
3970
3971static int cgltf_parse_json_buffers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
3972{
3973 i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_buffer), (void**)&out_data->buffers, &out_data->buffers_count);
3974 if (i < 0)
3975 {
3976 return i;
3977 }
3978
3979 for (cgltf_size j = 0; j < out_data->buffers_count; ++j)
3980 {
3981 i = cgltf_parse_json_buffer(options, tokens, i, json_chunk, &out_data->buffers[j]);
3982 if (i < 0)
3983 {
3984 return i;
3985 }
3986 }
3987 return i;
3988}
3989
3990static int cgltf_parse_json_skin(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_skin* out_skin)
3991{
3992 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
3993
3994 int size = tokens[i].size;
3995 ++i;
3996
3997 for (int j = 0; j < size; ++j)
3998 {
3999 CGLTF_CHECK_KEY(tokens[i]);
4000
4001 if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
4002 {
4003 i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_skin->name);
4004 }
4005 else if (cgltf_json_strcmp(tokens+i, json_chunk, "joints") == 0)
4006 {
4007 i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_skin->joints, &out_skin->joints_count);
4008 if (i < 0)
4009 {
4010 return i;
4011 }
4012
4013 for (cgltf_size k = 0; k < out_skin->joints_count; ++k)
4014 {
4015 out_skin->joints[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk));
4016 ++i;
4017 }
4018 }
4019 else if (cgltf_json_strcmp(tokens+i, json_chunk, "skeleton") == 0)
4020 {
4021 ++i;
4022 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
4023 out_skin->skeleton = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk));
4024 ++i;
4025 }
4026 else if (cgltf_json_strcmp(tokens+i, json_chunk, "inverseBindMatrices") == 0)
4027 {
4028 ++i;
4029 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
4030 out_skin->inverse_bind_matrices = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk));
4031 ++i;
4032 }
4033 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
4034 {
4035 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_skin->extras);
4036 }
4037 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
4038 {
4039 i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_skin->extensions_count, &out_skin->extensions);
4040 }
4041 else
4042 {
4043 i = cgltf_skip_json(tokens, i+1);
4044 }
4045
4046 if (i < 0)
4047 {
4048 return i;
4049 }
4050 }
4051
4052 return i;
4053}
4054
4055static int cgltf_parse_json_skins(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
4056{
4057 i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_skin), (void**)&out_data->skins, &out_data->skins_count);
4058 if (i < 0)
4059 {
4060 return i;
4061 }
4062
4063 for (cgltf_size j = 0; j < out_data->skins_count; ++j)
4064 {
4065 i = cgltf_parse_json_skin(options, tokens, i, json_chunk, &out_data->skins[j]);
4066 if (i < 0)
4067 {
4068 return i;
4069 }
4070 }
4071 return i;
4072}
4073
4074static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_camera* out_camera)
4075{
4076 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
4077
4078 int size = tokens[i].size;
4079 ++i;
4080
4081 for (int j = 0; j < size; ++j)
4082 {
4083 CGLTF_CHECK_KEY(tokens[i]);
4084
4085 if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
4086 {
4087 i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_camera->name);
4088 }
4089 else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0)
4090 {
4091 ++i;
4092 if (cgltf_json_strcmp(tokens + i, json_chunk, "perspective") == 0)
4093 {
4094 out_camera->type = cgltf_camera_type_perspective;
4095 }
4096 else if (cgltf_json_strcmp(tokens + i, json_chunk, "orthographic") == 0)
4097 {
4098 out_camera->type = cgltf_camera_type_orthographic;
4099 }
4100 ++i;
4101 }
4102 else if (cgltf_json_strcmp(tokens+i, json_chunk, "perspective") == 0)
4103 {
4104 ++i;
4105
4106 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
4107
4108 int data_size = tokens[i].size;
4109 ++i;
4110
4111 out_camera->type = cgltf_camera_type_perspective;
4112
4113 for (int k = 0; k < data_size; ++k)
4114 {
4115 CGLTF_CHECK_KEY(tokens[i]);
4116
4117 if (cgltf_json_strcmp(tokens+i, json_chunk, "aspectRatio") == 0)
4118 {
4119 ++i;
4120 out_camera->data.perspective.aspect_ratio = cgltf_json_to_float(tokens + i, json_chunk);
4121 ++i;
4122 }
4123 else if (cgltf_json_strcmp(tokens+i, json_chunk, "yfov") == 0)
4124 {
4125 ++i;
4126 out_camera->data.perspective.yfov = cgltf_json_to_float(tokens + i, json_chunk);
4127 ++i;
4128 }
4129 else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0)
4130 {
4131 ++i;
4132 out_camera->data.perspective.zfar = cgltf_json_to_float(tokens + i, json_chunk);
4133 ++i;
4134 }
4135 else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0)
4136 {
4137 ++i;
4138 out_camera->data.perspective.znear = cgltf_json_to_float(tokens + i, json_chunk);
4139 ++i;
4140 }
4141 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
4142 {
4143 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->data.perspective.extras);
4144 }
4145 else
4146 {
4147 i = cgltf_skip_json(tokens, i+1);
4148 }
4149
4150 if (i < 0)
4151 {
4152 return i;
4153 }
4154 }
4155 }
4156 else if (cgltf_json_strcmp(tokens+i, json_chunk, "orthographic") == 0)
4157 {
4158 ++i;
4159
4160 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
4161
4162 int data_size = tokens[i].size;
4163 ++i;
4164
4165 out_camera->type = cgltf_camera_type_orthographic;
4166
4167 for (int k = 0; k < data_size; ++k)
4168 {
4169 CGLTF_CHECK_KEY(tokens[i]);
4170
4171 if (cgltf_json_strcmp(tokens+i, json_chunk, "xmag") == 0)
4172 {
4173 ++i;
4174 out_camera->data.orthographic.xmag = cgltf_json_to_float(tokens + i, json_chunk);
4175 ++i;
4176 }
4177 else if (cgltf_json_strcmp(tokens+i, json_chunk, "ymag") == 0)
4178 {
4179 ++i;
4180 out_camera->data.orthographic.ymag = cgltf_json_to_float(tokens + i, json_chunk);
4181 ++i;
4182 }
4183 else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0)
4184 {
4185 ++i;
4186 out_camera->data.orthographic.zfar = cgltf_json_to_float(tokens + i, json_chunk);
4187 ++i;
4188 }
4189 else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0)
4190 {
4191 ++i;
4192 out_camera->data.orthographic.znear = cgltf_json_to_float(tokens + i, json_chunk);
4193 ++i;
4194 }
4195 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
4196 {
4197 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras);
4198 }
4199 else
4200 {
4201 i = cgltf_skip_json(tokens, i+1);
4202 }
4203
4204 if (i < 0)
4205 {
4206 return i;
4207 }
4208 }
4209 }
4210 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
4211 {
4212 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->extras);
4213 }
4214 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
4215 {
4216 i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_camera->extensions_count, &out_camera->extensions);
4217 }
4218 else
4219 {
4220 i = cgltf_skip_json(tokens, i+1);
4221 }
4222
4223 if (i < 0)
4224 {
4225 return i;
4226 }
4227 }
4228
4229 return i;
4230}
4231
4232static int cgltf_parse_json_cameras(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
4233{
4234 i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_camera), (void**)&out_data->cameras, &out_data->cameras_count);
4235 if (i < 0)
4236 {
4237 return i;
4238 }
4239
4240 for (cgltf_size j = 0; j < out_data->cameras_count; ++j)
4241 {
4242 i = cgltf_parse_json_camera(options, tokens, i, json_chunk, &out_data->cameras[j]);
4243 if (i < 0)
4244 {
4245 return i;
4246 }
4247 }
4248 return i;
4249}
4250
4251static int cgltf_parse_json_light(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_light* out_light)
4252{
4253 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
4254
4255 int size = tokens[i].size;
4256 ++i;
4257
4258 for (int j = 0; j < size; ++j)
4259 {
4260 CGLTF_CHECK_KEY(tokens[i]);
4261
4262 if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
4263 {
4264 i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_light->name);
4265 }
4266 else if (cgltf_json_strcmp(tokens + i, json_chunk, "color") == 0)
4267 {
4268 i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_light->color, 3);
4269 }
4270 else if (cgltf_json_strcmp(tokens + i, json_chunk, "intensity") == 0)
4271 {
4272 ++i;
4273 out_light->intensity = cgltf_json_to_float(tokens + i, json_chunk);
4274 ++i;
4275 }
4276 else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0)
4277 {
4278 ++i;
4279 if (cgltf_json_strcmp(tokens + i, json_chunk, "directional") == 0)
4280 {
4281 out_light->type = cgltf_light_type_directional;
4282 }
4283 else if (cgltf_json_strcmp(tokens + i, json_chunk, "point") == 0)
4284 {
4285 out_light->type = cgltf_light_type_point;
4286 }
4287 else if (cgltf_json_strcmp(tokens + i, json_chunk, "spot") == 0)
4288 {
4289 out_light->type = cgltf_light_type_spot;
4290 }
4291 ++i;
4292 }
4293 else if (cgltf_json_strcmp(tokens + i, json_chunk, "range") == 0)
4294 {
4295 ++i;
4296 out_light->range = cgltf_json_to_float(tokens + i, json_chunk);
4297 ++i;
4298 }
4299 else if (cgltf_json_strcmp(tokens+i, json_chunk, "spot") == 0)
4300 {
4301 ++i;
4302
4303 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
4304
4305 int data_size = tokens[i].size;
4306 ++i;
4307
4308 for (int k = 0; k < data_size; ++k)
4309 {
4310 CGLTF_CHECK_KEY(tokens[i]);
4311
4312 if (cgltf_json_strcmp(tokens+i, json_chunk, "innerConeAngle") == 0)
4313 {
4314 ++i;
4315 out_light->spot_inner_cone_angle = cgltf_json_to_float(tokens + i, json_chunk);
4316 ++i;
4317 }
4318 else if (cgltf_json_strcmp(tokens+i, json_chunk, "outerConeAngle") == 0)
4319 {
4320 ++i;
4321 out_light->spot_outer_cone_angle = cgltf_json_to_float(tokens + i, json_chunk);
4322 ++i;
4323 }
4324 else
4325 {
4326 i = cgltf_skip_json(tokens, i+1);
4327 }
4328
4329 if (i < 0)
4330 {
4331 return i;
4332 }
4333 }
4334 }
4335 else
4336 {
4337 i = cgltf_skip_json(tokens, i+1);
4338 }
4339
4340 if (i < 0)
4341 {
4342 return i;
4343 }
4344 }
4345
4346 return i;
4347}
4348
4349static int cgltf_parse_json_lights(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
4350{
4351 i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_light), (void**)&out_data->lights, &out_data->lights_count);
4352 if (i < 0)
4353 {
4354 return i;
4355 }
4356
4357 for (cgltf_size j = 0; j < out_data->lights_count; ++j)
4358 {
4359 i = cgltf_parse_json_light(options, tokens, i, json_chunk, &out_data->lights[j]);
4360 if (i < 0)
4361 {
4362 return i;
4363 }
4364 }
4365 return i;
4366}
4367
4368static int cgltf_parse_json_node(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_node* out_node)
4369{
4370 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
4371
4372 out_node->rotation[3] = 1.0f;
4373 out_node->scale[0] = 1.0f;
4374 out_node->scale[1] = 1.0f;
4375 out_node->scale[2] = 1.0f;
4376 out_node->matrix[0] = 1.0f;
4377 out_node->matrix[5] = 1.0f;
4378 out_node->matrix[10] = 1.0f;
4379 out_node->matrix[15] = 1.0f;
4380
4381 int size = tokens[i].size;
4382 ++i;
4383
4384 for (int j = 0; j < size; ++j)
4385 {
4386 CGLTF_CHECK_KEY(tokens[i]);
4387
4388 if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
4389 {
4390 i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_node->name);
4391 }
4392 else if (cgltf_json_strcmp(tokens+i, json_chunk, "children") == 0)
4393 {
4394 i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_node->children, &out_node->children_count);
4395 if (i < 0)
4396 {
4397 return i;
4398 }
4399
4400 for (cgltf_size k = 0; k < out_node->children_count; ++k)
4401 {
4402 out_node->children[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk));
4403 ++i;
4404 }
4405 }
4406 else if (cgltf_json_strcmp(tokens+i, json_chunk, "mesh") == 0)
4407 {
4408 ++i;
4409 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
4410 out_node->mesh = CGLTF_PTRINDEX(cgltf_mesh, cgltf_json_to_int(tokens + i, json_chunk));
4411 ++i;
4412 }
4413 else if (cgltf_json_strcmp(tokens+i, json_chunk, "skin") == 0)
4414 {
4415 ++i;
4416 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
4417 out_node->skin = CGLTF_PTRINDEX(cgltf_skin, cgltf_json_to_int(tokens + i, json_chunk));
4418 ++i;
4419 }
4420 else if (cgltf_json_strcmp(tokens+i, json_chunk, "camera") == 0)
4421 {
4422 ++i;
4423 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
4424 out_node->camera = CGLTF_PTRINDEX(cgltf_camera, cgltf_json_to_int(tokens + i, json_chunk));
4425 ++i;
4426 }
4427 else if (cgltf_json_strcmp(tokens+i, json_chunk, "translation") == 0)
4428 {
4429 out_node->has_translation = 1;
4430 i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->translation, 3);
4431 }
4432 else if (cgltf_json_strcmp(tokens+i, json_chunk, "rotation") == 0)
4433 {
4434 out_node->has_rotation = 1;
4435 i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->rotation, 4);
4436 }
4437 else if (cgltf_json_strcmp(tokens+i, json_chunk, "scale") == 0)
4438 {
4439 out_node->has_scale = 1;
4440 i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->scale, 3);
4441 }
4442 else if (cgltf_json_strcmp(tokens+i, json_chunk, "matrix") == 0)
4443 {
4444 out_node->has_matrix = 1;
4445 i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->matrix, 16);
4446 }
4447 else if (cgltf_json_strcmp(tokens + i, json_chunk, "weights") == 0)
4448 {
4449 i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_float), (void**)&out_node->weights, &out_node->weights_count);
4450 if (i < 0)
4451 {
4452 return i;
4453 }
4454
4455 i = cgltf_parse_json_float_array(tokens, i - 1, json_chunk, out_node->weights, (int)out_node->weights_count);
4456 }
4457 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
4458 {
4459 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_node->extras);
4460 }
4461 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
4462 {
4463 ++i;
4464
4465 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
4466 if(out_node->extensions)
4467 {
4468 return CGLTF_ERROR_JSON;
4469 }
4470
4471 int extensions_size = tokens[i].size;
4472 out_node->extensions_count= 0;
4473 out_node->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size);
4474
4475 if (!out_node->extensions)
4476 {
4477 return CGLTF_ERROR_NOMEM;
4478 }
4479
4480 ++i;
4481
4482 for (int k = 0; k < extensions_size; ++k)
4483 {
4484 CGLTF_CHECK_KEY(tokens[i]);
4485
4486 if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_lights_punctual") == 0)
4487 {
4488 ++i;
4489
4490 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
4491
4492 int data_size = tokens[i].size;
4493 ++i;
4494
4495 for (int m = 0; m < data_size; ++m)
4496 {
4497 CGLTF_CHECK_KEY(tokens[i]);
4498
4499 if (cgltf_json_strcmp(tokens + i, json_chunk, "light") == 0)
4500 {
4501 ++i;
4502 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
4503 out_node->light = CGLTF_PTRINDEX(cgltf_light, cgltf_json_to_int(tokens + i, json_chunk));
4504 ++i;
4505 }
4506 else
4507 {
4508 i = cgltf_skip_json(tokens, i + 1);
4509 }
4510
4511 if (i < 0)
4512 {
4513 return i;
4514 }
4515 }
4516 }
4517 else
4518 {
4519 i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_node->extensions[out_node->extensions_count++]));
4520 }
4521
4522 if (i < 0)
4523 {
4524 return i;
4525 }
4526 }
4527 }
4528 else
4529 {
4530 i = cgltf_skip_json(tokens, i+1);
4531 }
4532
4533 if (i < 0)
4534 {
4535 return i;
4536 }
4537 }
4538
4539 return i;
4540}
4541
4542static int cgltf_parse_json_nodes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
4543{
4544 i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_node), (void**)&out_data->nodes, &out_data->nodes_count);
4545 if (i < 0)
4546 {
4547 return i;
4548 }
4549
4550 for (cgltf_size j = 0; j < out_data->nodes_count; ++j)
4551 {
4552 i = cgltf_parse_json_node(options, tokens, i, json_chunk, &out_data->nodes[j]);
4553 if (i < 0)
4554 {
4555 return i;
4556 }
4557 }
4558 return i;
4559}
4560
4561static int cgltf_parse_json_scene(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_scene* out_scene)
4562{
4563 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
4564
4565 int size = tokens[i].size;
4566 ++i;
4567
4568 for (int j = 0; j < size; ++j)
4569 {
4570 CGLTF_CHECK_KEY(tokens[i]);
4571
4572 if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
4573 {
4574 i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_scene->name);
4575 }
4576 else if (cgltf_json_strcmp(tokens+i, json_chunk, "nodes") == 0)
4577 {
4578 i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_scene->nodes, &out_scene->nodes_count);
4579 if (i < 0)
4580 {
4581 return i;
4582 }
4583
4584 for (cgltf_size k = 0; k < out_scene->nodes_count; ++k)
4585 {
4586 out_scene->nodes[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk));
4587 ++i;
4588 }
4589 }
4590 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
4591 {
4592 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_scene->extras);
4593 }
4594 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
4595 {
4596 i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_scene->extensions_count, &out_scene->extensions);
4597 }
4598 else
4599 {
4600 i = cgltf_skip_json(tokens, i+1);
4601 }
4602
4603 if (i < 0)
4604 {
4605 return i;
4606 }
4607 }
4608
4609 return i;
4610}
4611
4612static int cgltf_parse_json_scenes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
4613{
4614 i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_scene), (void**)&out_data->scenes, &out_data->scenes_count);
4615 if (i < 0)
4616 {
4617 return i;
4618 }
4619
4620 for (cgltf_size j = 0; j < out_data->scenes_count; ++j)
4621 {
4622 i = cgltf_parse_json_scene(options, tokens, i, json_chunk, &out_data->scenes[j]);
4623 if (i < 0)
4624 {
4625 return i;
4626 }
4627 }
4628 return i;
4629}
4630
4631static int cgltf_parse_json_animation_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation_sampler* out_sampler)
4632{
4633 (void)options;
4634 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
4635
4636 int size = tokens[i].size;
4637 ++i;
4638
4639 for (int j = 0; j < size; ++j)
4640 {
4641 CGLTF_CHECK_KEY(tokens[i]);
4642
4643 if (cgltf_json_strcmp(tokens+i, json_chunk, "input") == 0)
4644 {
4645 ++i;
4646 out_sampler->input = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk));
4647 ++i;
4648 }
4649 else if (cgltf_json_strcmp(tokens+i, json_chunk, "output") == 0)
4650 {
4651 ++i;
4652 out_sampler->output = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk));
4653 ++i;
4654 }
4655 else if (cgltf_json_strcmp(tokens+i, json_chunk, "interpolation") == 0)
4656 {
4657 ++i;
4658 if (cgltf_json_strcmp(tokens + i, json_chunk, "LINEAR") == 0)
4659 {
4660 out_sampler->interpolation = cgltf_interpolation_type_linear;
4661 }
4662 else if (cgltf_json_strcmp(tokens + i, json_chunk, "STEP") == 0)
4663 {
4664 out_sampler->interpolation = cgltf_interpolation_type_step;
4665 }
4666 else if (cgltf_json_strcmp(tokens + i, json_chunk, "CUBICSPLINE") == 0)
4667 {
4668 out_sampler->interpolation = cgltf_interpolation_type_cubic_spline;
4669 }
4670 ++i;
4671 }
4672 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
4673 {
4674 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sampler->extras);
4675 }
4676 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
4677 {
4678 i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sampler->extensions_count, &out_sampler->extensions);
4679 }
4680 else
4681 {
4682 i = cgltf_skip_json(tokens, i+1);
4683 }
4684
4685 if (i < 0)
4686 {
4687 return i;
4688 }
4689 }
4690
4691 return i;
4692}
4693
4694static int cgltf_parse_json_animation_channel(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation_channel* out_channel)
4695{
4696 (void)options;
4697 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
4698
4699 int size = tokens[i].size;
4700 ++i;
4701
4702 for (int j = 0; j < size; ++j)
4703 {
4704 CGLTF_CHECK_KEY(tokens[i]);
4705
4706 if (cgltf_json_strcmp(tokens+i, json_chunk, "sampler") == 0)
4707 {
4708 ++i;
4709 out_channel->sampler = CGLTF_PTRINDEX(cgltf_animation_sampler, cgltf_json_to_int(tokens + i, json_chunk));
4710 ++i;
4711 }
4712 else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0)
4713 {
4714 ++i;
4715
4716 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
4717
4718 int target_size = tokens[i].size;
4719 ++i;
4720
4721 for (int k = 0; k < target_size; ++k)
4722 {
4723 CGLTF_CHECK_KEY(tokens[i]);
4724
4725 if (cgltf_json_strcmp(tokens+i, json_chunk, "node") == 0)
4726 {
4727 ++i;
4728 out_channel->target_node = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk));
4729 ++i;
4730 }
4731 else if (cgltf_json_strcmp(tokens+i, json_chunk, "path") == 0)
4732 {
4733 ++i;
4734 if (cgltf_json_strcmp(tokens+i, json_chunk, "translation") == 0)
4735 {
4736 out_channel->target_path = cgltf_animation_path_type_translation;
4737 }
4738 else if (cgltf_json_strcmp(tokens+i, json_chunk, "rotation") == 0)
4739 {
4740 out_channel->target_path = cgltf_animation_path_type_rotation;
4741 }
4742 else if (cgltf_json_strcmp(tokens+i, json_chunk, "scale") == 0)
4743 {
4744 out_channel->target_path = cgltf_animation_path_type_scale;
4745 }
4746 else if (cgltf_json_strcmp(tokens+i, json_chunk, "weights") == 0)
4747 {
4748 out_channel->target_path = cgltf_animation_path_type_weights;
4749 }
4750 ++i;
4751 }
4752 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
4753 {
4754 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_channel->extras);
4755 }
4756 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
4757 {
4758 i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_channel->extensions_count, &out_channel->extensions);
4759 }
4760 else
4761 {
4762 i = cgltf_skip_json(tokens, i+1);
4763 }
4764
4765 if (i < 0)
4766 {
4767 return i;
4768 }
4769 }
4770 }
4771 else
4772 {
4773 i = cgltf_skip_json(tokens, i+1);
4774 }
4775
4776 if (i < 0)
4777 {
4778 return i;
4779 }
4780 }
4781
4782 return i;
4783}
4784
4785static int cgltf_parse_json_animation(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation* out_animation)
4786{
4787 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
4788
4789 int size = tokens[i].size;
4790 ++i;
4791
4792 for (int j = 0; j < size; ++j)
4793 {
4794 CGLTF_CHECK_KEY(tokens[i]);
4795
4796 if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0)
4797 {
4798 i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_animation->name);
4799 }
4800 else if (cgltf_json_strcmp(tokens+i, json_chunk, "samplers") == 0)
4801 {
4802 i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_animation_sampler), (void**)&out_animation->samplers, &out_animation->samplers_count);
4803 if (i < 0)
4804 {
4805 return i;
4806 }
4807
4808 for (cgltf_size k = 0; k < out_animation->samplers_count; ++k)
4809 {
4810 i = cgltf_parse_json_animation_sampler(options, tokens, i, json_chunk, &out_animation->samplers[k]);
4811 if (i < 0)
4812 {
4813 return i;
4814 }
4815 }
4816 }
4817 else if (cgltf_json_strcmp(tokens+i, json_chunk, "channels") == 0)
4818 {
4819 i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_animation_channel), (void**)&out_animation->channels, &out_animation->channels_count);
4820 if (i < 0)
4821 {
4822 return i;
4823 }
4824
4825 for (cgltf_size k = 0; k < out_animation->channels_count; ++k)
4826 {
4827 i = cgltf_parse_json_animation_channel(options, tokens, i, json_chunk, &out_animation->channels[k]);
4828 if (i < 0)
4829 {
4830 return i;
4831 }
4832 }
4833 }
4834 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
4835 {
4836 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_animation->extras);
4837 }
4838 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
4839 {
4840 i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_animation->extensions_count, &out_animation->extensions);
4841 }
4842 else
4843 {
4844 i = cgltf_skip_json(tokens, i+1);
4845 }
4846
4847 if (i < 0)
4848 {
4849 return i;
4850 }
4851 }
4852
4853 return i;
4854}
4855
4856static int cgltf_parse_json_animations(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
4857{
4858 i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_animation), (void**)&out_data->animations, &out_data->animations_count);
4859 if (i < 0)
4860 {
4861 return i;
4862 }
4863
4864 for (cgltf_size j = 0; j < out_data->animations_count; ++j)
4865 {
4866 i = cgltf_parse_json_animation(options, tokens, i, json_chunk, &out_data->animations[j]);
4867 if (i < 0)
4868 {
4869 return i;
4870 }
4871 }
4872 return i;
4873}
4874
4875static int cgltf_parse_json_asset(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_asset* out_asset)
4876{
4877 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
4878
4879 int size = tokens[i].size;
4880 ++i;
4881
4882 for (int j = 0; j < size; ++j)
4883 {
4884 CGLTF_CHECK_KEY(tokens[i]);
4885
4886 if (cgltf_json_strcmp(tokens+i, json_chunk, "copyright") == 0)
4887 {
4888 i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->copyright);
4889 }
4890 else if (cgltf_json_strcmp(tokens+i, json_chunk, "generator") == 0)
4891 {
4892 i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->generator);
4893 }
4894 else if (cgltf_json_strcmp(tokens+i, json_chunk, "version") == 0)
4895 {
4896 i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->version);
4897 }
4898 else if (cgltf_json_strcmp(tokens+i, json_chunk, "minVersion") == 0)
4899 {
4900 i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->min_version);
4901 }
4902 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
4903 {
4904 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_asset->extras);
4905 }
4906 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
4907 {
4908 i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_asset->extensions_count, &out_asset->extensions);
4909 }
4910 else
4911 {
4912 i = cgltf_skip_json(tokens, i+1);
4913 }
4914
4915 if (i < 0)
4916 {
4917 return i;
4918 }
4919 }
4920
4921 if (out_asset->version && CGLTF_ATOF(out_asset->version) < 2)
4922 {
4923 return CGLTF_ERROR_LEGACY;
4924 }
4925
4926 return i;
4927}
4928
4929cgltf_size cgltf_num_components(cgltf_type type) {
4930 switch (type)
4931 {
4932 case cgltf_type_vec2:
4933 return 2;
4934 case cgltf_type_vec3:
4935 return 3;
4936 case cgltf_type_vec4:
4937 return 4;
4938 case cgltf_type_mat2:
4939 return 4;
4940 case cgltf_type_mat3:
4941 return 9;
4942 case cgltf_type_mat4:
4943 return 16;
4944 case cgltf_type_invalid:
4945 case cgltf_type_scalar:
4946 default:
4947 return 1;
4948 }
4949}
4950
4951static cgltf_size cgltf_component_size(cgltf_component_type component_type) {
4952 switch (component_type)
4953 {
4954 case cgltf_component_type_r_8:
4955 case cgltf_component_type_r_8u:
4956 return 1;
4957 case cgltf_component_type_r_16:
4958 case cgltf_component_type_r_16u:
4959 return 2;
4960 case cgltf_component_type_r_32u:
4961 case cgltf_component_type_r_32f:
4962 return 4;
4963 case cgltf_component_type_invalid:
4964 default:
4965 return 0;
4966 }
4967}
4968
4969static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type)
4970{
4971 cgltf_size component_size = cgltf_component_size(component_type);
4972 if (type == cgltf_type_mat2 && component_size == 1)
4973 {
4974 return 8 * component_size;
4975 }
4976 else if (type == cgltf_type_mat3 && (component_size == 1 || component_size == 2))
4977 {
4978 return 12 * component_size;
4979 }
4980 return component_size * cgltf_num_components(type);
4981}
4982
4983static int cgltf_fixup_pointers(cgltf_data* out_data);
4984
4985static int cgltf_parse_json_root(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data)
4986{
4987 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
4988
4989 int size = tokens[i].size;
4990 ++i;
4991
4992 for (int j = 0; j < size; ++j)
4993 {
4994 CGLTF_CHECK_KEY(tokens[i]);
4995
4996 if (cgltf_json_strcmp(tokens + i, json_chunk, "asset") == 0)
4997 {
4998 i = cgltf_parse_json_asset(options, tokens, i + 1, json_chunk, &out_data->asset);
4999 }
5000 else if (cgltf_json_strcmp(tokens + i, json_chunk, "meshes") == 0)
5001 {
5002 i = cgltf_parse_json_meshes(options, tokens, i + 1, json_chunk, out_data);
5003 }
5004 else if (cgltf_json_strcmp(tokens + i, json_chunk, "accessors") == 0)
5005 {
5006 i = cgltf_parse_json_accessors(options, tokens, i + 1, json_chunk, out_data);
5007 }
5008 else if (cgltf_json_strcmp(tokens + i, json_chunk, "bufferViews") == 0)
5009 {
5010 i = cgltf_parse_json_buffer_views(options, tokens, i + 1, json_chunk, out_data);
5011 }
5012 else if (cgltf_json_strcmp(tokens + i, json_chunk, "buffers") == 0)
5013 {
5014 i = cgltf_parse_json_buffers(options, tokens, i + 1, json_chunk, out_data);
5015 }
5016 else if (cgltf_json_strcmp(tokens + i, json_chunk, "materials") == 0)
5017 {
5018 i = cgltf_parse_json_materials(options, tokens, i + 1, json_chunk, out_data);
5019 }
5020 else if (cgltf_json_strcmp(tokens + i, json_chunk, "images") == 0)
5021 {
5022 i = cgltf_parse_json_images(options, tokens, i + 1, json_chunk, out_data);
5023 }
5024 else if (cgltf_json_strcmp(tokens + i, json_chunk, "textures") == 0)
5025 {
5026 i = cgltf_parse_json_textures(options, tokens, i + 1, json_chunk, out_data);
5027 }
5028 else if (cgltf_json_strcmp(tokens + i, json_chunk, "samplers") == 0)
5029 {
5030 i = cgltf_parse_json_samplers(options, tokens, i + 1, json_chunk, out_data);
5031 }
5032 else if (cgltf_json_strcmp(tokens + i, json_chunk, "skins") == 0)
5033 {
5034 i = cgltf_parse_json_skins(options, tokens, i + 1, json_chunk, out_data);
5035 }
5036 else if (cgltf_json_strcmp(tokens + i, json_chunk, "cameras") == 0)
5037 {
5038 i = cgltf_parse_json_cameras(options, tokens, i + 1, json_chunk, out_data);
5039 }
5040 else if (cgltf_json_strcmp(tokens + i, json_chunk, "nodes") == 0)
5041 {
5042 i = cgltf_parse_json_nodes(options, tokens, i + 1, json_chunk, out_data);
5043 }
5044 else if (cgltf_json_strcmp(tokens + i, json_chunk, "scenes") == 0)
5045 {
5046 i = cgltf_parse_json_scenes(options, tokens, i + 1, json_chunk, out_data);
5047 }
5048 else if (cgltf_json_strcmp(tokens + i, json_chunk, "scene") == 0)
5049 {
5050 ++i;
5051 out_data->scene = CGLTF_PTRINDEX(cgltf_scene, cgltf_json_to_int(tokens + i, json_chunk));
5052 ++i;
5053 }
5054 else if (cgltf_json_strcmp(tokens + i, json_chunk, "animations") == 0)
5055 {
5056 i = cgltf_parse_json_animations(options, tokens, i + 1, json_chunk, out_data);
5057 }
5058 else if (cgltf_json_strcmp(tokens+i, json_chunk, "extras") == 0)
5059 {
5060 i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_data->extras);
5061 }
5062 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
5063 {
5064 ++i;
5065
5066 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
5067 if(out_data->data_extensions)
5068 {
5069 return CGLTF_ERROR_JSON;
5070 }
5071
5072 int extensions_size = tokens[i].size;
5073 out_data->data_extensions_count = 0;
5074 out_data->data_extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size);
5075
5076 if (!out_data->data_extensions)
5077 {
5078 return CGLTF_ERROR_NOMEM;
5079 }
5080
5081 ++i;
5082
5083 for (int k = 0; k < extensions_size; ++k)
5084 {
5085 CGLTF_CHECK_KEY(tokens[i]);
5086
5087 if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_lights_punctual") == 0)
5088 {
5089 ++i;
5090
5091 CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
5092
5093 int data_size = tokens[i].size;
5094 ++i;
5095
5096 for (int m = 0; m < data_size; ++m)
5097 {
5098 CGLTF_CHECK_KEY(tokens[i]);
5099
5100 if (cgltf_json_strcmp(tokens + i, json_chunk, "lights") == 0)
5101 {
5102 i = cgltf_parse_json_lights(options, tokens, i + 1, json_chunk, out_data);
5103 }
5104 else
5105 {
5106 i = cgltf_skip_json(tokens, i + 1);
5107 }
5108
5109 if (i < 0)
5110 {
5111 return i;
5112 }
5113 }
5114 }
5115 else
5116 {
5117 i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_data->data_extensions[out_data->data_extensions_count++]));
5118 }
5119
5120 if (i < 0)
5121 {
5122 return i;
5123 }
5124 }
5125 }
5126 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensionsUsed") == 0)
5127 {
5128 i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_data->extensions_used, &out_data->extensions_used_count);
5129 }
5130 else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensionsRequired") == 0)
5131 {
5132 i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_data->extensions_required, &out_data->extensions_required_count);
5133 }
5134 else
5135 {
5136 i = cgltf_skip_json(tokens, i + 1);
5137 }
5138
5139 if (i < 0)
5140 {
5141 return i;
5142 }
5143 }
5144
5145 return i;
5146}
5147
5148cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data** out_data)
5149{
5150 jsmn_parser parser = { 0, 0, 0 };
5151
5152 if (options->json_token_count == 0)
5153 {
5154 int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, NULL, 0);
5155
5156 if (token_count <= 0)
5157 {
5158 return cgltf_result_invalid_json;
5159 }
5160
5161 options->json_token_count = token_count;
5162 }
5163
5164 jsmntok_t* tokens = (jsmntok_t*)options->memory.alloc(options->memory.user_data, sizeof(jsmntok_t) * (options->json_token_count + 1));
5165
5166 if (!tokens)
5167 {
5168 return cgltf_result_out_of_memory;
5169 }
5170
5171 jsmn_init(&parser);
5172
5173 int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, tokens, options->json_token_count);
5174
5175 if (token_count <= 0)
5176 {
5177 options->memory.free(options->memory.user_data, tokens);
5178 return cgltf_result_invalid_json;
5179 }
5180
5181 // this makes sure that we always have an UNDEFINED token at the end of the stream
5182 // for invalid JSON inputs this makes sure we don't perform out of bound reads of token data
5183 tokens[token_count].type = JSMN_UNDEFINED;
5184
5185 cgltf_data* data = (cgltf_data*)options->memory.alloc(options->memory.user_data, sizeof(cgltf_data));
5186
5187 if (!data)
5188 {
5189 options->memory.free(options->memory.user_data, tokens);
5190 return cgltf_result_out_of_memory;
5191 }
5192
5193 memset(data, 0, sizeof(cgltf_data));
5194 data->memory = options->memory;
5195 data->file = options->file;
5196
5197 int i = cgltf_parse_json_root(options, tokens, 0, json_chunk, data);
5198
5199 options->memory.free(options->memory.user_data, tokens);
5200
5201 if (i < 0)
5202 {
5203 cgltf_free(data);
5204
5205 switch (i)
5206 {
5207 case CGLTF_ERROR_NOMEM: return cgltf_result_out_of_memory;
5208 case CGLTF_ERROR_LEGACY: return cgltf_result_legacy_gltf;
5209 default: return cgltf_result_invalid_gltf;
5210 }
5211 }
5212
5213 if (cgltf_fixup_pointers(data) < 0)
5214 {
5215 cgltf_free(data);
5216 return cgltf_result_invalid_gltf;
5217 }
5218
5219 data->json = (const char*)json_chunk;
5220 data->json_size = size;
5221
5222 *out_data = data;
5223
5224 return cgltf_result_success;
5225}
5226
5227static int cgltf_fixup_pointers(cgltf_data* data)
5228{
5229 for (cgltf_size i = 0; i < data->meshes_count; ++i)
5230 {
5231 for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j)
5232 {
5233 CGLTF_PTRFIXUP(data->meshes[i].primitives[j].indices, data->accessors, data->accessors_count);
5234 CGLTF_PTRFIXUP(data->meshes[i].primitives[j].material, data->materials, data->materials_count);
5235
5236 for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k)
5237 {
5238 CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].attributes[k].data, data->accessors, data->accessors_count);
5239 }
5240
5241 for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k)
5242 {
5243 for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m)
5244 {
5245 CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].targets[k].attributes[m].data, data->accessors, data->accessors_count);
5246 }
5247 }
5248
5249 if (data->meshes[i].primitives[j].has_draco_mesh_compression)
5250 {
5251 CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].draco_mesh_compression.buffer_view, data->buffer_views, data->buffer_views_count);
5252 for (cgltf_size m = 0; m < data->meshes[i].primitives[j].draco_mesh_compression.attributes_count; ++m)
5253 {
5254 CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].draco_mesh_compression.attributes[m].data, data->accessors, data->accessors_count);
5255 }
5256 }
5257 }
5258 }
5259
5260 for (cgltf_size i = 0; i < data->accessors_count; ++i)
5261 {
5262 CGLTF_PTRFIXUP(data->accessors[i].buffer_view, data->buffer_views, data->buffer_views_count);
5263
5264 if (data->accessors[i].is_sparse)
5265 {
5266 CGLTF_PTRFIXUP_REQ(data->accessors[i].sparse.indices_buffer_view, data->buffer_views, data->buffer_views_count);
5267 CGLTF_PTRFIXUP_REQ(data->accessors[i].sparse.values_buffer_view, data->buffer_views, data->buffer_views_count);
5268 }
5269
5270 if (data->accessors[i].buffer_view)
5271 {
5272 data->accessors[i].stride = data->accessors[i].buffer_view->stride;
5273 }
5274
5275 if (data->accessors[i].stride == 0)
5276 {
5277 data->accessors[i].stride = cgltf_calc_size(data->accessors[i].type, data->accessors[i].component_type);
5278 }
5279 }
5280
5281 for (cgltf_size i = 0; i < data->textures_count; ++i)
5282 {
5283 CGLTF_PTRFIXUP(data->textures[i].image, data->images, data->images_count);
5284 CGLTF_PTRFIXUP(data->textures[i].sampler, data->samplers, data->samplers_count);
5285 }
5286
5287 for (cgltf_size i = 0; i < data->images_count; ++i)
5288 {
5289 CGLTF_PTRFIXUP(data->images[i].buffer_view, data->buffer_views, data->buffer_views_count);
5290 }
5291
5292 for (cgltf_size i = 0; i < data->materials_count; ++i)
5293 {
5294 CGLTF_PTRFIXUP(data->materials[i].normal_texture.texture, data->textures, data->textures_count);
5295 CGLTF_PTRFIXUP(data->materials[i].emissive_texture.texture, data->textures, data->textures_count);
5296 CGLTF_PTRFIXUP(data->materials[i].occlusion_texture.texture, data->textures, data->textures_count);
5297
5298 CGLTF_PTRFIXUP(data->materials[i].pbr_metallic_roughness.base_color_texture.texture, data->textures, data->textures_count);
5299 CGLTF_PTRFIXUP(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture, data->textures, data->textures_count);
5300
5301 CGLTF_PTRFIXUP(data->materials[i].pbr_specular_glossiness.diffuse_texture.texture, data->textures, data->textures_count);
5302 CGLTF_PTRFIXUP(data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.texture, data->textures, data->textures_count);
5303
5304 CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_texture.texture, data->textures, data->textures_count);
5305 CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_roughness_texture.texture, data->textures, data->textures_count);
5306 CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_normal_texture.texture, data->textures, data->textures_count);
5307
5308 CGLTF_PTRFIXUP(data->materials[i].specular.specular_texture.texture, data->textures, data->textures_count);
5309
5310 CGLTF_PTRFIXUP(data->materials[i].transmission.transmission_texture.texture, data->textures, data->textures_count);
5311 }
5312
5313 for (cgltf_size i = 0; i < data->buffer_views_count; ++i)
5314 {
5315 CGLTF_PTRFIXUP_REQ(data->buffer_views[i].buffer, data->buffers, data->buffers_count);
5316 }
5317
5318 for (cgltf_size i = 0; i < data->skins_count; ++i)
5319 {
5320 for (cgltf_size j = 0; j < data->skins[i].joints_count; ++j)
5321 {
5322 CGLTF_PTRFIXUP_REQ(data->skins[i].joints[j], data->nodes, data->nodes_count);
5323 }
5324
5325 CGLTF_PTRFIXUP(data->skins[i].skeleton, data->nodes, data->nodes_count);
5326 CGLTF_PTRFIXUP(data->skins[i].inverse_bind_matrices, data->accessors, data->accessors_count);
5327 }
5328
5329 for (cgltf_size i = 0; i < data->nodes_count; ++i)
5330 {
5331 for (cgltf_size j = 0; j < data->nodes[i].children_count; ++j)
5332 {
5333 CGLTF_PTRFIXUP_REQ(data->nodes[i].children[j], data->nodes, data->nodes_count);
5334
5335 if (data->nodes[i].children[j]->parent)
5336 {
5337 return CGLTF_ERROR_JSON;
5338 }
5339
5340 data->nodes[i].children[j]->parent = &data->nodes[i];
5341 }
5342
5343 CGLTF_PTRFIXUP(data->nodes[i].mesh, data->meshes, data->meshes_count);
5344 CGLTF_PTRFIXUP(data->nodes[i].skin, data->skins, data->skins_count);
5345 CGLTF_PTRFIXUP(data->nodes[i].camera, data->cameras, data->cameras_count);
5346 CGLTF_PTRFIXUP(data->nodes[i].light, data->lights, data->lights_count);
5347 }
5348
5349 for (cgltf_size i = 0; i < data->scenes_count; ++i)
5350 {
5351 for (cgltf_size j = 0; j < data->scenes[i].nodes_count; ++j)
5352 {
5353 CGLTF_PTRFIXUP_REQ(data->scenes[i].nodes[j], data->nodes, data->nodes_count);
5354
5355 if (data->scenes[i].nodes[j]->parent)
5356 {
5357 return CGLTF_ERROR_JSON;
5358 }
5359 }
5360 }
5361
5362 CGLTF_PTRFIXUP(data->scene, data->scenes, data->scenes_count);
5363
5364 for (cgltf_size i = 0; i < data->animations_count; ++i)
5365 {
5366 for (cgltf_size j = 0; j < data->animations[i].samplers_count; ++j)
5367 {
5368 CGLTF_PTRFIXUP_REQ(data->animations[i].samplers[j].input, data->accessors, data->accessors_count);
5369 CGLTF_PTRFIXUP_REQ(data->animations[i].samplers[j].output, data->accessors, data->accessors_count);
5370 }
5371
5372 for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j)
5373 {
5374 CGLTF_PTRFIXUP_REQ(data->animations[i].channels[j].sampler, data->animations[i].samplers, data->animations[i].samplers_count);
5375 CGLTF_PTRFIXUP(data->animations[i].channels[j].target_node, data->nodes, data->nodes_count);
5376 }
5377 }
5378
5379 return 0;
5380}
5381
5382/*
5383 * -- jsmn.c start --
5384 * Source: https://github.com/zserge/jsmn
5385 * License: MIT
5386 *
5387 * Copyright (c) 2010 Serge A. Zaitsev
5388
5389 * Permission is hereby granted, free of charge, to any person obtaining a copy
5390 * of this software and associated documentation files (the "Software"), to deal
5391 * in the Software without restriction, including without limitation the rights
5392 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
5393 * copies of the Software, and to permit persons to whom the Software is
5394 * furnished to do so, subject to the following conditions:
5395
5396 * The above copyright notice and this permission notice shall be included in
5397 * all copies or substantial portions of the Software.
5398
5399 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5400 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5401 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
5402 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
5403 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
5404 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
5405 * THE SOFTWARE.
5406 */
5407
5408/**
5409 * Allocates a fresh unused token from the token pull.
5410 */
5411static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
5412 jsmntok_t *tokens, size_t num_tokens) {
5413 jsmntok_t *tok;
5414 if (parser->toknext >= num_tokens) {
5415 return NULL;
5416 }
5417 tok = &tokens[parser->toknext++];
5418 tok->start = tok->end = -1;
5419 tok->size = 0;
5420#ifdef JSMN_PARENT_LINKS
5421 tok->parent = -1;
5422#endif
5423 return tok;
5424}
5425
5426/**
5427 * Fills token type and boundaries.
5428 */
5429static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
5430 int start, int end) {
5431 token->type = type;
5432 token->start = start;
5433 token->end = end;
5434 token->size = 0;
5435}
5436
5437/**
5438 * Fills next available token with JSON primitive.
5439 */
5440static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
5441 size_t len, jsmntok_t *tokens, size_t num_tokens) {
5442 jsmntok_t *token;
5443 int start;
5444
5445 start = parser->pos;
5446
5447 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
5448 switch (js[parser->pos]) {
5449#ifndef JSMN_STRICT
5450 /* In strict mode primitive must be followed by "," or "}" or "]" */
5451 case ':':
5452#endif
5453 case '\t' : case '\r' : case '\n' : case ' ' :
5454 case ',' : case ']' : case '}' :
5455 goto found;
5456 }
5457 if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
5458 parser->pos = start;
5459 return JSMN_ERROR_INVAL;
5460 }
5461 }
5462#ifdef JSMN_STRICT
5463 /* In strict mode primitive must be followed by a comma/object/array */
5464 parser->pos = start;
5465 return JSMN_ERROR_PART;
5466#endif
5467
5468found:
5469 if (tokens == NULL) {
5470 parser->pos--;
5471 return 0;
5472 }
5473 token = jsmn_alloc_token(parser, tokens, num_tokens);
5474 if (token == NULL) {
5475 parser->pos = start;
5476 return JSMN_ERROR_NOMEM;
5477 }
5478 jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
5479#ifdef JSMN_PARENT_LINKS
5480 token->parent = parser->toksuper;
5481#endif
5482 parser->pos--;
5483 return 0;
5484}
5485
5486/**
5487 * Fills next token with JSON string.
5488 */
5489static int jsmn_parse_string(jsmn_parser *parser, const char *js,
5490 size_t len, jsmntok_t *tokens, size_t num_tokens) {
5491 jsmntok_t *token;
5492
5493 int start = parser->pos;
5494
5495 parser->pos++;
5496
5497 /* Skip starting quote */
5498 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
5499 char c = js[parser->pos];
5500
5501 /* Quote: end of string */
5502 if (c == '\"') {
5503 if (tokens == NULL) {
5504 return 0;
5505 }
5506 token = jsmn_alloc_token(parser, tokens, num_tokens);
5507 if (token == NULL) {
5508 parser->pos = start;
5509 return JSMN_ERROR_NOMEM;
5510 }
5511 jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
5512#ifdef JSMN_PARENT_LINKS
5513 token->parent = parser->toksuper;
5514#endif
5515 return 0;
5516 }
5517
5518 /* Backslash: Quoted symbol expected */
5519 if (c == '\\' && parser->pos + 1 < len) {
5520 int i;
5521 parser->pos++;
5522 switch (js[parser->pos]) {
5523 /* Allowed escaped symbols */
5524 case '\"': case '/' : case '\\' : case 'b' :
5525 case 'f' : case 'r' : case 'n' : case 't' :
5526 break;
5527 /* Allows escaped symbol \uXXXX */
5528 case 'u':
5529 parser->pos++;
5530 for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
5531 /* If it isn't a hex character we have an error */
5532 if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
5533 (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
5534 (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
5535 parser->pos = start;
5536 return JSMN_ERROR_INVAL;
5537 }
5538 parser->pos++;
5539 }
5540 parser->pos--;
5541 break;
5542 /* Unexpected symbol */
5543 default:
5544 parser->pos = start;
5545 return JSMN_ERROR_INVAL;
5546 }
5547 }
5548 }
5549 parser->pos = start;
5550 return JSMN_ERROR_PART;
5551}
5552
5553/**
5554 * Parse JSON string and fill tokens.
5555 */
5556static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
5557 jsmntok_t *tokens, size_t num_tokens) {
5558 int r;
5559 int i;
5560 jsmntok_t *token;
5561 int count = parser->toknext;
5562
5563 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
5564 char c;
5565 jsmntype_t type;
5566
5567 c = js[parser->pos];
5568 switch (c) {
5569 case '{': case '[':
5570 count++;
5571 if (tokens == NULL) {
5572 break;
5573 }
5574 token = jsmn_alloc_token(parser, tokens, num_tokens);
5575 if (token == NULL)
5576 return JSMN_ERROR_NOMEM;
5577 if (parser->toksuper != -1) {
5578 tokens[parser->toksuper].size++;
5579#ifdef JSMN_PARENT_LINKS
5580 token->parent = parser->toksuper;
5581#endif
5582 }
5583 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
5584 token->start = parser->pos;
5585 parser->toksuper = parser->toknext - 1;
5586 break;
5587 case '}': case ']':
5588 if (tokens == NULL)
5589 break;
5590 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
5591#ifdef JSMN_PARENT_LINKS
5592 if (parser->toknext < 1) {
5593 return JSMN_ERROR_INVAL;
5594 }
5595 token = &tokens[parser->toknext - 1];
5596 for (;;) {
5597 if (token->start != -1 && token->end == -1) {
5598 if (token->type != type) {
5599 return JSMN_ERROR_INVAL;
5600 }
5601 token->end = parser->pos + 1;
5602 parser->toksuper = token->parent;
5603 break;
5604 }
5605 if (token->parent == -1) {
5606 if(token->type != type || parser->toksuper == -1) {
5607 return JSMN_ERROR_INVAL;
5608 }
5609 break;
5610 }
5611 token = &tokens[token->parent];
5612 }
5613#else
5614 for (i = parser->toknext - 1; i >= 0; i--) {
5615 token = &tokens[i];
5616 if (token->start != -1 && token->end == -1) {
5617 if (token->type != type) {
5618 return JSMN_ERROR_INVAL;
5619 }
5620 parser->toksuper = -1;
5621 token->end = parser->pos + 1;
5622 break;
5623 }
5624 }
5625 /* Error if unmatched closing bracket */
5626 if (i == -1) return JSMN_ERROR_INVAL;
5627 for (; i >= 0; i--) {
5628 token = &tokens[i];
5629 if (token->start != -1 && token->end == -1) {
5630 parser->toksuper = i;
5631 break;
5632 }
5633 }
5634#endif
5635 break;
5636 case '\"':
5637 r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
5638 if (r < 0) return r;
5639 count++;
5640 if (parser->toksuper != -1 && tokens != NULL)
5641 tokens[parser->toksuper].size++;
5642 break;
5643 case '\t' : case '\r' : case '\n' : case ' ':
5644 break;
5645 case ':':
5646 parser->toksuper = parser->toknext - 1;
5647 break;
5648 case ',':
5649 if (tokens != NULL && parser->toksuper != -1 &&
5650 tokens[parser->toksuper].type != JSMN_ARRAY &&
5651 tokens[parser->toksuper].type != JSMN_OBJECT) {
5652#ifdef JSMN_PARENT_LINKS
5653 parser->toksuper = tokens[parser->toksuper].parent;
5654#else
5655 for (i = parser->toknext - 1; i >= 0; i--) {
5656 if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
5657 if (tokens[i].start != -1 && tokens[i].end == -1) {
5658 parser->toksuper = i;
5659 break;
5660 }
5661 }
5662 }
5663#endif
5664 }
5665 break;
5666#ifdef JSMN_STRICT
5667 /* In strict mode primitives are: numbers and booleans */
5668 case '-': case '0': case '1' : case '2': case '3' : case '4':
5669 case '5': case '6': case '7' : case '8': case '9':
5670 case 't': case 'f': case 'n' :
5671 /* And they must not be keys of the object */
5672 if (tokens != NULL && parser->toksuper != -1) {
5673 jsmntok_t *t = &tokens[parser->toksuper];
5674 if (t->type == JSMN_OBJECT ||
5675 (t->type == JSMN_STRING && t->size != 0)) {
5676 return JSMN_ERROR_INVAL;
5677 }
5678 }
5679#else
5680 /* In non-strict mode every unquoted value is a primitive */
5681 default:
5682#endif
5683 r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
5684 if (r < 0) return r;
5685 count++;
5686 if (parser->toksuper != -1 && tokens != NULL)
5687 tokens[parser->toksuper].size++;
5688 break;
5689
5690#ifdef JSMN_STRICT
5691 /* Unexpected char in strict mode */
5692 default:
5693 return JSMN_ERROR_INVAL;
5694#endif
5695 }
5696 }
5697
5698 if (tokens != NULL) {
5699 for (i = parser->toknext - 1; i >= 0; i--) {
5700 /* Unmatched opened object or array */
5701 if (tokens[i].start != -1 && tokens[i].end == -1) {
5702 return JSMN_ERROR_PART;
5703 }
5704 }
5705 }
5706
5707 return count;
5708}
5709
5710/**
5711 * Creates a new parser based over a given buffer with an array of tokens
5712 * available.
5713 */
5714static void jsmn_init(jsmn_parser *parser) {
5715 parser->pos = 0;
5716 parser->toknext = 0;
5717 parser->toksuper = -1;
5718}
5719/*
5720 * -- jsmn.c end --
5721 */
5722
5723#endif /* #ifdef CGLTF_IMPLEMENTATION */
5724
5725/* cgltf is distributed under MIT license:
5726 *
5727 * Copyright (c) 2018 Johannes Kuhlmann
5728
5729 * Permission is hereby granted, free of charge, to any person obtaining a copy
5730 * of this software and associated documentation files (the "Software"), to deal
5731 * in the Software without restriction, including without limitation the rights
5732 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
5733 * copies of the Software, and to permit persons to whom the Software is
5734 * furnished to do so, subject to the following conditions:
5735
5736 * The above copyright notice and this permission notice shall be included in all
5737 * copies or substantial portions of the Software.
5738
5739 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5740 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5741 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
5742 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
5743 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
5744 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
5745 * SOFTWARE.
5746 */
diff --git a/contrib/cgltf/cgltf_write.h b/contrib/cgltf/cgltf_write.h
new file mode 100644
index 0000000..2096a5b
--- /dev/null
+++ b/contrib/cgltf/cgltf_write.h
@@ -0,0 +1,1173 @@
1/**
2 * cgltf_write - a single-file glTF 2.0 writer written in C99.
3 *
4 * Version: 1.7
5 *
6 * Website: https://github.com/jkuhlmann/cgltf
7 *
8 * Distributed under the MIT License, see notice at the end of this file.
9 *
10 * Building:
11 * Include this file where you need the struct and function
12 * declarations. Have exactly one source file where you define
13 * `CGLTF_WRITE_IMPLEMENTATION` before including this file to get the
14 * function definitions.
15 *
16 * Reference:
17 * `cgltf_result cgltf_write_file(const cgltf_options* options, const char*
18 * path, const cgltf_data* data)` writes JSON to the given file path. Buffer
19 * files and external images are not written out. `data` is not deallocated.
20 *
21 * `cgltf_size cgltf_write(const cgltf_options* options, char* buffer,
22 * cgltf_size size, const cgltf_data* data)` writes JSON into the given memory
23 * buffer. Returns the number of bytes written to `buffer`, including a null
24 * terminator. If buffer is null, returns the number of bytes that would have
25 * been written. `data` is not deallocated.
26 *
27 * To write custom JSON into the `extras` field, aggregate all the custom JSON
28 * into a single buffer, then set `file_data` to this buffer. By supplying
29 * start_offset and end_offset values for various objects, you can select a
30 * range of characters within the aggregated buffer.
31 */
32#ifndef CGLTF_WRITE_H_INCLUDED__
33#define CGLTF_WRITE_H_INCLUDED__
34
35#include "cgltf.h"
36
37#include <stddef.h>
38#include <stdbool.h>
39
40#ifdef __cplusplus
41extern "C" {
42#endif
43
44cgltf_result cgltf_write_file(const cgltf_options* options, const char* path, const cgltf_data* data);
45cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size size, const cgltf_data* data);
46
47#ifdef __cplusplus
48}
49#endif
50
51#endif /* #ifndef CGLTF_WRITE_H_INCLUDED__ */
52
53/*
54 *
55 * Stop now, if you are only interested in the API.
56 * Below, you find the implementation.
57 *
58 */
59
60#if defined(__INTELLISENSE__) || defined(__JETBRAINS_IDE__)
61/* This makes MSVC/CLion intellisense work. */
62#define CGLTF_IMPLEMENTATION
63#endif
64
65#ifdef CGLTF_WRITE_IMPLEMENTATION
66
67#include <stdio.h>
68#include <stdint.h>
69#include <stdlib.h>
70#include <string.h>
71
72#define CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM (1 << 0)
73#define CGLTF_EXTENSION_FLAG_MATERIALS_UNLIT (1 << 1)
74#define CGLTF_EXTENSION_FLAG_SPECULAR_GLOSSINESS (1 << 2)
75#define CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL (1 << 3)
76#define CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION (1 << 4)
77#define CGLTF_EXTENSION_FLAG_MATERIALS_CLEARCOAT (1 << 5)
78#define CGLTF_EXTENSION_FLAG_MATERIALS_IOR (1 << 6)
79#define CGLTF_EXTENSION_FLAG_MATERIALS_SPECULAR (1 << 7)
80#define CGLTF_EXTENSION_FLAG_MATERIALS_TRANSMISSION (1 << 8)
81
82typedef struct {
83 char* buffer;
84 cgltf_size buffer_size;
85 cgltf_size remaining;
86 char* cursor;
87 cgltf_size tmp;
88 cgltf_size chars_written;
89 const cgltf_data* data;
90 int depth;
91 const char* indent;
92 int needs_comma;
93 uint32_t extension_flags;
94 uint32_t required_extension_flags;
95} cgltf_write_context;
96
97#define CGLTF_MIN(a, b) (a < b ? a : b)
98
99#define CGLTF_SPRINTF(...) { \
100 context->tmp = snprintf ( context->cursor, context->remaining, __VA_ARGS__ ); \
101 context->chars_written += context->tmp; \
102 if (context->cursor) { \
103 context->cursor += context->tmp; \
104 context->remaining -= context->tmp; \
105 } }
106
107#define CGLTF_SNPRINTF(length, ...) { \
108 context->tmp = snprintf ( context->cursor, CGLTF_MIN(length + 1, context->remaining), __VA_ARGS__ ); \
109 context->chars_written += length; \
110 if (context->cursor) { \
111 context->cursor += length; \
112 context->remaining -= length; \
113 } }
114
115#define CGLTF_WRITE_IDXPROP(label, val, start) if (val) { \
116 cgltf_write_indent(context); \
117 CGLTF_SPRINTF("\"%s\": %d", label, (int) (val - start)); \
118 context->needs_comma = 1; }
119
120#define CGLTF_WRITE_IDXARRPROP(label, dim, vals, start) if (vals) { \
121 cgltf_write_indent(context); \
122 CGLTF_SPRINTF("\"%s\": [", label); \
123 for (int i = 0; i < (int)(dim); ++i) { \
124 int idx = (int) (vals[i] - start); \
125 if (i != 0) CGLTF_SPRINTF(","); \
126 CGLTF_SPRINTF(" %d", idx); \
127 } \
128 CGLTF_SPRINTF(" ]"); \
129 context->needs_comma = 1; }
130
131#define CGLTF_WRITE_TEXTURE_INFO(label, info) if (info.texture) { \
132 cgltf_write_line(context, "\"" label "\": {"); \
133 CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \
134 cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \
135 cgltf_write_floatprop(context, "scale", info.scale, 1.0f); \
136 if (info.has_transform) { \
137 context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \
138 cgltf_write_texture_transform(context, &info.transform); \
139 } \
140 cgltf_write_extras(context, &info.extras); \
141 cgltf_write_line(context, "}"); }
142
143static void cgltf_write_indent(cgltf_write_context* context)
144{
145 if (context->needs_comma)
146 {
147 CGLTF_SPRINTF(",\n");
148 context->needs_comma = 0;
149 }
150 else
151 {
152 CGLTF_SPRINTF("\n");
153 }
154 for (int i = 0; i < context->depth; ++i)
155 {
156 CGLTF_SPRINTF("%s", context->indent);
157 }
158}
159
160static void cgltf_write_line(cgltf_write_context* context, const char* line)
161{
162 if (line[0] == ']' || line[0] == '}')
163 {
164 --context->depth;
165 context->needs_comma = 0;
166 }
167 cgltf_write_indent(context);
168 CGLTF_SPRINTF("%s", line);
169 cgltf_size last = (cgltf_size)(strlen(line) - 1);
170 if (line[0] == ']' || line[0] == '}')
171 {
172 context->needs_comma = 1;
173 }
174 if (line[last] == '[' || line[last] == '{')
175 {
176 ++context->depth;
177 context->needs_comma = 0;
178 }
179}
180
181static void cgltf_write_strprop(cgltf_write_context* context, const char* label, const char* val)
182{
183 if (val)
184 {
185 cgltf_write_indent(context);
186 CGLTF_SPRINTF("\"%s\": \"%s\"", label, val);
187 context->needs_comma = 1;
188 }
189}
190
191static void cgltf_write_extras(cgltf_write_context* context, const cgltf_extras* extras)
192{
193 cgltf_size length = extras->end_offset - extras->start_offset;
194 if (length > 0 && context->data->file_data)
195 {
196 char* json_string = ((char*) context->data->file_data) + extras->start_offset;
197 cgltf_write_indent(context);
198 CGLTF_SPRINTF("%s", "\"extras\": ");
199 CGLTF_SNPRINTF(length, "%s", json_string);
200 context->needs_comma = 1;
201 }
202}
203
204static void cgltf_write_stritem(cgltf_write_context* context, const char* item)
205{
206 cgltf_write_indent(context);
207 CGLTF_SPRINTF("\"%s\"", item);
208 context->needs_comma = 1;
209}
210
211static void cgltf_write_intprop(cgltf_write_context* context, const char* label, int val, int def)
212{
213 if (val != def)
214 {
215 cgltf_write_indent(context);
216 CGLTF_SPRINTF("\"%s\": %d", label, val);
217 context->needs_comma = 1;
218 }
219}
220
221static void cgltf_write_floatprop(cgltf_write_context* context, const char* label, float val, float def)
222{
223 if (val != def)
224 {
225 cgltf_write_indent(context);
226 CGLTF_SPRINTF("\"%s\": ", label);
227 CGLTF_SPRINTF("%g", val);
228 context->needs_comma = 1;
229
230 if (context->cursor)
231 {
232 char *decimal_comma = strchr(context->cursor - context->tmp, ',');
233 if (decimal_comma)
234 {
235 *decimal_comma = '.';
236 }
237 }
238 }
239}
240
241static void cgltf_write_boolprop_optional(cgltf_write_context* context, const char* label, bool val, bool def)
242{
243 if (val != def)
244 {
245 cgltf_write_indent(context);
246 CGLTF_SPRINTF("\"%s\": %s", label, val ? "true" : "false");
247 context->needs_comma = 1;
248 }
249}
250
251static void cgltf_write_floatarrayprop(cgltf_write_context* context, const char* label, const cgltf_float* vals, cgltf_size dim)
252{
253 cgltf_write_indent(context);
254 CGLTF_SPRINTF("\"%s\": [", label);
255 for (cgltf_size i = 0; i < dim; ++i)
256 {
257 if (i != 0)
258 {
259 CGLTF_SPRINTF(", %g", vals[i]);
260 }
261 else
262 {
263 CGLTF_SPRINTF("%g", vals[i]);
264 }
265 }
266 CGLTF_SPRINTF("]");
267 context->needs_comma = 1;
268}
269
270static bool cgltf_check_floatarray(const float* vals, int dim, float val) {
271 while (dim--)
272 {
273 if (vals[dim] != val)
274 {
275 return true;
276 }
277 }
278 return false;
279}
280
281static int cgltf_int_from_component_type(cgltf_component_type ctype)
282{
283 switch (ctype)
284 {
285 case cgltf_component_type_r_8: return 5120;
286 case cgltf_component_type_r_8u: return 5121;
287 case cgltf_component_type_r_16: return 5122;
288 case cgltf_component_type_r_16u: return 5123;
289 case cgltf_component_type_r_32u: return 5125;
290 case cgltf_component_type_r_32f: return 5126;
291 default: return 0;
292 }
293}
294
295static const char* cgltf_str_from_alpha_mode(cgltf_alpha_mode alpha_mode)
296{
297 switch (alpha_mode)
298 {
299 case cgltf_alpha_mode_mask: return "MASK";
300 case cgltf_alpha_mode_blend: return "BLEND";
301 default: return NULL;
302 }
303}
304
305static const char* cgltf_str_from_type(cgltf_type type)
306{
307 switch (type)
308 {
309 case cgltf_type_scalar: return "SCALAR";
310 case cgltf_type_vec2: return "VEC2";
311 case cgltf_type_vec3: return "VEC3";
312 case cgltf_type_vec4: return "VEC4";
313 case cgltf_type_mat2: return "MAT2";
314 case cgltf_type_mat3: return "MAT3";
315 case cgltf_type_mat4: return "MAT4";
316 default: return NULL;
317 }
318}
319
320static cgltf_size cgltf_dim_from_type(cgltf_type type)
321{
322 switch (type)
323 {
324 case cgltf_type_scalar: return 1;
325 case cgltf_type_vec2: return 2;
326 case cgltf_type_vec3: return 3;
327 case cgltf_type_vec4: return 4;
328 case cgltf_type_mat2: return 4;
329 case cgltf_type_mat3: return 9;
330 case cgltf_type_mat4: return 16;
331 default: return 0;
332 }
333}
334
335static const char* cgltf_str_from_camera_type(cgltf_camera_type camera_type)
336{
337 switch (camera_type)
338 {
339 case cgltf_camera_type_perspective: return "perspective";
340 case cgltf_camera_type_orthographic: return "orthographic";
341 default: return NULL;
342 }
343}
344
345static const char* cgltf_str_from_light_type(cgltf_light_type light_type)
346{
347 switch (light_type)
348 {
349 case cgltf_light_type_directional: return "directional";
350 case cgltf_light_type_point: return "point";
351 case cgltf_light_type_spot: return "spot";
352 default: return NULL;
353 }
354}
355
356static void cgltf_write_texture_transform(cgltf_write_context* context, const cgltf_texture_transform* transform)
357{
358 cgltf_write_line(context, "\"extensions\": {");
359 cgltf_write_line(context, "\"KHR_texture_transform\": {");
360 if (cgltf_check_floatarray(transform->offset, 2, 0.0f))
361 {
362 cgltf_write_floatarrayprop(context, "offset", transform->offset, 2);
363 }
364 cgltf_write_floatprop(context, "rotation", transform->rotation, 0.0f);
365 if (cgltf_check_floatarray(transform->scale, 2, 1.0f))
366 {
367 cgltf_write_floatarrayprop(context, "scale", transform->scale, 2);
368 }
369 cgltf_write_intprop(context, "texCoord", transform->texcoord, 0);
370 cgltf_write_line(context, "}");
371 cgltf_write_line(context, "}");
372}
373
374static void cgltf_write_asset(cgltf_write_context* context, const cgltf_asset* asset)
375{
376 cgltf_write_line(context, "\"asset\": {");
377 cgltf_write_strprop(context, "copyright", asset->copyright);
378 cgltf_write_strprop(context, "generator", asset->generator);
379 cgltf_write_strprop(context, "version", asset->version);
380 cgltf_write_strprop(context, "min_version", asset->min_version);
381 cgltf_write_extras(context, &asset->extras);
382 cgltf_write_line(context, "}");
383}
384
385static void cgltf_write_primitive(cgltf_write_context* context, const cgltf_primitive* prim)
386{
387 cgltf_write_intprop(context, "mode", (int) prim->type, 4);
388 CGLTF_WRITE_IDXPROP("indices", prim->indices, context->data->accessors);
389 CGLTF_WRITE_IDXPROP("material", prim->material, context->data->materials);
390 cgltf_write_line(context, "\"attributes\": {");
391 for (cgltf_size i = 0; i < prim->attributes_count; ++i)
392 {
393 const cgltf_attribute* attr = prim->attributes + i;
394 CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors);
395 }
396 cgltf_write_line(context, "}");
397
398 if (prim->targets_count)
399 {
400 cgltf_write_line(context, "\"targets\": [");
401 for (cgltf_size i = 0; i < prim->targets_count; ++i)
402 {
403 cgltf_write_line(context, "{");
404 for (cgltf_size j = 0; j < prim->targets[i].attributes_count; ++j)
405 {
406 const cgltf_attribute* attr = prim->targets[i].attributes + j;
407 CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors);
408 }
409 cgltf_write_line(context, "}");
410 }
411 cgltf_write_line(context, "]");
412 }
413 cgltf_write_extras(context, &prim->extras);
414
415 cgltf_bool has_extensions = prim->has_draco_mesh_compression;
416 if (has_extensions) {
417 cgltf_write_line(context, "\"extensions\": {");
418
419 if (prim->has_draco_mesh_compression) {
420 context->extension_flags |= CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION;
421 if (prim->attributes_count == 0 || prim->indices == 0) {
422 context->required_extension_flags |= CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION;
423 }
424
425 cgltf_write_line(context, "\"KHR_draco_mesh_compression\": {");
426 CGLTF_WRITE_IDXPROP("bufferView", prim->draco_mesh_compression.buffer_view, context->data->buffer_views);
427 cgltf_write_line(context, "\"attributes\": {");
428 for (cgltf_size i = 0; i < prim->draco_mesh_compression.attributes_count; ++i)
429 {
430 const cgltf_attribute* attr = prim->draco_mesh_compression.attributes + i;
431 CGLTF_WRITE_IDXPROP(attr->name, attr->data, context->data->accessors);
432 }
433 cgltf_write_line(context, "}");
434 cgltf_write_line(context, "}");
435 }
436
437 cgltf_write_line(context, "}");
438 }
439}
440
441static void cgltf_write_mesh(cgltf_write_context* context, const cgltf_mesh* mesh)
442{
443 cgltf_write_line(context, "{");
444 cgltf_write_strprop(context, "name", mesh->name);
445
446 cgltf_write_line(context, "\"primitives\": [");
447 for (cgltf_size i = 0; i < mesh->primitives_count; ++i)
448 {
449 cgltf_write_line(context, "{");
450 cgltf_write_primitive(context, mesh->primitives + i);
451 cgltf_write_line(context, "}");
452 }
453 cgltf_write_line(context, "]");
454
455 if (mesh->weights_count > 0)
456 {
457 cgltf_write_floatarrayprop(context, "weights", mesh->weights, mesh->weights_count);
458 }
459 cgltf_write_extras(context, &mesh->extras);
460 cgltf_write_line(context, "}");
461}
462
463static void cgltf_write_buffer_view(cgltf_write_context* context, const cgltf_buffer_view* view)
464{
465 cgltf_write_line(context, "{");
466 CGLTF_WRITE_IDXPROP("buffer", view->buffer, context->data->buffers);
467 cgltf_write_intprop(context, "byteLength", (int)view->size, -1);
468 cgltf_write_intprop(context, "byteOffset", (int)view->offset, 0);
469 cgltf_write_intprop(context, "byteStride", (int)view->stride, 0);
470 // NOTE: We skip writing "target" because the spec says its usage can be inferred.
471 cgltf_write_extras(context, &view->extras);
472 cgltf_write_line(context, "}");
473}
474
475
476static void cgltf_write_buffer(cgltf_write_context* context, const cgltf_buffer* buffer)
477{
478 cgltf_write_line(context, "{");
479 cgltf_write_strprop(context, "uri", buffer->uri);
480 cgltf_write_intprop(context, "byteLength", (int)buffer->size, -1);
481 cgltf_write_extras(context, &buffer->extras);
482 cgltf_write_line(context, "}");
483}
484
485static void cgltf_write_material(cgltf_write_context* context, const cgltf_material* material)
486{
487 cgltf_write_line(context, "{");
488 cgltf_write_strprop(context, "name", material->name);
489 cgltf_write_floatprop(context, "alphaCutoff", material->alpha_cutoff, 0.5f);
490 cgltf_write_boolprop_optional(context, "doubleSided", material->double_sided, false);
491 // cgltf_write_boolprop_optional(context, "unlit", material->unlit, false);
492
493 if (material->unlit)
494 {
495 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_UNLIT;
496 }
497
498 if (material->has_pbr_specular_glossiness)
499 {
500 context->extension_flags |= CGLTF_EXTENSION_FLAG_SPECULAR_GLOSSINESS;
501 }
502
503 if (material->has_clearcoat)
504 {
505 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_CLEARCOAT;
506 }
507
508 if (material->has_transmission)
509 {
510 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_TRANSMISSION;
511 }
512
513 if (material->has_ior)
514 {
515 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_IOR;
516 }
517
518 if (material->has_specular)
519 {
520 context->extension_flags |= CGLTF_EXTENSION_FLAG_MATERIALS_SPECULAR;
521 }
522
523 if (material->has_pbr_metallic_roughness)
524 {
525 const cgltf_pbr_metallic_roughness* params = &material->pbr_metallic_roughness;
526 cgltf_write_line(context, "\"pbrMetallicRoughness\": {");
527 CGLTF_WRITE_TEXTURE_INFO("baseColorTexture", params->base_color_texture);
528 CGLTF_WRITE_TEXTURE_INFO("metallicRoughnessTexture", params->metallic_roughness_texture);
529 cgltf_write_floatprop(context, "metallicFactor", params->metallic_factor, 1.0f);
530 cgltf_write_floatprop(context, "roughnessFactor", params->roughness_factor, 1.0f);
531 if (cgltf_check_floatarray(params->base_color_factor, 4, 1.0f))
532 {
533 cgltf_write_floatarrayprop(context, "baseColorFactor", params->base_color_factor, 4);
534 }
535 cgltf_write_extras(context, &params->extras);
536 cgltf_write_line(context, "}");
537 }
538
539 if (material->unlit || material->has_pbr_specular_glossiness || material->has_clearcoat || material->has_ior || material->has_specular || material->has_transmission)
540 {
541 cgltf_write_line(context, "\"extensions\": {");
542 if (material->has_clearcoat)
543 {
544 const cgltf_clearcoat* params = &material->clearcoat;
545 cgltf_write_line(context, "\"KHR_materials_clearcoat\": {");
546 CGLTF_WRITE_TEXTURE_INFO("clearcoatTexture", params->clearcoat_texture);
547 CGLTF_WRITE_TEXTURE_INFO("clearcoatRoughnessTexture", params->clearcoat_roughness_texture);
548 CGLTF_WRITE_TEXTURE_INFO("clearcoatNormalTexture", params->clearcoat_normal_texture);
549 cgltf_write_floatprop(context, "clearcoatFactor", params->clearcoat_factor, 0.0f);
550 cgltf_write_floatprop(context, "clearcoatRoughnessFactor", params->clearcoat_roughness_factor, 0.0f);
551 cgltf_write_line(context, "}");
552 }
553 if (material->has_ior)
554 {
555 const cgltf_ior* params = &material->ior;
556 cgltf_write_line(context, "\"KHR_materials_ior\": {");
557 cgltf_write_floatprop(context, "ior", params->ior, 1.5f);
558 cgltf_write_line(context, "}");
559 }
560 if (material->has_specular)
561 {
562 const cgltf_specular* params = &material->specular;
563 cgltf_write_line(context, "\"KHR_materials_specular\": {");
564 CGLTF_WRITE_TEXTURE_INFO("specularTexture", params->specular_texture);
565 cgltf_write_floatprop(context, "specularFactor", params->specular_factor, 1.0f);
566 if (cgltf_check_floatarray(params->specular_color_factor, 3, 1.0f))
567 {
568 cgltf_write_floatarrayprop(context, "specularColorFactor", params->specular_color_factor, 3);
569 }
570 cgltf_write_line(context, "}");
571 }
572 if (material->has_transmission)
573 {
574 const cgltf_transmission* params = &material->transmission;
575 cgltf_write_line(context, "\"KHR_materials_transmission\": {");
576 CGLTF_WRITE_TEXTURE_INFO("transmissionTexture", params->transmission_texture);
577 cgltf_write_floatprop(context, "transmissionFactor", params->transmission_factor, 0.0f);
578 cgltf_write_line(context, "}");
579 }
580 if (material->has_pbr_specular_glossiness)
581 {
582 const cgltf_pbr_specular_glossiness* params = &material->pbr_specular_glossiness;
583 cgltf_write_line(context, "\"KHR_materials_pbrSpecularGlossiness\": {");
584 CGLTF_WRITE_TEXTURE_INFO("diffuseTexture", params->diffuse_texture);
585 CGLTF_WRITE_TEXTURE_INFO("specularGlossinessTexture", params->specular_glossiness_texture);
586 if (cgltf_check_floatarray(params->diffuse_factor, 4, 1.0f))
587 {
588 cgltf_write_floatarrayprop(context, "dffuseFactor", params->diffuse_factor, 4);
589 }
590 if (cgltf_check_floatarray(params->specular_factor, 3, 1.0f))
591 {
592 cgltf_write_floatarrayprop(context, "specularFactor", params->specular_factor, 3);
593 }
594 cgltf_write_floatprop(context, "glossinessFactor", params->glossiness_factor, 1.0f);
595 cgltf_write_line(context, "}");
596 }
597 if (material->unlit)
598 {
599 cgltf_write_line(context, "\"KHR_materials_unlit\": {}");
600 }
601 cgltf_write_line(context, "}");
602 }
603
604 CGLTF_WRITE_TEXTURE_INFO("normalTexture", material->normal_texture);
605 CGLTF_WRITE_TEXTURE_INFO("occlusionTexture", material->occlusion_texture);
606 CGLTF_WRITE_TEXTURE_INFO("emissiveTexture", material->emissive_texture);
607 if (cgltf_check_floatarray(material->emissive_factor, 3, 0.0f))
608 {
609 cgltf_write_floatarrayprop(context, "emissiveFactor", material->emissive_factor, 3);
610 }
611 cgltf_write_strprop(context, "alphaMode", cgltf_str_from_alpha_mode(material->alpha_mode));
612 cgltf_write_extras(context, &material->extras);
613 cgltf_write_line(context, "}");
614}
615
616static void cgltf_write_image(cgltf_write_context* context, const cgltf_image* image)
617{
618 cgltf_write_line(context, "{");
619 cgltf_write_strprop(context, "name", image->name);
620 cgltf_write_strprop(context, "uri", image->uri);
621 CGLTF_WRITE_IDXPROP("bufferView", image->buffer_view, context->data->buffer_views);
622 cgltf_write_strprop(context, "mimeType", image->mime_type);
623 cgltf_write_extras(context, &image->extras);
624 cgltf_write_line(context, "}");
625}
626
627static void cgltf_write_texture(cgltf_write_context* context, const cgltf_texture* texture)
628{
629 cgltf_write_line(context, "{");
630 cgltf_write_strprop(context, "name", texture->name);
631 CGLTF_WRITE_IDXPROP("source", texture->image, context->data->images);
632 CGLTF_WRITE_IDXPROP("sampler", texture->sampler, context->data->samplers);
633 cgltf_write_extras(context, &texture->extras);
634 cgltf_write_line(context, "}");
635}
636
637static void cgltf_write_skin(cgltf_write_context* context, const cgltf_skin* skin)
638{
639 cgltf_write_line(context, "{");
640 CGLTF_WRITE_IDXPROP("skeleton", skin->skeleton, context->data->nodes);
641 CGLTF_WRITE_IDXPROP("inverseBindMatrices", skin->inverse_bind_matrices, context->data->accessors);
642 CGLTF_WRITE_IDXARRPROP("joints", skin->joints_count, skin->joints, context->data->nodes);
643 cgltf_write_strprop(context, "name", skin->name);
644 cgltf_write_extras(context, &skin->extras);
645 cgltf_write_line(context, "}");
646}
647
648static const char* cgltf_write_str_path_type(cgltf_animation_path_type path_type)
649{
650 switch (path_type)
651 {
652 case cgltf_animation_path_type_translation:
653 return "translation";
654 case cgltf_animation_path_type_rotation:
655 return "rotation";
656 case cgltf_animation_path_type_scale:
657 return "scale";
658 case cgltf_animation_path_type_weights:
659 return "weights";
660 case cgltf_animation_path_type_invalid:
661 break;
662 }
663 return "invalid";
664}
665
666static const char* cgltf_write_str_interpolation_type(cgltf_interpolation_type interpolation_type)
667{
668 switch (interpolation_type)
669 {
670 case cgltf_interpolation_type_linear:
671 return "LINEAR";
672 case cgltf_interpolation_type_step:
673 return "STEP";
674 case cgltf_interpolation_type_cubic_spline:
675 return "CUBICSPLINE";
676 }
677 return "invalid";
678}
679
680static void cgltf_write_path_type(cgltf_write_context* context, const char *label, cgltf_animation_path_type path_type)
681{
682 cgltf_write_strprop(context, label, cgltf_write_str_path_type(path_type));
683}
684
685static void cgltf_write_interpolation_type(cgltf_write_context* context, const char *label, cgltf_interpolation_type interpolation_type)
686{
687 cgltf_write_strprop(context, label, cgltf_write_str_interpolation_type(interpolation_type));
688}
689
690static void cgltf_write_animation_sampler(cgltf_write_context* context, const cgltf_animation_sampler* animation_sampler)
691{
692 cgltf_write_line(context, "{");
693 cgltf_write_interpolation_type(context, "interpolation", animation_sampler->interpolation);
694 CGLTF_WRITE_IDXPROP("input", animation_sampler->input, context->data->accessors);
695 CGLTF_WRITE_IDXPROP("output", animation_sampler->output, context->data->accessors);
696 cgltf_write_extras(context, &animation_sampler->extras);
697 cgltf_write_line(context, "}");
698}
699
700static void cgltf_write_animation_channel(cgltf_write_context* context, const cgltf_animation* animation, const cgltf_animation_channel* animation_channel)
701{
702 cgltf_write_line(context, "{");
703 CGLTF_WRITE_IDXPROP("sampler", animation_channel->sampler, animation->samplers);
704 cgltf_write_line(context, "\"target\": {");
705 CGLTF_WRITE_IDXPROP("node", animation_channel->target_node, context->data->nodes);
706 cgltf_write_path_type(context, "path", animation_channel->target_path);
707 cgltf_write_line(context, "}");
708 cgltf_write_extras(context, &animation_channel->extras);
709 cgltf_write_line(context, "}");
710}
711
712static void cgltf_write_animation(cgltf_write_context* context, const cgltf_animation* animation)
713{
714 cgltf_write_line(context, "{");
715 cgltf_write_strprop(context, "name", animation->name);
716
717 if (animation->samplers_count > 0)
718 {
719 cgltf_write_line(context, "\"samplers\": [");
720 for (cgltf_size i = 0; i < animation->samplers_count; ++i)
721 {
722 cgltf_write_animation_sampler(context, animation->samplers + i);
723 }
724 cgltf_write_line(context, "]");
725 }
726 if (animation->channels_count > 0)
727 {
728 cgltf_write_line(context, "\"channels\": [");
729 for (cgltf_size i = 0; i < animation->channels_count; ++i)
730 {
731 cgltf_write_animation_channel(context, animation, animation->channels + i);
732 }
733 cgltf_write_line(context, "]");
734 }
735 cgltf_write_extras(context, &animation->extras);
736 cgltf_write_line(context, "}");
737}
738
739static void cgltf_write_sampler(cgltf_write_context* context, const cgltf_sampler* sampler)
740{
741 cgltf_write_line(context, "{");
742 cgltf_write_intprop(context, "magFilter", sampler->mag_filter, 0);
743 cgltf_write_intprop(context, "minFilter", sampler->min_filter, 0);
744 cgltf_write_intprop(context, "wrapS", sampler->wrap_s, 10497);
745 cgltf_write_intprop(context, "wrapT", sampler->wrap_t, 10497);
746 cgltf_write_extras(context, &sampler->extras);
747 cgltf_write_line(context, "}");
748}
749
750static void cgltf_write_node(cgltf_write_context* context, const cgltf_node* node)
751{
752 cgltf_write_line(context, "{");
753 CGLTF_WRITE_IDXARRPROP("children", node->children_count, node->children, context->data->nodes);
754 CGLTF_WRITE_IDXPROP("mesh", node->mesh, context->data->meshes);
755 cgltf_write_strprop(context, "name", node->name);
756 if (node->has_matrix)
757 {
758 cgltf_write_floatarrayprop(context, "matrix", node->matrix, 16);
759 }
760 if (node->has_translation)
761 {
762 cgltf_write_floatarrayprop(context, "translation", node->translation, 3);
763 }
764 if (node->has_rotation)
765 {
766 cgltf_write_floatarrayprop(context, "rotation", node->rotation, 4);
767 }
768 if (node->has_scale)
769 {
770 cgltf_write_floatarrayprop(context, "scale", node->scale, 3);
771 }
772 if (node->skin)
773 {
774 CGLTF_WRITE_IDXPROP("skin", node->skin, context->data->skins);
775 }
776
777 if (node->light)
778 {
779 context->extension_flags |= CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL;
780 cgltf_write_line(context, "\"extensions\": {");
781 cgltf_write_line(context, "\"KHR_lights_punctual\": {");
782 CGLTF_WRITE_IDXPROP("light", node->light, context->data->lights);
783 cgltf_write_line(context, "}");
784 cgltf_write_line(context, "}");
785 }
786
787 if (node->weights_count > 0)
788 {
789 cgltf_write_floatarrayprop(context, "weights", node->weights, node->weights_count);
790 }
791
792 if (node->camera)
793 {
794 CGLTF_WRITE_IDXPROP("camera", node->camera, context->data->cameras);
795 }
796
797 cgltf_write_extras(context, &node->extras);
798 cgltf_write_line(context, "}");
799}
800
801static void cgltf_write_scene(cgltf_write_context* context, const cgltf_scene* scene)
802{
803 cgltf_write_line(context, "{");
804 cgltf_write_strprop(context, "name", scene->name);
805 CGLTF_WRITE_IDXARRPROP("nodes", scene->nodes_count, scene->nodes, context->data->nodes);
806 cgltf_write_extras(context, &scene->extras);
807 cgltf_write_line(context, "}");
808}
809
810static void cgltf_write_accessor(cgltf_write_context* context, const cgltf_accessor* accessor)
811{
812 cgltf_write_line(context, "{");
813 CGLTF_WRITE_IDXPROP("bufferView", accessor->buffer_view, context->data->buffer_views);
814 cgltf_write_intprop(context, "componentType", cgltf_int_from_component_type(accessor->component_type), 0);
815 cgltf_write_strprop(context, "type", cgltf_str_from_type(accessor->type));
816 cgltf_size dim = cgltf_dim_from_type(accessor->type);
817 cgltf_write_boolprop_optional(context, "normalized", accessor->normalized, false);
818 cgltf_write_intprop(context, "byteOffset", (int)accessor->offset, 0);
819 cgltf_write_intprop(context, "count", (int)accessor->count, -1);
820 if (accessor->has_min)
821 {
822 cgltf_write_floatarrayprop(context, "min", accessor->min, dim);
823 }
824 if (accessor->has_max)
825 {
826 cgltf_write_floatarrayprop(context, "max", accessor->max, dim);
827 }
828 if (accessor->is_sparse)
829 {
830 cgltf_write_line(context, "\"sparse\": {");
831 cgltf_write_intprop(context, "count", (int)accessor->sparse.count, 0);
832 cgltf_write_line(context, "\"indices\": {");
833 cgltf_write_intprop(context, "byteOffset", (int)accessor->sparse.indices_byte_offset, 0);
834 CGLTF_WRITE_IDXPROP("bufferView", accessor->sparse.indices_buffer_view, context->data->buffer_views);
835 cgltf_write_intprop(context, "componentType", cgltf_int_from_component_type(accessor->sparse.indices_component_type), 0);
836 cgltf_write_extras(context, &accessor->sparse.indices_extras);
837 cgltf_write_line(context, "}");
838 cgltf_write_line(context, "\"values\": {");
839 cgltf_write_intprop(context, "byteOffset", (int)accessor->sparse.values_byte_offset, 0);
840 CGLTF_WRITE_IDXPROP("bufferView", accessor->sparse.values_buffer_view, context->data->buffer_views);
841 cgltf_write_extras(context, &accessor->sparse.values_extras);
842 cgltf_write_line(context, "}");
843 cgltf_write_extras(context, &accessor->sparse.extras);
844 cgltf_write_line(context, "}");
845 }
846 cgltf_write_extras(context, &accessor->extras);
847 cgltf_write_line(context, "}");
848}
849
850static void cgltf_write_camera(cgltf_write_context* context, const cgltf_camera* camera)
851{
852 cgltf_write_line(context, "{");
853 cgltf_write_strprop(context, "type", cgltf_str_from_camera_type(camera->type));
854 if (camera->name)
855 {
856 cgltf_write_strprop(context, "name", camera->name);
857 }
858
859 if (camera->type == cgltf_camera_type_orthographic)
860 {
861 cgltf_write_line(context, "\"orthographic\": {");
862 cgltf_write_floatprop(context, "xmag", camera->data.orthographic.xmag, -1.0f);
863 cgltf_write_floatprop(context, "ymag", camera->data.orthographic.ymag, -1.0f);
864 cgltf_write_floatprop(context, "zfar", camera->data.orthographic.zfar, -1.0f);
865 cgltf_write_floatprop(context, "znear", camera->data.orthographic.znear, -1.0f);
866 cgltf_write_extras(context, &camera->data.orthographic.extras);
867 cgltf_write_line(context, "}");
868 }
869 else if (camera->type == cgltf_camera_type_perspective)
870 {
871 cgltf_write_line(context, "\"perspective\": {");
872 cgltf_write_floatprop(context, "aspectRatio", camera->data.perspective.aspect_ratio, -1.0f);
873 cgltf_write_floatprop(context, "yfov", camera->data.perspective.yfov, -1.0f);
874 cgltf_write_floatprop(context, "zfar", camera->data.perspective.zfar, -1.0f);
875 cgltf_write_floatprop(context, "znear", camera->data.perspective.znear, -1.0f);
876 cgltf_write_extras(context, &camera->data.perspective.extras);
877 cgltf_write_line(context, "}");
878 }
879 cgltf_write_extras(context, &camera->extras);
880 cgltf_write_line(context, "}");
881}
882
883static void cgltf_write_light(cgltf_write_context* context, const cgltf_light* light)
884{
885 cgltf_write_line(context, "{");
886 cgltf_write_strprop(context, "type", cgltf_str_from_light_type(light->type));
887 if (light->name)
888 {
889 cgltf_write_strprop(context, "name", light->name);
890 }
891 if (cgltf_check_floatarray(light->color, 3, 1.0f))
892 {
893 cgltf_write_floatarrayprop(context, "color", light->color, 3);
894 }
895 cgltf_write_floatprop(context, "intensity", light->intensity, 1.0f);
896 cgltf_write_floatprop(context, "range", light->range, 0.0f);
897
898 if (light->type == cgltf_light_type_spot)
899 {
900 cgltf_write_line(context, "\"spot\": {");
901 cgltf_write_floatprop(context, "innerConeAngle", light->spot_inner_cone_angle, 0.0f);
902 cgltf_write_floatprop(context, "outerConeAngle", light->spot_outer_cone_angle, 3.14159265358979323846f/4.0f);
903 cgltf_write_line(context, "}");
904 }
905 cgltf_write_line(context, "}");
906}
907
908cgltf_result cgltf_write_file(const cgltf_options* options, const char* path, const cgltf_data* data)
909{
910 cgltf_size expected = cgltf_write(options, NULL, 0, data);
911 char* buffer = (char*) malloc(expected);
912 cgltf_size actual = cgltf_write(options, buffer, expected, data);
913 if (expected != actual) {
914 fprintf(stderr, "Error: expected %zu bytes but wrote %zu bytes.\n", expected, actual);
915 }
916 FILE* file = fopen(path, "wt");
917 if (!file)
918 {
919 return cgltf_result_file_not_found;
920 }
921 // Note that cgltf_write() includes a null terminator, which we omit from the file content.
922 fwrite(buffer, actual - 1, 1, file);
923 fclose(file);
924 free(buffer);
925 return cgltf_result_success;
926}
927
928static void cgltf_write_extensions(cgltf_write_context* context, uint32_t extension_flags)
929{
930 if (extension_flags & CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM) {
931 cgltf_write_stritem(context, "KHR_texture_transform");
932 }
933 if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_UNLIT) {
934 cgltf_write_stritem(context, "KHR_materials_unlit");
935 }
936 if (extension_flags & CGLTF_EXTENSION_FLAG_SPECULAR_GLOSSINESS) {
937 cgltf_write_stritem(context, "KHR_materials_pbrSpecularGlossiness");
938 }
939 if (extension_flags & CGLTF_EXTENSION_FLAG_LIGHTS_PUNCTUAL) {
940 cgltf_write_stritem(context, "KHR_lights_punctual");
941 }
942 if (extension_flags & CGLTF_EXTENSION_FLAG_DRACO_MESH_COMPRESSION) {
943 cgltf_write_stritem(context, "KHR_draco_mesh_compression");
944 }
945 if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_CLEARCOAT) {
946 cgltf_write_stritem(context, "KHR_materials_clearcoat");
947 }
948 if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_IOR) {
949 cgltf_write_stritem(context, "KHR_materials_ior");
950 }
951 if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_SPECULAR) {
952 cgltf_write_stritem(context, "KHR_materials_specular");
953 }
954 if (extension_flags & CGLTF_EXTENSION_FLAG_MATERIALS_TRANSMISSION) {
955 cgltf_write_stritem(context, "KHR_materials_transmission");
956 }
957}
958
959cgltf_size cgltf_write(const cgltf_options* options, char* buffer, cgltf_size size, const cgltf_data* data)
960{
961 (void)options;
962 cgltf_write_context ctx;
963 ctx.buffer = buffer;
964 ctx.buffer_size = size;
965 ctx.remaining = size;
966 ctx.cursor = buffer;
967 ctx.chars_written = 0;
968 ctx.data = data;
969 ctx.depth = 1;
970 ctx.indent = " ";
971 ctx.needs_comma = 0;
972 ctx.extension_flags = 0;
973 ctx.required_extension_flags = 0;
974
975 cgltf_write_context* context = &ctx;
976
977 CGLTF_SPRINTF("{");
978
979 if (data->accessors_count > 0)
980 {
981 cgltf_write_line(context, "\"accessors\": [");
982 for (cgltf_size i = 0; i < data->accessors_count; ++i)
983 {
984 cgltf_write_accessor(context, data->accessors + i);
985 }
986 cgltf_write_line(context, "]");
987 }
988
989 cgltf_write_asset(context, &data->asset);
990
991 if (data->buffer_views_count > 0)
992 {
993 cgltf_write_line(context, "\"bufferViews\": [");
994 for (cgltf_size i = 0; i < data->buffer_views_count; ++i)
995 {
996 cgltf_write_buffer_view(context, data->buffer_views + i);
997 }
998 cgltf_write_line(context, "]");
999 }
1000
1001 if (data->buffers_count > 0)
1002 {
1003 cgltf_write_line(context, "\"buffers\": [");
1004 for (cgltf_size i = 0; i < data->buffers_count; ++i)
1005 {
1006 cgltf_write_buffer(context, data->buffers + i);
1007 }
1008 cgltf_write_line(context, "]");
1009 }
1010
1011 if (data->images_count > 0)
1012 {
1013 cgltf_write_line(context, "\"images\": [");
1014 for (cgltf_size i = 0; i < data->images_count; ++i)
1015 {
1016 cgltf_write_image(context, data->images + i);
1017 }
1018 cgltf_write_line(context, "]");
1019 }
1020
1021 if (data->meshes_count > 0)
1022 {
1023 cgltf_write_line(context, "\"meshes\": [");
1024 for (cgltf_size i = 0; i < data->meshes_count; ++i)
1025 {
1026 cgltf_write_mesh(context, data->meshes + i);
1027 }
1028 cgltf_write_line(context, "]");
1029 }
1030
1031 if (data->materials_count > 0)
1032 {
1033 cgltf_write_line(context, "\"materials\": [");
1034 for (cgltf_size i = 0; i < data->materials_count; ++i)
1035 {
1036 cgltf_write_material(context, data->materials + i);
1037 }
1038 cgltf_write_line(context, "]");
1039 }
1040
1041 if (data->nodes_count > 0)
1042 {
1043 cgltf_write_line(context, "\"nodes\": [");
1044 for (cgltf_size i = 0; i < data->nodes_count; ++i)
1045 {
1046 cgltf_write_node(context, data->nodes + i);
1047 }
1048 cgltf_write_line(context, "]");
1049 }
1050
1051 if (data->samplers_count > 0)
1052 {
1053 cgltf_write_line(context, "\"samplers\": [");
1054 for (cgltf_size i = 0; i < data->samplers_count; ++i)
1055 {
1056 cgltf_write_sampler(context, data->samplers + i);
1057 }
1058 cgltf_write_line(context, "]");
1059 }
1060
1061 CGLTF_WRITE_IDXPROP("scene", data->scene, data->scenes);
1062
1063 if (data->scenes_count > 0)
1064 {
1065 cgltf_write_line(context, "\"scenes\": [");
1066 for (cgltf_size i = 0; i < data->scenes_count; ++i)
1067 {
1068 cgltf_write_scene(context, data->scenes + i);
1069 }
1070 cgltf_write_line(context, "]");
1071 }
1072
1073 if (data->textures_count > 0)
1074 {
1075 cgltf_write_line(context, "\"textures\": [");
1076 for (cgltf_size i = 0; i < data->textures_count; ++i)
1077 {
1078 cgltf_write_texture(context, data->textures + i);
1079 }
1080 cgltf_write_line(context, "]");
1081 }
1082
1083 if (data->skins_count > 0)
1084 {
1085 cgltf_write_line(context, "\"skins\": [");
1086 for (cgltf_size i = 0; i < data->skins_count; ++i)
1087 {
1088 cgltf_write_skin(context, data->skins + i);
1089 }
1090 cgltf_write_line(context, "]");
1091 }
1092
1093 if (data->animations_count > 0)
1094 {
1095 cgltf_write_line(context, "\"animations\": [");
1096 for (cgltf_size i = 0; i < data->animations_count; ++i)
1097 {
1098 cgltf_write_animation(context, data->animations + i);
1099 }
1100 cgltf_write_line(context, "]");
1101 }
1102
1103 if (data->cameras_count > 0)
1104 {
1105 cgltf_write_line(context, "\"cameras\": [");
1106 for (cgltf_size i = 0; i < data->cameras_count; ++i)
1107 {
1108 cgltf_write_camera(context, data->cameras + i);
1109 }
1110 cgltf_write_line(context, "]");
1111 }
1112
1113 if (data->lights_count > 0)
1114 {
1115 cgltf_write_line(context, "\"extensions\": {");
1116
1117 cgltf_write_line(context, "\"KHR_lights_punctual\": {");
1118 cgltf_write_line(context, "\"lights\": [");
1119 for (cgltf_size i = 0; i < data->lights_count; ++i)
1120 {
1121 cgltf_write_light(context, data->lights + i);
1122 }
1123 cgltf_write_line(context, "]");
1124 cgltf_write_line(context, "}");
1125
1126 cgltf_write_line(context, "}");
1127 }
1128
1129 if (context->extension_flags != 0) {
1130 cgltf_write_line(context, "\"extensionsUsed\": [");
1131 cgltf_write_extensions(context, context->extension_flags);
1132 cgltf_write_line(context, "]");
1133 }
1134
1135 if (context->required_extension_flags != 0) {
1136 cgltf_write_line(context, "\"extensionsRequired\": [");
1137 cgltf_write_extensions(context, context->required_extension_flags);
1138 cgltf_write_line(context, "]");
1139 }
1140
1141 cgltf_write_extras(context, &data->extras);
1142
1143 CGLTF_SPRINTF("\n}\n");
1144
1145 // snprintf does not include the null terminator in its return value, so be sure to include it
1146 // in the returned byte count.
1147 return 1 + ctx.chars_written;
1148}
1149
1150#endif /* #ifdef CGLTF_WRITE_IMPLEMENTATION */
1151
1152/* cgltf is distributed under MIT license:
1153 *
1154 * Copyright (c) 2019 Philip Rideout
1155
1156 * Permission is hereby granted, free of charge, to any person obtaining a copy
1157 * of this software and associated documentation files (the "Software"), to deal
1158 * in the Software without restriction, including without limitation the rights
1159 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1160 * copies of the Software, and to permit persons to whom the Software is
1161 * furnished to do so, subject to the following conditions:
1162
1163 * The above copyright notice and this permission notice shall be included in all
1164 * copies or substantial portions of the Software.
1165
1166 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1167 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1168 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1169 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1170 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1171 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1172 * SOFTWARE.
1173 */