Browse Source

Updated cgltf.

Бранимир Караџић 3 years ago
parent
commit
c25aab33eb
2 changed files with 262 additions and 98 deletions
  1. 158 79
      3rdparty/cgltf/cgltf.h
  2. 104 19
      3rdparty/cgltf/cgltf_write.h

+ 158 - 79
3rdparty/cgltf/cgltf.h

@@ -1,7 +1,7 @@
 /**
  * cgltf - a single-file glTF 2.0 parser written in C99.
  *
- * Version: 1.12
+ * Version: 1.13
  *
  * Website: https://github.com/jkuhlmann/cgltf
  *
@@ -80,13 +80,9 @@
  * `cgltf_accessor_read_index` is similar to its floating-point counterpart, but it returns size_t
  * and only works with single-component data types.
  *
- * `cgltf_result cgltf_copy_extras_json(const cgltf_data*, const cgltf_extras*,
- * char* dest, cgltf_size* dest_size)` allows users to retrieve the "extras" data that
- * can be attached to many glTF objects (which can be arbitrary JSON data). The
- * `cgltf_extras` struct stores the offsets of the start and end of the extras JSON data
- * as it appears in the complete glTF JSON data. This function copies the extras data
- * into the provided buffer. If `dest` is NULL, the length of the data is written into
- * `dest_size`. You can then parse this data using your own JSON parser
+ * `cgltf_copy_extras_json` allows users to retrieve the "extras" data that can be attached to many
+ * glTF objects (which can be arbitrary JSON data). This is a legacy function, consider using
+ * cgltf_extras::data directly instead. You can parse this data using your own JSON parser
  * or, if you've included the cgltf implementation using the integrated JSMN JSON parser.
  */
 #ifndef CGLTF_H_INCLUDED__
@@ -256,8 +252,10 @@ typedef enum cgltf_data_free_method {
 } cgltf_data_free_method;
 
 typedef struct cgltf_extras {
-	cgltf_size start_offset;
-	cgltf_size end_offset;
+	cgltf_size start_offset; /* this field is deprecated and will be removed in the future; use data instead */
+	cgltf_size end_offset; /* this field is deprecated and will be removed in the future; use data instead */
+
+	char* data;
 } cgltf_extras;
 
 typedef struct cgltf_extension {
@@ -432,8 +430,6 @@ typedef struct cgltf_pbr_metallic_roughness
 	cgltf_float base_color_factor[4];
 	cgltf_float metallic_factor;
 	cgltf_float roughness_factor;
-
-	cgltf_extras extras;
 } cgltf_pbr_metallic_roughness;
 
 typedef struct cgltf_pbr_specular_glossiness
@@ -841,6 +837,7 @@ cgltf_size cgltf_num_components(cgltf_type type);
 
 cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count);
 
+/* this function is deprecated and will be removed in the future; use cgltf_extras::data instead */
 cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size);
 
 #ifdef __cplusplus
@@ -924,12 +921,15 @@ static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t
  */
 
 
+#ifndef CGLTF_CONSTS
 static const cgltf_size GlbHeaderSize = 12;
 static const cgltf_size GlbChunkHeaderSize = 8;
 static const uint32_t GlbVersion = 2;
 static const uint32_t GlbMagic = 0x46546C67;
 static const uint32_t GlbMagicJsonChunk = 0x4E4F534A;
 static const uint32_t GlbMagicBinChunk = 0x004E4942;
+#define CGLTF_CONSTS
+#endif
 
 #ifndef CGLTF_MALLOC
 #define CGLTF_MALLOC(size) malloc(size)
@@ -1745,7 +1745,12 @@ cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras*
 	return cgltf_result_success;
 }
 
-void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_size extensions_count)
+static void cgltf_free_extras(cgltf_data* data, cgltf_extras* extras)
+{
+	data->memory.free_func(data->memory.user_data, extras->data);
+}
+
+static void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_size extensions_count)
 {
 	for (cgltf_size i = 0; i < extensions_count; ++i)
 	{
@@ -1755,6 +1760,12 @@ void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_
 	data->memory.free_func(data->memory.user_data, extensions);
 }
 
+static void cgltf_free_texture_view(cgltf_data* data, cgltf_texture_view* view)
+{
+	cgltf_free_extensions(data, view->extensions, view->extensions_count);
+	cgltf_free_extras(data, &view->extras);
+}
+
 void cgltf_free(cgltf_data* data)
 {
 	if (!data)
@@ -1770,6 +1781,7 @@ void cgltf_free(cgltf_data* data)
 	data->memory.free_func(data->memory.user_data, data->asset.min_version);
 
 	cgltf_free_extensions(data, data->asset.extensions, data->asset.extensions_count);
+	cgltf_free_extras(data, &data->asset.extras);
 
 	for (cgltf_size i = 0; i < data->accessors_count; ++i)
 	{
@@ -1780,8 +1792,12 @@ void cgltf_free(cgltf_data* data)
 			cgltf_free_extensions(data, data->accessors[i].sparse.extensions, data->accessors[i].sparse.extensions_count);
 			cgltf_free_extensions(data, data->accessors[i].sparse.indices_extensions, data->accessors[i].sparse.indices_extensions_count);
 			cgltf_free_extensions(data, data->accessors[i].sparse.values_extensions, data->accessors[i].sparse.values_extensions_count);
+			cgltf_free_extras(data, &data->accessors[i].sparse.extras);
+			cgltf_free_extras(data, &data->accessors[i].sparse.indices_extras);
+			cgltf_free_extras(data, &data->accessors[i].sparse.values_extras);
 		}
 		cgltf_free_extensions(data, data->accessors[i].extensions, data->accessors[i].extensions_count);
+		cgltf_free_extras(data, &data->accessors[i].extras);
 	}
 	data->memory.free_func(data->memory.user_data, data->accessors);
 
@@ -1791,6 +1807,7 @@ void cgltf_free(cgltf_data* data)
 		data->memory.free_func(data->memory.user_data, data->buffer_views[i].data);
 
 		cgltf_free_extensions(data, data->buffer_views[i].extensions, data->buffer_views[i].extensions_count);
+		cgltf_free_extras(data, &data->buffer_views[i].extras);
 	}
 	data->memory.free_func(data->memory.user_data, data->buffer_views);
 
@@ -1810,8 +1827,8 @@ void cgltf_free(cgltf_data* data)
 		data->memory.free_func(data->memory.user_data, data->buffers[i].uri);
 
 		cgltf_free_extensions(data, data->buffers[i].extensions, data->buffers[i].extensions_count);
+		cgltf_free_extras(data, &data->buffers[i].extras);
 	}
-
 	data->memory.free_func(data->memory.user_data, data->buffers);
 
 	for (cgltf_size i = 0; i < data->meshes_count; ++i)
@@ -1849,9 +1866,15 @@ void cgltf_free(cgltf_data* data)
 				data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes);
 			}
 
+			for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k)
+			{
+				cgltf_free_extras(data, &data->meshes[i].primitives[j].mappings[k].extras);
+			}
+
 			data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].mappings);
 
 			cgltf_free_extensions(data, data->meshes[i].primitives[j].extensions, data->meshes[i].primitives[j].extensions_count);
+			cgltf_free_extras(data, &data->meshes[i].primitives[j].extras);
 		}
 
 		data->memory.free_func(data->memory.user_data, data->meshes[i].primitives);
@@ -1863,6 +1886,7 @@ void cgltf_free(cgltf_data* data)
 		}
 
 		cgltf_free_extensions(data, data->meshes[i].extensions, data->meshes[i].extensions_count);
+		cgltf_free_extras(data, &data->meshes[i].extras);
 
 		data->memory.free_func(data->memory.user_data, data->meshes[i].target_names);
 	}
@@ -1875,49 +1899,50 @@ void cgltf_free(cgltf_data* data)
 
 		if(data->materials[i].has_pbr_metallic_roughness)
 		{
-			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);
-			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);
+			cgltf_free_texture_view(data, &data->materials[i].pbr_metallic_roughness.metallic_roughness_texture);
+			cgltf_free_texture_view(data, &data->materials[i].pbr_metallic_roughness.base_color_texture);
 		}
 		if(data->materials[i].has_pbr_specular_glossiness)
 		{
-			cgltf_free_extensions(data, data->materials[i].pbr_specular_glossiness.diffuse_texture.extensions, data->materials[i].pbr_specular_glossiness.diffuse_texture.extensions_count);
-			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);
+			cgltf_free_texture_view(data, &data->materials[i].pbr_specular_glossiness.diffuse_texture);
+			cgltf_free_texture_view(data, &data->materials[i].pbr_specular_glossiness.specular_glossiness_texture);
 		}
 		if(data->materials[i].has_clearcoat)
 		{
-			cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_texture.extensions, data->materials[i].clearcoat.clearcoat_texture.extensions_count);
-			cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_roughness_texture.extensions, data->materials[i].clearcoat.clearcoat_roughness_texture.extensions_count);
-			cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_normal_texture.extensions, data->materials[i].clearcoat.clearcoat_normal_texture.extensions_count);
+			cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_texture);
+			cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_roughness_texture);
+			cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_normal_texture);
 		}
 		if(data->materials[i].has_specular)
 		{
-			cgltf_free_extensions(data, data->materials[i].specular.specular_texture.extensions, data->materials[i].specular.specular_texture.extensions_count);
-			cgltf_free_extensions(data, data->materials[i].specular.specular_color_texture.extensions, data->materials[i].specular.specular_color_texture.extensions_count);
+			cgltf_free_texture_view(data, &data->materials[i].specular.specular_texture);
+			cgltf_free_texture_view(data, &data->materials[i].specular.specular_color_texture);
 		}
 		if(data->materials[i].has_transmission)
 		{
-			cgltf_free_extensions(data, data->materials[i].transmission.transmission_texture.extensions, data->materials[i].transmission.transmission_texture.extensions_count);
+			cgltf_free_texture_view(data, &data->materials[i].transmission.transmission_texture);
 		}
 		if (data->materials[i].has_volume)
 		{
-			cgltf_free_extensions(data, data->materials[i].volume.thickness_texture.extensions, data->materials[i].volume.thickness_texture.extensions_count);
+			cgltf_free_texture_view(data, &data->materials[i].volume.thickness_texture);
 		}
 		if(data->materials[i].has_sheen)
 		{
-			cgltf_free_extensions(data, data->materials[i].sheen.sheen_color_texture.extensions, data->materials[i].sheen.sheen_color_texture.extensions_count);
-			cgltf_free_extensions(data, data->materials[i].sheen.sheen_roughness_texture.extensions, data->materials[i].sheen.sheen_roughness_texture.extensions_count);
+			cgltf_free_texture_view(data, &data->materials[i].sheen.sheen_color_texture);
+			cgltf_free_texture_view(data, &data->materials[i].sheen.sheen_roughness_texture);
 		}
 		if(data->materials[i].has_iridescence)
 		{
-			cgltf_free_extensions(data, data->materials[i].iridescence.iridescence_texture.extensions, data->materials[i].iridescence.iridescence_texture.extensions_count);
-			cgltf_free_extensions(data, data->materials[i].iridescence.iridescence_thickness_texture.extensions, data->materials[i].iridescence.iridescence_thickness_texture.extensions_count);
+			cgltf_free_texture_view(data, &data->materials[i].iridescence.iridescence_texture);
+			cgltf_free_texture_view(data, &data->materials[i].iridescence.iridescence_thickness_texture);
 		}
 
-		cgltf_free_extensions(data, data->materials[i].normal_texture.extensions, data->materials[i].normal_texture.extensions_count);
-		cgltf_free_extensions(data, data->materials[i].occlusion_texture.extensions, data->materials[i].occlusion_texture.extensions_count);
-		cgltf_free_extensions(data, data->materials[i].emissive_texture.extensions, data->materials[i].emissive_texture.extensions_count);
+		cgltf_free_texture_view(data, &data->materials[i].normal_texture);
+		cgltf_free_texture_view(data, &data->materials[i].occlusion_texture);
+		cgltf_free_texture_view(data, &data->materials[i].emissive_texture);
 
 		cgltf_free_extensions(data, data->materials[i].extensions, data->materials[i].extensions_count);
+		cgltf_free_extras(data, &data->materials[i].extras);
 	}
 
 	data->memory.free_func(data->memory.user_data, data->materials);
@@ -1929,6 +1954,7 @@ void cgltf_free(cgltf_data* data)
 		data->memory.free_func(data->memory.user_data, data->images[i].mime_type);
 
 		cgltf_free_extensions(data, data->images[i].extensions, data->images[i].extensions_count);
+		cgltf_free_extras(data, &data->images[i].extras);
 	}
 
 	data->memory.free_func(data->memory.user_data, data->images);
@@ -1936,7 +1962,9 @@ void cgltf_free(cgltf_data* data)
 	for (cgltf_size i = 0; i < data->textures_count; ++i)
 	{
 		data->memory.free_func(data->memory.user_data, data->textures[i].name);
+
 		cgltf_free_extensions(data, data->textures[i].extensions, data->textures[i].extensions_count);
+		cgltf_free_extras(data, &data->textures[i].extras);
 	}
 
 	data->memory.free_func(data->memory.user_data, data->textures);
@@ -1944,7 +1972,9 @@ void cgltf_free(cgltf_data* data)
 	for (cgltf_size i = 0; i < data->samplers_count; ++i)
 	{
 		data->memory.free_func(data->memory.user_data, data->samplers[i].name);
+
 		cgltf_free_extensions(data, data->samplers[i].extensions, data->samplers[i].extensions_count);
+		cgltf_free_extras(data, &data->samplers[i].extras);
 	}
 
 	data->memory.free_func(data->memory.user_data, data->samplers);
@@ -1955,6 +1985,7 @@ void cgltf_free(cgltf_data* data)
 		data->memory.free_func(data->memory.user_data, data->skins[i].joints);
 
 		cgltf_free_extensions(data, data->skins[i].extensions, data->skins[i].extensions_count);
+		cgltf_free_extras(data, &data->skins[i].extras);
 	}
 
 	data->memory.free_func(data->memory.user_data, data->skins);
@@ -1962,7 +1993,18 @@ void cgltf_free(cgltf_data* data)
 	for (cgltf_size i = 0; i < data->cameras_count; ++i)
 	{
 		data->memory.free_func(data->memory.user_data, data->cameras[i].name);
+
+		if (data->cameras[i].type == cgltf_camera_type_perspective)
+		{
+			cgltf_free_extras(data, &data->cameras[i].data.perspective.extras);
+		}
+		else if (data->cameras[i].type == cgltf_camera_type_orthographic)
+		{
+			cgltf_free_extras(data, &data->cameras[i].data.orthographic.extras);
+		}
+
 		cgltf_free_extensions(data, data->cameras[i].extensions, data->cameras[i].extensions_count);
+		cgltf_free_extras(data, &data->cameras[i].extras);
 	}
 
 	data->memory.free_func(data->memory.user_data, data->cameras);
@@ -1970,6 +2012,8 @@ void cgltf_free(cgltf_data* data)
 	for (cgltf_size i = 0; i < data->lights_count; ++i)
 	{
 		data->memory.free_func(data->memory.user_data, data->lights[i].name);
+
+		cgltf_free_extras(data, &data->lights[i].extras);
 	}
 
 	data->memory.free_func(data->memory.user_data, data->lights);
@@ -1979,7 +2023,19 @@ void cgltf_free(cgltf_data* data)
 		data->memory.free_func(data->memory.user_data, data->nodes[i].name);
 		data->memory.free_func(data->memory.user_data, data->nodes[i].children);
 		data->memory.free_func(data->memory.user_data, data->nodes[i].weights);
+
+		if (data->nodes[i].has_mesh_gpu_instancing)
+		{
+			for (cgltf_size j = 0; j < data->nodes[i].mesh_gpu_instancing.attributes_count; ++j)
+			{
+				data->memory.free_func(data->memory.user_data, data->nodes[i].mesh_gpu_instancing.attributes[j].name);
+			}
+
+			data->memory.free_func(data->memory.user_data, data->nodes[i].mesh_gpu_instancing.attributes);
+		}
+
 		cgltf_free_extensions(data, data->nodes[i].extensions, data->nodes[i].extensions_count);
+		cgltf_free_extras(data, &data->nodes[i].extras);
 	}
 
 	data->memory.free_func(data->memory.user_data, data->nodes);
@@ -1990,6 +2046,7 @@ void cgltf_free(cgltf_data* data)
 		data->memory.free_func(data->memory.user_data, data->scenes[i].nodes);
 
 		cgltf_free_extensions(data, data->scenes[i].extensions, data->scenes[i].extensions_count);
+		cgltf_free_extras(data, &data->scenes[i].extras);
 	}
 
 	data->memory.free_func(data->memory.user_data, data->scenes);
@@ -2000,16 +2057,19 @@ void cgltf_free(cgltf_data* data)
 		for (cgltf_size j = 0; j <  data->animations[i].samplers_count; ++j)
 		{
 			cgltf_free_extensions(data, data->animations[i].samplers[j].extensions, data->animations[i].samplers[j].extensions_count);
+			cgltf_free_extras(data, &data->animations[i].samplers[j].extras);
 		}
 		data->memory.free_func(data->memory.user_data, data->animations[i].samplers);
 
 		for (cgltf_size j = 0; j <  data->animations[i].channels_count; ++j)
 		{
 			cgltf_free_extensions(data, data->animations[i].channels[j].extensions, data->animations[i].channels[j].extensions_count);
+			cgltf_free_extras(data, &data->animations[i].channels[j].extras);
 		}
 		data->memory.free_func(data->memory.user_data, data->animations[i].channels);
 
 		cgltf_free_extensions(data, data->animations[i].extensions, data->animations[i].extensions_count);
+		cgltf_free_extras(data, &data->animations[i].extras);
 	}
 
 	data->memory.free_func(data->memory.user_data, data->animations);
@@ -2017,11 +2077,14 @@ void cgltf_free(cgltf_data* data)
 	for (cgltf_size i = 0; i < data->variants_count; ++i)
 	{
 		data->memory.free_func(data->memory.user_data, data->variants[i].name);
+
+		cgltf_free_extras(data, &data->variants[i].extras);
 	}
 
 	data->memory.free_func(data->memory.user_data, data->variants);
 
 	cgltf_free_extensions(data, data->data_extensions, data->data_extensions_count);
+	cgltf_free_extras(data, &data->extras);
 
 	for (cgltf_size i = 0; i < data->extensions_used_count; ++i)
 	{
@@ -2683,11 +2746,27 @@ static int cgltf_parse_json_attribute_list(cgltf_options* options, jsmntok_t con
 	return i;
 }
 
-static int cgltf_parse_json_extras(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras)
+static int cgltf_parse_json_extras(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras)
 {
-	(void)json_chunk;
+	if (out_extras->data)
+	{
+		return CGLTF_ERROR_JSON;
+	}
+
+	/* fill deprecated fields for now, this will be removed in the future */
 	out_extras->start_offset = tokens[i].start;
 	out_extras->end_offset = tokens[i].end;
+
+	size_t start = tokens[i].start;
+	size_t size = tokens[i].end - start;
+	out_extras->data = (char*)options->memory.alloc_func(options->memory.user_data, size + 1);
+	if (!out_extras->data)
+	{
+		return CGLTF_ERROR_NOMEM;
+	}
+	strncpy(out_extras->data, (const char*)json_chunk + start, size);
+	out_extras->data[size] = '\0';
+
 	i = cgltf_skip_json(tokens, i);
 	return i;
 }
@@ -2842,7 +2921,7 @@ static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmnto
 
 		int material = -1;
 		int variants_tok = -1;
-		cgltf_extras extras = {0, 0};
+		int extras_tok = -1;
 
 		for (int k = 0; k < obj_size; ++k)
 		{
@@ -2863,7 +2942,8 @@ static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmnto
 			}
 			else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 			{
-				i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &extras);
+				extras_tok = i + 1;
+				i = cgltf_skip_json(tokens, extras_tok);
 			}
 			else
 			{
@@ -2891,7 +2971,13 @@ static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmnto
 
 				out_mappings[*offset].material = CGLTF_PTRINDEX(cgltf_material, material);
 				out_mappings[*offset].variant = variant;
-				out_mappings[*offset].extras = extras;
+
+				if (extras_tok >= 0)
+				{
+					int e = cgltf_parse_json_extras(options, tokens, extras_tok, json_chunk, &out_mappings[*offset].extras);
+					if (e < 0)
+						return e;
+				}
 
 				(*offset)++;
 			}
@@ -3006,7 +3092,7 @@ static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* t
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_prim->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_prim->extras);
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 		{
@@ -3253,7 +3339,7 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co
 				}
 				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 				{
-					i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->indices_extras);
+					i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->indices_extras);
 				}
 				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 				{
@@ -3296,7 +3382,7 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co
 				}
 				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 				{
-					i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->values_extras);
+					i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->values_extras);
 				}
 				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 				{
@@ -3315,7 +3401,7 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->extras);
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 		{
@@ -3438,7 +3524,7 @@ static int cgltf_parse_json_accessor(cgltf_options* options, jsmntok_t const* to
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_accessor->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_accessor->extras);
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 		{
@@ -3544,7 +3630,7 @@ static int cgltf_parse_json_texture_view(cgltf_options* options, jsmntok_t const
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_texture_view->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_texture_view->extras);
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 		{
@@ -3640,10 +3726,6 @@ static int cgltf_parse_json_pbr_metallic_roughness(cgltf_options* options, jsmnt
 			i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk,
 				&out_pbr->metallic_roughness_texture);
 		}
-		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
-		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_pbr->extras);
-		}
 		else
 		{
 			i = cgltf_skip_json(tokens, i+1);
@@ -4076,7 +4158,7 @@ static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* token
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_image->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_image->extras);
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 		{
@@ -4145,7 +4227,7 @@ static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tok
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sampler->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sampler->extras);
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 		{
@@ -4194,7 +4276,7 @@ static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tok
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_texture->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_texture->extras);
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 		{
@@ -4357,7 +4439,7 @@ static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* to
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_material->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_material->extras);
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 		{
@@ -4710,7 +4792,7 @@ static int cgltf_parse_json_buffer_view(cgltf_options* options, jsmntok_t const*
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_buffer_view->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_buffer_view->extras);
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 		{
@@ -4813,7 +4895,7 @@ static int cgltf_parse_json_buffer(cgltf_options* options, jsmntok_t const* toke
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_buffer->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_buffer->extras);
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 		{
@@ -4897,7 +4979,7 @@ static int cgltf_parse_json_skin(cgltf_options* options, jsmntok_t const* tokens
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_skin->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_skin->extras);
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 		{
@@ -4951,19 +5033,6 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
 		{
 			i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_camera->name);
 		}
-		else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0)
-		{
-			++i;
-			if (cgltf_json_strcmp(tokens + i, json_chunk, "perspective") == 0)
-			{
-				out_camera->type = cgltf_camera_type_perspective;
-			}
-			else if (cgltf_json_strcmp(tokens + i, json_chunk, "orthographic") == 0)
-			{
-				out_camera->type = cgltf_camera_type_orthographic;
-			}
-			++i;
-		}
 		else if (cgltf_json_strcmp(tokens+i, json_chunk, "perspective") == 0)
 		{
 			++i;
@@ -4973,6 +5042,11 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
 			int data_size = tokens[i].size;
 			++i;
 
+			if (out_camera->type != cgltf_camera_type_invalid)
+			{
+				return CGLTF_ERROR_JSON;
+			}
+
 			out_camera->type = cgltf_camera_type_perspective;
 
 			for (int k = 0; k < data_size; ++k)
@@ -5007,7 +5081,7 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
 				}
 				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 				{
-					i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->data.perspective.extras);
+					i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.perspective.extras);
 				}
 				else
 				{
@@ -5029,6 +5103,11 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
 			int data_size = tokens[i].size;
 			++i;
 
+			if (out_camera->type != cgltf_camera_type_invalid)
+			{
+				return CGLTF_ERROR_JSON;
+			}
+
 			out_camera->type = cgltf_camera_type_orthographic;
 
 			for (int k = 0; k < data_size; ++k)
@@ -5061,7 +5140,7 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
 				}
 				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 				{
-					i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras);
+					i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras);
 				}
 				else
 				{
@@ -5076,7 +5155,7 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->extras);
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 		{
@@ -5209,7 +5288,7 @@ static int cgltf_parse_json_light(cgltf_options* options, jsmntok_t const* token
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_light->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_light->extras);
 		}
 		else
 		{
@@ -5335,7 +5414,7 @@ static int cgltf_parse_json_node(cgltf_options* options, jsmntok_t const* tokens
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_node->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_node->extras);
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 		{
@@ -5473,7 +5552,7 @@ static int cgltf_parse_json_scene(cgltf_options* options, jsmntok_t const* token
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_scene->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_scene->extras);
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 		{
@@ -5555,7 +5634,7 @@ static int cgltf_parse_json_animation_sampler(cgltf_options* options, jsmntok_t
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sampler->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sampler->extras);
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 		{
@@ -5635,7 +5714,7 @@ static int cgltf_parse_json_animation_channel(cgltf_options* options, jsmntok_t
 				}
 				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 				{
-					i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_channel->extras);
+					i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_channel->extras);
 				}
 				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 				{
@@ -5717,7 +5796,7 @@ static int cgltf_parse_json_animation(cgltf_options* options, jsmntok_t const* t
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_animation->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_animation->extras);
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 		{
@@ -5773,7 +5852,7 @@ static int cgltf_parse_json_variant(cgltf_options* options, jsmntok_t const* tok
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_variant->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_variant->extras);
 		}
 		else
 		{
@@ -5837,7 +5916,7 @@ static int cgltf_parse_json_asset(cgltf_options* options, jsmntok_t const* token
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_asset->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_asset->extras);
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 		{
@@ -5993,7 +6072,7 @@ static int cgltf_parse_json_root(cgltf_options* options, jsmntok_t const* tokens
 		}
 		else if (cgltf_json_strcmp(tokens+i, json_chunk, "extras") == 0)
 		{
-			i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_data->extras);
+			i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_data->extras);
 		}
 		else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
 		{

+ 104 - 19
3rdparty/cgltf/cgltf_write.h

@@ -1,7 +1,7 @@
 /**
  * cgltf_write - a single-file glTF 2.0 writer written in C99.
  *
- * Version: 1.12
+ * Version: 1.13
  *
  * Website: https://github.com/jkuhlmann/cgltf
  *
@@ -15,19 +15,17 @@
  *
  * Reference:
  * `cgltf_result cgltf_write_file(const cgltf_options* options, const char*
- * path, const cgltf_data* data)` writes JSON to the given file path. Buffer
- * files and external images are not written out. `data` is not deallocated.
+ * path, const cgltf_data* data)` writes a glTF data to the given file path.
+ * If `options->type` is `cgltf_file_type_glb`, both JSON content and binary
+ * buffer of the given glTF data will be written in a GLB format.
+ * Otherwise, only the JSON part will be written.
+ * External buffers and images are not written out. `data` is not deallocated.
  *
  * `cgltf_size cgltf_write(const cgltf_options* options, char* buffer,
  * cgltf_size size, const cgltf_data* data)` writes JSON into the given memory
  * buffer. Returns the number of bytes written to `buffer`, including a null
  * terminator. If buffer is null, returns the number of bytes that would have
  * been written. `data` is not deallocated.
- *
- * To write custom JSON into the `extras` field, aggregate all the custom JSON
- * into a single buffer, then set `file_data` to this buffer. By supplying
- * start_offset and end_offset values for various objects, you can select a
- * range of characters within the aggregated buffer.
  */
 #ifndef CGLTF_WRITE_H_INCLUDED__
 #define CGLTF_WRITE_H_INCLUDED__
@@ -147,6 +145,17 @@ typedef struct {
 		context->needs_comma = 1; }
 
 #define CGLTF_WRITE_TEXTURE_INFO(label, info) if (info.texture) { \
+		cgltf_write_line(context, "\"" label "\": {"); \
+		CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \
+		cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \
+		if (info.has_transform) { \
+			context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \
+			cgltf_write_texture_transform(context, &info.transform); \
+		} \
+		cgltf_write_extras(context, &info.extras); \
+		cgltf_write_line(context, "}"); }
+
+#define CGLTF_WRITE_NORMAL_TEXTURE_INFO(label, info) if (info.texture) { \
 		cgltf_write_line(context, "\"" label "\": {"); \
 		CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \
 		cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \
@@ -158,6 +167,28 @@ typedef struct {
 		cgltf_write_extras(context, &info.extras); \
 		cgltf_write_line(context, "}"); }
 
+#define CGLTF_WRITE_OCCLUSION_TEXTURE_INFO(label, info) if (info.texture) { \
+		cgltf_write_line(context, "\"" label "\": {"); \
+		CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \
+		cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \
+		cgltf_write_floatprop(context, "strength", info.scale, 1.0f); \
+		if (info.has_transform) { \
+			context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \
+			cgltf_write_texture_transform(context, &info.transform); \
+		} \
+		cgltf_write_extras(context, &info.extras); \
+		cgltf_write_line(context, "}"); }
+
+#ifndef CGLTF_CONSTS
+static const cgltf_size GlbHeaderSize = 12;
+static const cgltf_size GlbChunkHeaderSize = 8;
+static const uint32_t GlbVersion = 2;
+static const uint32_t GlbMagic = 0x46546C67;
+static const uint32_t GlbMagicJsonChunk = 0x4E4F534A;
+static const uint32_t GlbMagicBinChunk = 0x004E4942;
+#define CGLTF_CONSTS
+#endif
+
 static void cgltf_write_indent(cgltf_write_context* context)
 {
 	if (context->needs_comma)
@@ -208,15 +239,24 @@ static void cgltf_write_strprop(cgltf_write_context* context, const char* label,
 
 static void cgltf_write_extras(cgltf_write_context* context, const cgltf_extras* extras)
 {
-	cgltf_size length = extras->end_offset - extras->start_offset;
-	if (length > 0 && context->data->file_data)
+	if (extras->data)
 	{
-		char* json_string = ((char*) context->data->file_data) + extras->start_offset;
 		cgltf_write_indent(context);
-		CGLTF_SPRINTF("%s", "\"extras\": ");
-		CGLTF_SNPRINTF(length, "%.*s", (int)(extras->end_offset - extras->start_offset), json_string);
+		CGLTF_SPRINTF("\"extras\": %s", extras->data);
 		context->needs_comma = 1;
 	}
+	else
+	{
+		cgltf_size length = extras->end_offset - extras->start_offset;
+		if (length > 0 && context->data->json)
+		{
+			char* json_string = ((char*) context->data->json) + extras->start_offset;
+			cgltf_write_indent(context);
+			CGLTF_SPRINTF("%s", "\"extras\": ");
+			CGLTF_SNPRINTF(length, "%.*s", (int)(extras->end_offset - extras->start_offset), json_string);
+			context->needs_comma = 1;
+		}
+	}
 }
 
 static void cgltf_write_stritem(cgltf_write_context* context, const char* item)
@@ -613,7 +653,6 @@ static void cgltf_write_material(cgltf_write_context* context, const cgltf_mater
 		{
 			cgltf_write_floatarrayprop(context, "baseColorFactor", params->base_color_factor, 4);
 		}
-		cgltf_write_extras(context, &params->extras);
 		cgltf_write_line(context, "}");
 	}
 
@@ -626,7 +665,7 @@ static void cgltf_write_material(cgltf_write_context* context, const cgltf_mater
 			cgltf_write_line(context, "\"KHR_materials_clearcoat\": {");
 			CGLTF_WRITE_TEXTURE_INFO("clearcoatTexture", params->clearcoat_texture);
 			CGLTF_WRITE_TEXTURE_INFO("clearcoatRoughnessTexture", params->clearcoat_roughness_texture);
-			CGLTF_WRITE_TEXTURE_INFO("clearcoatNormalTexture", params->clearcoat_normal_texture);
+			CGLTF_WRITE_NORMAL_TEXTURE_INFO("clearcoatNormalTexture", params->clearcoat_normal_texture);
 			cgltf_write_floatprop(context, "clearcoatFactor", params->clearcoat_factor, 0.0f);
 			cgltf_write_floatprop(context, "clearcoatRoughnessFactor", params->clearcoat_roughness_factor, 0.0f);
 			cgltf_write_line(context, "}");
@@ -731,8 +770,8 @@ static void cgltf_write_material(cgltf_write_context* context, const cgltf_mater
 		cgltf_write_line(context, "}");
 	}
 
-	CGLTF_WRITE_TEXTURE_INFO("normalTexture", material->normal_texture);
-	CGLTF_WRITE_TEXTURE_INFO("occlusionTexture", material->occlusion_texture);
+	CGLTF_WRITE_NORMAL_TEXTURE_INFO("normalTexture", material->normal_texture);
+	CGLTF_WRITE_OCCLUSION_TEXTURE_INFO("occlusionTexture", material->occlusion_texture);
 	CGLTF_WRITE_TEXTURE_INFO("emissiveTexture", material->emissive_texture);
 	if (cgltf_check_floatarray(material->emissive_factor, 3, 0.0f))
 	{
@@ -1097,6 +1136,47 @@ static void cgltf_write_variant(cgltf_write_context* context, const cgltf_materi
 	cgltf_write_line(context, "}");
 }
 
+static void cgltf_write_glb(FILE* file, const void* json_buf, const cgltf_size json_size, const void* bin_buf, const cgltf_size bin_size)
+{
+	char header[GlbHeaderSize];
+	char chunk_header[GlbChunkHeaderSize];
+	char json_pad[3] = { 0x20, 0x20, 0x20 };
+	char bin_pad[3] = { 0, 0, 0 };
+
+	cgltf_size json_padsize = (json_size % 4 != 0) ? 4 - json_size % 4 : 0;
+	cgltf_size bin_padsize = (bin_size % 4 != 0) ? 4 - bin_size % 4 : 0;
+	cgltf_size total_size = GlbHeaderSize + GlbChunkHeaderSize + json_size + json_padsize;
+	if (bin_buf != NULL && bin_size > 0) {
+		total_size += GlbChunkHeaderSize + bin_size + bin_padsize;
+	}
+
+	// Write a GLB header
+	memcpy(header, &GlbMagic, 4);
+	memcpy(header + 4, &GlbVersion, 4);
+	memcpy(header + 8, &total_size, 4);
+	fwrite(header, 1, GlbHeaderSize, file);
+
+	// Write a JSON chunk (header & data)
+	uint32_t json_chunk_size = (uint32_t)(json_size + json_padsize);
+	memcpy(chunk_header, &json_chunk_size, 4);
+	memcpy(chunk_header + 4, &GlbMagicJsonChunk, 4);
+	fwrite(chunk_header, 1, GlbChunkHeaderSize, file);
+
+	fwrite(json_buf, 1, json_size, file);
+	fwrite(json_pad, 1, json_padsize, file);
+
+	if (bin_buf != NULL && bin_size > 0) {
+		// Write a binary chunk (header & data)
+		uint32_t bin_chunk_size = (uint32_t)(bin_size + bin_padsize);
+		memcpy(chunk_header, &bin_chunk_size, 4);
+		memcpy(chunk_header + 4, &GlbMagicBinChunk, 4);
+		fwrite(chunk_header, 1, GlbChunkHeaderSize, file);
+
+		fwrite(bin_buf, 1, bin_size, file);
+		fwrite(bin_pad, 1, bin_padsize, file);
+	}
+}
+
 cgltf_result cgltf_write_file(const cgltf_options* options, const char* path, const cgltf_data* data)
 {
 	cgltf_size expected = cgltf_write(options, NULL, 0, data);
@@ -1105,13 +1185,18 @@ cgltf_result cgltf_write_file(const cgltf_options* options, const char* path, co
 	if (expected != actual) {
 		fprintf(stderr, "Error: expected %zu bytes but wrote %zu bytes.\n", expected, actual);
 	}
-	FILE* file = fopen(path, "wt");
+	FILE* file = fopen(path, "wb");
 	if (!file)
 	{
 		return cgltf_result_file_not_found;
 	}
 	// Note that cgltf_write() includes a null terminator, which we omit from the file content.
-	fwrite(buffer, actual - 1, 1, file);
+	if (options->type == cgltf_file_type_glb) {
+		cgltf_write_glb(file, buffer, actual - 1, data->bin, data->bin_size);
+	} else {
+		// Write a plain JSON file.
+		fwrite(buffer, actual - 1, 1, file);
+	}
 	fclose(file);
 	free(buffer);
 	return cgltf_result_success;