From 86d5a546b4eab47b991a0acc4ab444c725feb081 Mon Sep 17 00:00:00 2001 From: quou Date: Sun, 25 Aug 2024 15:38:49 +1000 Subject: GLTF meshes --- 3de.c | 9 + Makefile | 11 +- asset.h | 18 +- cgltf.h | 7081 +++++++++++++++++++++++++++++++++++++++++++++++ convmesh.c | 398 ++- intermediate/cube.obj | 45 - intermediate/gun.glb | Bin 0 -> 9176 bytes intermediate/gun.obj | 194 -- intermediate/guy.fbx | Bin 115868 -> 0 bytes intermediate/guy.glb | Bin 0 -> 35768 bytes intermediate/monkey.obj | 2541 ----------------- 11 files changed, 7284 insertions(+), 3013 deletions(-) create mode 100644 cgltf.h delete mode 100644 intermediate/cube.obj create mode 100644 intermediate/gun.glb delete mode 100644 intermediate/gun.obj delete mode 100644 intermediate/guy.fbx create mode 100644 intermediate/guy.glb delete mode 100644 intermediate/monkey.obj diff --git a/3de.c b/3de.c index cc7adbd..94dceac 100644 --- a/3de.c +++ b/3de.c @@ -73,6 +73,14 @@ void draw_map(Renderer* r, const Player* p, const Map* m) { ren_map(r, m, pos, p->f, p->l); } +void draw_guy(Renderer* r, const Player* p) { + const Mesh* m = get_mesh(asset_id_guy_mesh); + const Texture* tex = get_texture(asset_id_guy_texture); + push_player_cam(p); + ren_mesh(r, m, tex); + pop_player_cam(); +} + int entrypoint(int argc, const char** argv, Arena* a) { App* app; Renderer r = { 0 }; @@ -147,6 +155,7 @@ int entrypoint(int argc, const char** argv, Arena* a) { edit_map(&e, &g, map); } else { draw_map(&r, &p, map); + draw_guy(&r, &p); draw_player_world(&r, &p); } /*draw_tri(&r, app->mx, app->my);*/ diff --git a/Makefile b/Makefile index b80e512..d5c8166 100644 --- a/Makefile +++ b/Makefile @@ -30,9 +30,8 @@ sources = \ objects = $(sources:%.c=%.o) mesh_sources = \ - $(int_dir)/cube.obj \ - $(int_dir)/gun.obj \ - $(int_dir)/monkey.obj + $(int_dir)/gun.glb \ + $(int_dir)/guy.glb texture_sources = \ $(int_dir)/brick.bmp \ @@ -41,7 +40,7 @@ texture_sources = \ $(int_dir)/gun.bmp \ $(int_dir)/guy.bmp -meshes = $(mesh_sources:$(int_dir)/%.obj=$(data_dir)/%.msh) +meshes = $(mesh_sources:$(int_dir)/%.glb=$(data_dir)/%.msh) textures = $(texture_sources:$(int_dir)/%.bmp=$(data_dir)/%.bc1) all: $(target) $(pack) @@ -52,14 +51,14 @@ $(objects): %.o : %.c $(target): $(objects) $(linker) $(objects) -o $@ $(lflags) -$(meshes): $(data_dir)/%.msh : $(int_dir)/%.obj | $(convmesh) $(data_dir) +$(meshes): $(data_dir)/%.msh : $(int_dir)/%.glb | $(convmesh) $(data_dir) ./$(convmesh) $< $@ $(textures): $(data_dir)/%.bc1 : $(int_dir)/%.bmp | $(convtexture) $(data_dir) ./$(convtexture) $< $@ $(convmesh): convmesh.c - $(tool_compiler) $(cflags) -o $@ $< + $(tool_compiler) -o $@ $< $(convtexture): convtexture.c $(tool_compiler) $(cflags) -o $@ $< -lm diff --git a/asset.h b/asset.h index 370ad15..d3a29a6 100644 --- a/asset.h +++ b/asset.h @@ -7,25 +7,25 @@ struct Mesh; #define assets_xmacro() \ x( \ - asset_id_cube, \ - asset_type_mesh, \ - "cube.msh" \ - ) \ - x( \ - asset_id_monkey, \ + asset_id_gun_mesh, \ asset_type_mesh, \ - "monkey.msh" \ + "gun.msh" \ ) \ x( \ - asset_id_gun_mesh, \ + asset_id_guy_mesh, \ asset_type_mesh, \ - "gun.msh" \ + "guy.msh" \ ) \ x( \ asset_id_gun_texture, \ asset_type_texture, \ "gun.bc1" \ ) \ + x( \ + asset_id_guy_texture, \ + asset_type_texture, \ + "guy.bc1" \ + ) \ x( \ asset_id_brick_texture, \ asset_type_texture, \ diff --git a/cgltf.h b/cgltf.h new file mode 100644 index 0000000..36fd644 --- /dev/null +++ b/cgltf.h @@ -0,0 +1,7081 @@ +/** + * cgltf - a single-file glTF 2.0 parser written in C99. + * + * Version: 1.14 + * + * Website: https://github.com/jkuhlmann/cgltf + * + * Distributed under the MIT License, see notice at the end of this file. + * + * Building: + * Include this file where you need the struct and function + * declarations. Have exactly one source file where you define + * `CGLTF_IMPLEMENTATION` before including this file to get the + * function definitions. + * + * Reference: + * `cgltf_result cgltf_parse(const cgltf_options*, const void*, + * cgltf_size, cgltf_data**)` parses both glTF and GLB data. If + * this function returns `cgltf_result_success`, you have to call + * `cgltf_free()` on the created `cgltf_data*` variable. + * Note that contents of external files for buffers and images are not + * automatically loaded. You'll need to read these files yourself using + * URIs in the `cgltf_data` structure. + * + * `cgltf_options` is the struct passed to `cgltf_parse()` to control + * parts of the parsing process. You can use it to force the file type + * and provide memory allocation as well as file operation callbacks. + * Should be zero-initialized to trigger default behavior. + * + * `cgltf_data` is the struct allocated and filled by `cgltf_parse()`. + * It generally mirrors the glTF format as described by the spec (see + * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0). + * + * `void cgltf_free(cgltf_data*)` frees the allocated `cgltf_data` + * variable. + * + * `cgltf_result cgltf_load_buffers(const cgltf_options*, cgltf_data*, + * const char* gltf_path)` can be optionally called to open and read buffer + * files using the `FILE*` APIs. The `gltf_path` argument is the path to + * the original glTF file, which allows the parser to resolve the path to + * buffer files. + * + * `cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, + * cgltf_size size, const char* base64, void** out_data)` decodes + * base64-encoded data content. Used internally by `cgltf_load_buffers()`. + * This is useful when decoding data URIs in images. + * + * `cgltf_result cgltf_parse_file(const cgltf_options* options, const + * char* path, cgltf_data** out_data)` can be used to open the given + * file using `FILE*` APIs and parse the data using `cgltf_parse()`. + * + * `cgltf_result cgltf_validate(cgltf_data*)` can be used to do additional + * checks to make sure the parsed glTF data is valid. + * + * `cgltf_node_transform_local` converts the translation / rotation / scale properties of a node + * into a mat4. + * + * `cgltf_node_transform_world` calls `cgltf_node_transform_local` on every ancestor in order + * to compute the root-to-node transformation. + * + * `cgltf_accessor_unpack_floats` reads in the data from an accessor, applies sparse data (if any), + * and converts them to floating point. Assumes that `cgltf_load_buffers` has already been called. + * By passing null for the output pointer, users can find out how many floats are required in the + * output buffer. + * + * `cgltf_accessor_unpack_indices` reads in the index data from an accessor. Assumes that + * `cgltf_load_buffers` has already been called. By passing null for the output pointer, users can + * find out how many indices are required in the output buffer. Returns 0 if the accessor is + * sparse or if the output component size is less than the accessor's component size. + * + * `cgltf_num_components` is a tiny utility that tells you the dimensionality of + * a certain accessor type. This can be used before `cgltf_accessor_unpack_floats` to help allocate + * the necessary amount of memory. `cgltf_component_size` and `cgltf_calc_size` exist for + * similar purposes. + * + * `cgltf_accessor_read_float` reads a certain element from a non-sparse accessor and converts it to + * floating point, assuming that `cgltf_load_buffers` has already been called. The passed-in element + * size is the number of floats in the output buffer, which should be in the range [1, 16]. Returns + * false if the passed-in element_size is too small, or if the accessor is sparse. + * + * `cgltf_accessor_read_uint` is similar to its floating-point counterpart, but limited to reading + * vector types and does not support matrix types. The passed-in element size is the number of uints + * in the output buffer, which should be in the range [1, 4]. Returns false if the passed-in + * element_size is too small, or if the accessor is sparse. + * + * `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_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__ +#define CGLTF_H_INCLUDED__ + +#include +#include /* For uint8_t, uint32_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef size_t cgltf_size; +typedef long long int cgltf_ssize; +typedef float cgltf_float; +typedef int cgltf_int; +typedef unsigned int cgltf_uint; +typedef int cgltf_bool; + +typedef enum cgltf_file_type +{ + cgltf_file_type_invalid, + cgltf_file_type_gltf, + cgltf_file_type_glb, + cgltf_file_type_max_enum +} cgltf_file_type; + +typedef enum cgltf_result +{ + cgltf_result_success, + cgltf_result_data_too_short, + cgltf_result_unknown_format, + cgltf_result_invalid_json, + cgltf_result_invalid_gltf, + cgltf_result_invalid_options, + cgltf_result_file_not_found, + cgltf_result_io_error, + cgltf_result_out_of_memory, + cgltf_result_legacy_gltf, + cgltf_result_max_enum +} cgltf_result; + +typedef struct cgltf_memory_options +{ + void* (*alloc_func)(void* user, cgltf_size size); + void (*free_func) (void* user, void* ptr); + void* user_data; +} cgltf_memory_options; + +typedef struct cgltf_file_options +{ + 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); + void (*release)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data); + void* user_data; +} cgltf_file_options; + +typedef struct cgltf_options +{ + cgltf_file_type type; /* invalid == auto detect */ + cgltf_size json_token_count; /* 0 == auto */ + cgltf_memory_options memory; + cgltf_file_options file; +} cgltf_options; + +typedef enum cgltf_buffer_view_type +{ + cgltf_buffer_view_type_invalid, + cgltf_buffer_view_type_indices, + cgltf_buffer_view_type_vertices, + cgltf_buffer_view_type_max_enum +} cgltf_buffer_view_type; + +typedef enum cgltf_attribute_type +{ + cgltf_attribute_type_invalid, + cgltf_attribute_type_position, + cgltf_attribute_type_normal, + cgltf_attribute_type_tangent, + cgltf_attribute_type_texcoord, + cgltf_attribute_type_color, + cgltf_attribute_type_joints, + cgltf_attribute_type_weights, + cgltf_attribute_type_custom, + cgltf_attribute_type_max_enum +} cgltf_attribute_type; + +typedef enum cgltf_component_type +{ + cgltf_component_type_invalid, + cgltf_component_type_r_8, /* BYTE */ + cgltf_component_type_r_8u, /* UNSIGNED_BYTE */ + cgltf_component_type_r_16, /* SHORT */ + cgltf_component_type_r_16u, /* UNSIGNED_SHORT */ + cgltf_component_type_r_32u, /* UNSIGNED_INT */ + cgltf_component_type_r_32f, /* FLOAT */ + cgltf_component_type_max_enum +} cgltf_component_type; + +typedef enum cgltf_type +{ + cgltf_type_invalid, + cgltf_type_scalar, + cgltf_type_vec2, + cgltf_type_vec3, + cgltf_type_vec4, + cgltf_type_mat2, + cgltf_type_mat3, + cgltf_type_mat4, + cgltf_type_max_enum +} cgltf_type; + +typedef enum cgltf_primitive_type +{ + cgltf_primitive_type_invalid, + cgltf_primitive_type_points, + cgltf_primitive_type_lines, + cgltf_primitive_type_line_loop, + cgltf_primitive_type_line_strip, + cgltf_primitive_type_triangles, + cgltf_primitive_type_triangle_strip, + cgltf_primitive_type_triangle_fan, + cgltf_primitive_type_max_enum +} cgltf_primitive_type; + +typedef enum cgltf_alpha_mode +{ + cgltf_alpha_mode_opaque, + cgltf_alpha_mode_mask, + cgltf_alpha_mode_blend, + cgltf_alpha_mode_max_enum +} cgltf_alpha_mode; + +typedef enum cgltf_animation_path_type { + cgltf_animation_path_type_invalid, + cgltf_animation_path_type_translation, + cgltf_animation_path_type_rotation, + cgltf_animation_path_type_scale, + cgltf_animation_path_type_weights, + cgltf_animation_path_type_max_enum +} cgltf_animation_path_type; + +typedef enum cgltf_interpolation_type { + cgltf_interpolation_type_linear, + cgltf_interpolation_type_step, + cgltf_interpolation_type_cubic_spline, + cgltf_interpolation_type_max_enum +} cgltf_interpolation_type; + +typedef enum cgltf_camera_type { + cgltf_camera_type_invalid, + cgltf_camera_type_perspective, + cgltf_camera_type_orthographic, + cgltf_camera_type_max_enum +} cgltf_camera_type; + +typedef enum cgltf_light_type { + cgltf_light_type_invalid, + cgltf_light_type_directional, + cgltf_light_type_point, + cgltf_light_type_spot, + cgltf_light_type_max_enum +} cgltf_light_type; + +typedef enum cgltf_data_free_method { + cgltf_data_free_method_none, + cgltf_data_free_method_file_release, + cgltf_data_free_method_memory_free, + cgltf_data_free_method_max_enum +} cgltf_data_free_method; + +typedef struct cgltf_extras { + 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 { + char* name; + char* data; +} cgltf_extension; + +typedef struct cgltf_buffer +{ + char* name; + cgltf_size size; + char* uri; + void* data; /* loaded by cgltf_load_buffers */ + cgltf_data_free_method data_free_method; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_buffer; + +typedef enum cgltf_meshopt_compression_mode { + cgltf_meshopt_compression_mode_invalid, + cgltf_meshopt_compression_mode_attributes, + cgltf_meshopt_compression_mode_triangles, + cgltf_meshopt_compression_mode_indices, + cgltf_meshopt_compression_mode_max_enum +} cgltf_meshopt_compression_mode; + +typedef enum cgltf_meshopt_compression_filter { + cgltf_meshopt_compression_filter_none, + cgltf_meshopt_compression_filter_octahedral, + cgltf_meshopt_compression_filter_quaternion, + cgltf_meshopt_compression_filter_exponential, + cgltf_meshopt_compression_filter_max_enum +} cgltf_meshopt_compression_filter; + +typedef struct cgltf_meshopt_compression +{ + cgltf_buffer* buffer; + cgltf_size offset; + cgltf_size size; + cgltf_size stride; + cgltf_size count; + cgltf_meshopt_compression_mode mode; + cgltf_meshopt_compression_filter filter; +} cgltf_meshopt_compression; + +typedef struct cgltf_buffer_view +{ + char *name; + cgltf_buffer* buffer; + cgltf_size offset; + cgltf_size size; + cgltf_size stride; /* 0 == automatically determined by accessor */ + cgltf_buffer_view_type type; + void* data; /* overrides buffer->data if present, filled by extensions */ + cgltf_bool has_meshopt_compression; + cgltf_meshopt_compression meshopt_compression; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_buffer_view; + +typedef struct cgltf_accessor_sparse +{ + cgltf_size count; + cgltf_buffer_view* indices_buffer_view; + cgltf_size indices_byte_offset; + cgltf_component_type indices_component_type; + cgltf_buffer_view* values_buffer_view; + cgltf_size values_byte_offset; +} cgltf_accessor_sparse; + +typedef struct cgltf_accessor +{ + char* name; + cgltf_component_type component_type; + cgltf_bool normalized; + cgltf_type type; + cgltf_size offset; + cgltf_size count; + cgltf_size stride; + cgltf_buffer_view* buffer_view; + cgltf_bool has_min; + cgltf_float min[16]; + cgltf_bool has_max; + cgltf_float max[16]; + cgltf_bool is_sparse; + cgltf_accessor_sparse sparse; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_accessor; + +typedef struct cgltf_attribute +{ + char* name; + cgltf_attribute_type type; + cgltf_int index; + cgltf_accessor* data; +} cgltf_attribute; + +typedef struct cgltf_image +{ + char* name; + char* uri; + cgltf_buffer_view* buffer_view; + char* mime_type; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_image; + +typedef struct cgltf_sampler +{ + char* name; + cgltf_int mag_filter; + cgltf_int min_filter; + cgltf_int wrap_s; + cgltf_int wrap_t; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_sampler; + +typedef struct cgltf_texture +{ + char* name; + cgltf_image* image; + cgltf_sampler* sampler; + cgltf_bool has_basisu; + cgltf_image* basisu_image; + cgltf_bool has_webp; + cgltf_image* webp_image; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_texture; + +typedef struct cgltf_texture_transform +{ + cgltf_float offset[2]; + cgltf_float rotation; + cgltf_float scale[2]; + cgltf_bool has_texcoord; + cgltf_int texcoord; +} cgltf_texture_transform; + +typedef struct cgltf_texture_view +{ + cgltf_texture* texture; + cgltf_int texcoord; + cgltf_float scale; /* equivalent to strength for occlusion_texture */ + cgltf_bool has_transform; + cgltf_texture_transform transform; +} cgltf_texture_view; + +typedef struct cgltf_pbr_metallic_roughness +{ + cgltf_texture_view base_color_texture; + cgltf_texture_view metallic_roughness_texture; + + cgltf_float base_color_factor[4]; + cgltf_float metallic_factor; + cgltf_float roughness_factor; +} cgltf_pbr_metallic_roughness; + +typedef struct cgltf_pbr_specular_glossiness +{ + cgltf_texture_view diffuse_texture; + cgltf_texture_view specular_glossiness_texture; + + cgltf_float diffuse_factor[4]; + cgltf_float specular_factor[3]; + cgltf_float glossiness_factor; +} cgltf_pbr_specular_glossiness; + +typedef struct cgltf_clearcoat +{ + cgltf_texture_view clearcoat_texture; + cgltf_texture_view clearcoat_roughness_texture; + cgltf_texture_view clearcoat_normal_texture; + + cgltf_float clearcoat_factor; + cgltf_float clearcoat_roughness_factor; +} cgltf_clearcoat; + +typedef struct cgltf_transmission +{ + cgltf_texture_view transmission_texture; + cgltf_float transmission_factor; +} cgltf_transmission; + +typedef struct cgltf_ior +{ + cgltf_float ior; +} cgltf_ior; + +typedef struct cgltf_specular +{ + cgltf_texture_view specular_texture; + cgltf_texture_view specular_color_texture; + cgltf_float specular_color_factor[3]; + cgltf_float specular_factor; +} cgltf_specular; + +typedef struct cgltf_volume +{ + cgltf_texture_view thickness_texture; + cgltf_float thickness_factor; + cgltf_float attenuation_color[3]; + cgltf_float attenuation_distance; +} cgltf_volume; + +typedef struct cgltf_sheen +{ + cgltf_texture_view sheen_color_texture; + cgltf_float sheen_color_factor[3]; + cgltf_texture_view sheen_roughness_texture; + cgltf_float sheen_roughness_factor; +} cgltf_sheen; + +typedef struct cgltf_emissive_strength +{ + cgltf_float emissive_strength; +} cgltf_emissive_strength; + +typedef struct cgltf_iridescence +{ + cgltf_float iridescence_factor; + cgltf_texture_view iridescence_texture; + cgltf_float iridescence_ior; + cgltf_float iridescence_thickness_min; + cgltf_float iridescence_thickness_max; + cgltf_texture_view iridescence_thickness_texture; +} cgltf_iridescence; + +typedef struct cgltf_anisotropy +{ + cgltf_float anisotropy_strength; + cgltf_float anisotropy_rotation; + cgltf_texture_view anisotropy_texture; +} cgltf_anisotropy; + +typedef struct cgltf_dispersion +{ + cgltf_float dispersion; +} cgltf_dispersion; + +typedef struct cgltf_material +{ + char* name; + cgltf_bool has_pbr_metallic_roughness; + cgltf_bool has_pbr_specular_glossiness; + cgltf_bool has_clearcoat; + cgltf_bool has_transmission; + cgltf_bool has_volume; + cgltf_bool has_ior; + cgltf_bool has_specular; + cgltf_bool has_sheen; + cgltf_bool has_emissive_strength; + cgltf_bool has_iridescence; + cgltf_bool has_anisotropy; + cgltf_bool has_dispersion; + cgltf_pbr_metallic_roughness pbr_metallic_roughness; + cgltf_pbr_specular_glossiness pbr_specular_glossiness; + cgltf_clearcoat clearcoat; + cgltf_ior ior; + cgltf_specular specular; + cgltf_sheen sheen; + cgltf_transmission transmission; + cgltf_volume volume; + cgltf_emissive_strength emissive_strength; + cgltf_iridescence iridescence; + cgltf_anisotropy anisotropy; + cgltf_dispersion dispersion; + cgltf_texture_view normal_texture; + cgltf_texture_view occlusion_texture; + cgltf_texture_view emissive_texture; + cgltf_float emissive_factor[3]; + cgltf_alpha_mode alpha_mode; + cgltf_float alpha_cutoff; + cgltf_bool double_sided; + cgltf_bool unlit; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_material; + +typedef struct cgltf_material_mapping +{ + cgltf_size variant; + cgltf_material* material; + cgltf_extras extras; +} cgltf_material_mapping; + +typedef struct cgltf_morph_target { + cgltf_attribute* attributes; + cgltf_size attributes_count; +} cgltf_morph_target; + +typedef struct cgltf_draco_mesh_compression { + cgltf_buffer_view* buffer_view; + cgltf_attribute* attributes; + cgltf_size attributes_count; +} cgltf_draco_mesh_compression; + +typedef struct cgltf_mesh_gpu_instancing { + cgltf_attribute* attributes; + cgltf_size attributes_count; +} cgltf_mesh_gpu_instancing; + +typedef struct cgltf_primitive { + cgltf_primitive_type type; + cgltf_accessor* indices; + cgltf_material* material; + cgltf_attribute* attributes; + cgltf_size attributes_count; + cgltf_morph_target* targets; + cgltf_size targets_count; + cgltf_extras extras; + cgltf_bool has_draco_mesh_compression; + cgltf_draco_mesh_compression draco_mesh_compression; + cgltf_material_mapping* mappings; + cgltf_size mappings_count; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_primitive; + +typedef struct cgltf_mesh { + char* name; + cgltf_primitive* primitives; + cgltf_size primitives_count; + cgltf_float* weights; + cgltf_size weights_count; + char** target_names; + cgltf_size target_names_count; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_mesh; + +typedef struct cgltf_node cgltf_node; + +typedef struct cgltf_skin { + char* name; + cgltf_node** joints; + cgltf_size joints_count; + cgltf_node* skeleton; + cgltf_accessor* inverse_bind_matrices; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_skin; + +typedef struct cgltf_camera_perspective { + cgltf_bool has_aspect_ratio; + cgltf_float aspect_ratio; + cgltf_float yfov; + cgltf_bool has_zfar; + cgltf_float zfar; + cgltf_float znear; + cgltf_extras extras; +} cgltf_camera_perspective; + +typedef struct cgltf_camera_orthographic { + cgltf_float xmag; + cgltf_float ymag; + cgltf_float zfar; + cgltf_float znear; + cgltf_extras extras; +} cgltf_camera_orthographic; + +typedef struct cgltf_camera { + char* name; + cgltf_camera_type type; + union { + cgltf_camera_perspective perspective; + cgltf_camera_orthographic orthographic; + } data; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_camera; + +typedef struct cgltf_light { + char* name; + cgltf_float color[3]; + cgltf_float intensity; + cgltf_light_type type; + cgltf_float range; + cgltf_float spot_inner_cone_angle; + cgltf_float spot_outer_cone_angle; + cgltf_extras extras; +} cgltf_light; + +struct cgltf_node { + char* name; + cgltf_node* parent; + cgltf_node** children; + cgltf_size children_count; + cgltf_skin* skin; + cgltf_mesh* mesh; + cgltf_camera* camera; + cgltf_light* light; + cgltf_float* weights; + cgltf_size weights_count; + cgltf_bool has_translation; + cgltf_bool has_rotation; + cgltf_bool has_scale; + cgltf_bool has_matrix; + cgltf_float translation[3]; + cgltf_float rotation[4]; + cgltf_float scale[3]; + cgltf_float matrix[16]; + cgltf_extras extras; + cgltf_bool has_mesh_gpu_instancing; + cgltf_mesh_gpu_instancing mesh_gpu_instancing; + cgltf_size extensions_count; + cgltf_extension* extensions; +}; + +typedef struct cgltf_scene { + char* name; + cgltf_node** nodes; + cgltf_size nodes_count; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_scene; + +typedef struct cgltf_animation_sampler { + cgltf_accessor* input; + cgltf_accessor* output; + cgltf_interpolation_type interpolation; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_animation_sampler; + +typedef struct cgltf_animation_channel { + cgltf_animation_sampler* sampler; + cgltf_node* target_node; + cgltf_animation_path_type target_path; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_animation_channel; + +typedef struct cgltf_animation { + char* name; + cgltf_animation_sampler* samplers; + cgltf_size samplers_count; + cgltf_animation_channel* channels; + cgltf_size channels_count; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_animation; + +typedef struct cgltf_material_variant +{ + char* name; + cgltf_extras extras; +} cgltf_material_variant; + +typedef struct cgltf_asset { + char* copyright; + char* generator; + char* version; + char* min_version; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_asset; + +typedef struct cgltf_data +{ + cgltf_file_type file_type; + void* file_data; + + cgltf_asset asset; + + cgltf_mesh* meshes; + cgltf_size meshes_count; + + cgltf_material* materials; + cgltf_size materials_count; + + cgltf_accessor* accessors; + cgltf_size accessors_count; + + cgltf_buffer_view* buffer_views; + cgltf_size buffer_views_count; + + cgltf_buffer* buffers; + cgltf_size buffers_count; + + cgltf_image* images; + cgltf_size images_count; + + cgltf_texture* textures; + cgltf_size textures_count; + + cgltf_sampler* samplers; + cgltf_size samplers_count; + + cgltf_skin* skins; + cgltf_size skins_count; + + cgltf_camera* cameras; + cgltf_size cameras_count; + + cgltf_light* lights; + cgltf_size lights_count; + + cgltf_node* nodes; + cgltf_size nodes_count; + + cgltf_scene* scenes; + cgltf_size scenes_count; + + cgltf_scene* scene; + + cgltf_animation* animations; + cgltf_size animations_count; + + cgltf_material_variant* variants; + cgltf_size variants_count; + + cgltf_extras extras; + + cgltf_size data_extensions_count; + cgltf_extension* data_extensions; + + char** extensions_used; + cgltf_size extensions_used_count; + + char** extensions_required; + cgltf_size extensions_required_count; + + const char* json; + cgltf_size json_size; + + const void* bin; + cgltf_size bin_size; + + cgltf_memory_options memory; + cgltf_file_options file; +} cgltf_data; + +cgltf_result cgltf_parse( + const cgltf_options* options, + const void* data, + cgltf_size size, + cgltf_data** out_data); + +cgltf_result cgltf_parse_file( + const cgltf_options* options, + const char* path, + cgltf_data** out_data); + +cgltf_result cgltf_load_buffers( + const cgltf_options* options, + cgltf_data* data, + const char* gltf_path); + +cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data); + +cgltf_size cgltf_decode_string(char* string); +cgltf_size cgltf_decode_uri(char* uri); + +cgltf_result cgltf_validate(cgltf_data* data); + +void cgltf_free(cgltf_data* data); + +void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix); +void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix); + +const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view); + +cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size); +cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size); +cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index); + +cgltf_size cgltf_num_components(cgltf_type type); +cgltf_size cgltf_component_size(cgltf_component_type component_type); +cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type); + +cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count); +cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* out, cgltf_size out_component_size, cgltf_size index_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); + +cgltf_size cgltf_mesh_index(const cgltf_data* data, const cgltf_mesh* object); +cgltf_size cgltf_material_index(const cgltf_data* data, const cgltf_material* object); +cgltf_size cgltf_accessor_index(const cgltf_data* data, const cgltf_accessor* object); +cgltf_size cgltf_buffer_view_index(const cgltf_data* data, const cgltf_buffer_view* object); +cgltf_size cgltf_buffer_index(const cgltf_data* data, const cgltf_buffer* object); +cgltf_size cgltf_image_index(const cgltf_data* data, const cgltf_image* object); +cgltf_size cgltf_texture_index(const cgltf_data* data, const cgltf_texture* object); +cgltf_size cgltf_sampler_index(const cgltf_data* data, const cgltf_sampler* object); +cgltf_size cgltf_skin_index(const cgltf_data* data, const cgltf_skin* object); +cgltf_size cgltf_camera_index(const cgltf_data* data, const cgltf_camera* object); +cgltf_size cgltf_light_index(const cgltf_data* data, const cgltf_light* object); +cgltf_size cgltf_node_index(const cgltf_data* data, const cgltf_node* object); +cgltf_size cgltf_scene_index(const cgltf_data* data, const cgltf_scene* object); +cgltf_size cgltf_animation_index(const cgltf_data* data, const cgltf_animation* object); +cgltf_size cgltf_animation_sampler_index(const cgltf_animation* animation, const cgltf_animation_sampler* object); +cgltf_size cgltf_animation_channel_index(const cgltf_animation* animation, const cgltf_animation_channel* object); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef CGLTF_H_INCLUDED__ */ + +/* + * + * Stop now, if you are only interested in the API. + * Below, you find the implementation. + * + */ + +#if defined(__INTELLISENSE__) || defined(__JETBRAINS_IDE__) +/* This makes MSVC/CLion intellisense work. */ +#define CGLTF_IMPLEMENTATION +#endif + +#ifdef CGLTF_IMPLEMENTATION + +#include /* For assert */ +#include /* For strncpy */ +#include /* For fopen */ +#include /* For UINT_MAX etc */ +#include /* For FLT_MAX */ + +#if !defined(CGLTF_MALLOC) || !defined(CGLTF_FREE) || !defined(CGLTF_ATOI) || !defined(CGLTF_ATOF) || !defined(CGLTF_ATOLL) +#include /* For malloc, free, atoi, atof */ +#endif + +/* JSMN_PARENT_LINKS is necessary to make parsing large structures linear in input size */ +#define JSMN_PARENT_LINKS + +/* JSMN_STRICT is necessary to reject invalid JSON documents */ +#define JSMN_STRICT + +/* + * -- jsmn.h start -- + * Source: https://github.com/zserge/jsmn + * License: MIT + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; +typedef struct { + jsmntype_t type; + ptrdiff_t start; + ptrdiff_t end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; +typedef struct { + size_t pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser; +static void jsmn_init(jsmn_parser *parser); +static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens); +/* + * -- jsmn.h end -- + */ + + +#ifndef CGLTF_CONSTS +#define GlbHeaderSize 12 +#define 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) +#endif +#ifndef CGLTF_FREE +#define CGLTF_FREE(ptr) free(ptr) +#endif +#ifndef CGLTF_ATOI +#define CGLTF_ATOI(str) atoi(str) +#endif +#ifndef CGLTF_ATOF +#define CGLTF_ATOF(str) atof(str) +#endif +#ifndef CGLTF_ATOLL +#define CGLTF_ATOLL(str) atoll(str) +#endif +#ifndef CGLTF_VALIDATE_ENABLE_ASSERTS +#define CGLTF_VALIDATE_ENABLE_ASSERTS 0 +#endif + +static void* cgltf_default_alloc(void* user, cgltf_size size) +{ + (void)user; + return CGLTF_MALLOC(size); +} + +static void cgltf_default_free(void* user, void* ptr) +{ + (void)user; + CGLTF_FREE(ptr); +} + +static void* cgltf_calloc(cgltf_options* options, size_t element_size, cgltf_size count) +{ + if (SIZE_MAX / element_size < count) + { + return NULL; + } + void* result = options->memory.alloc_func(options->memory.user_data, element_size * count); + if (!result) + { + return NULL; + } + memset(result, 0, element_size * count); + return result; +} + +static 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) +{ + (void)file_options; + void* (*memory_alloc)(void*, cgltf_size) = memory_options->alloc_func ? memory_options->alloc_func : &cgltf_default_alloc; + void (*memory_free)(void*, void*) = memory_options->free_func ? memory_options->free_func : &cgltf_default_free; + + FILE* file = fopen(path, "rb"); + if (!file) + { + return cgltf_result_file_not_found; + } + + cgltf_size file_size = size ? *size : 0; + + if (file_size == 0) + { + fseek(file, 0, SEEK_END); + +#ifdef _MSC_VER + __int64 length = _ftelli64(file); +#else + long length = ftell(file); +#endif + + if (length < 0) + { + fclose(file); + return cgltf_result_io_error; + } + + fseek(file, 0, SEEK_SET); + file_size = (cgltf_size)length; + } + + char* file_data = (char*)memory_alloc(memory_options->user_data, file_size); + if (!file_data) + { + fclose(file); + return cgltf_result_out_of_memory; + } + + cgltf_size read_size = fread(file_data, 1, file_size, file); + + fclose(file); + + if (read_size != file_size) + { + memory_free(memory_options->user_data, file_data); + return cgltf_result_io_error; + } + + if (size) + { + *size = file_size; + } + if (data) + { + *data = file_data; + } + + return cgltf_result_success; +} + +static void cgltf_default_file_release(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data) +{ + (void)file_options; + void (*memfree)(void*, void*) = memory_options->free_func ? memory_options->free_func : &cgltf_default_free; + memfree(memory_options->user_data, data); +} + +static cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data** out_data); + +cgltf_result cgltf_parse(const cgltf_options* options, const void* data, cgltf_size size, cgltf_data** out_data) +{ + if (size < GlbHeaderSize) + { + return cgltf_result_data_too_short; + } + + if (options == NULL) + { + return cgltf_result_invalid_options; + } + + cgltf_options fixed_options = *options; + if (fixed_options.memory.alloc_func == NULL) + { + fixed_options.memory.alloc_func = &cgltf_default_alloc; + } + if (fixed_options.memory.free_func == NULL) + { + fixed_options.memory.free_func = &cgltf_default_free; + } + + uint32_t tmp; + // Magic + memcpy(&tmp, data, 4); + if (tmp != GlbMagic) + { + if (fixed_options.type == cgltf_file_type_invalid) + { + fixed_options.type = cgltf_file_type_gltf; + } + else if (fixed_options.type == cgltf_file_type_glb) + { + return cgltf_result_unknown_format; + } + } + + if (fixed_options.type == cgltf_file_type_gltf) + { + cgltf_result json_result = cgltf_parse_json(&fixed_options, (const uint8_t*)data, size, out_data); + if (json_result != cgltf_result_success) + { + return json_result; + } + + (*out_data)->file_type = cgltf_file_type_gltf; + + return cgltf_result_success; + } + + const uint8_t* ptr = (const uint8_t*)data; + // Version + memcpy(&tmp, ptr + 4, 4); + uint32_t version = tmp; + if (version != GlbVersion) + { + return version < GlbVersion ? cgltf_result_legacy_gltf : cgltf_result_unknown_format; + } + + // Total length + memcpy(&tmp, ptr + 8, 4); + if (tmp > size) + { + return cgltf_result_data_too_short; + } + + const uint8_t* json_chunk = ptr + GlbHeaderSize; + + if (GlbHeaderSize + GlbChunkHeaderSize > size) + { + return cgltf_result_data_too_short; + } + + // JSON chunk: length + uint32_t json_length; + memcpy(&json_length, json_chunk, 4); + if (json_length > size - GlbHeaderSize - GlbChunkHeaderSize) + { + return cgltf_result_data_too_short; + } + + // JSON chunk: magic + memcpy(&tmp, json_chunk + 4, 4); + if (tmp != GlbMagicJsonChunk) + { + return cgltf_result_unknown_format; + } + + json_chunk += GlbChunkHeaderSize; + + const void* bin = NULL; + cgltf_size bin_size = 0; + + if (GlbChunkHeaderSize <= size - GlbHeaderSize - GlbChunkHeaderSize - json_length) + { + // We can read another chunk + const uint8_t* bin_chunk = json_chunk + json_length; + + // Bin chunk: length + uint32_t bin_length; + memcpy(&bin_length, bin_chunk, 4); + if (bin_length > size - GlbHeaderSize - GlbChunkHeaderSize - json_length - GlbChunkHeaderSize) + { + return cgltf_result_data_too_short; + } + + // Bin chunk: magic + memcpy(&tmp, bin_chunk + 4, 4); + if (tmp != GlbMagicBinChunk) + { + return cgltf_result_unknown_format; + } + + bin_chunk += GlbChunkHeaderSize; + + bin = bin_chunk; + bin_size = bin_length; + } + + cgltf_result json_result = cgltf_parse_json(&fixed_options, json_chunk, json_length, out_data); + if (json_result != cgltf_result_success) + { + return json_result; + } + + (*out_data)->file_type = cgltf_file_type_glb; + (*out_data)->bin = bin; + (*out_data)->bin_size = bin_size; + + return cgltf_result_success; +} + +cgltf_result cgltf_parse_file(const cgltf_options* options, const char* path, cgltf_data** out_data) +{ + if (options == NULL) + { + return cgltf_result_invalid_options; + } + + 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; + void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = options->file.release ? options->file.release : cgltf_default_file_release; + + void* file_data = NULL; + cgltf_size file_size = 0; + cgltf_result result = file_read(&options->memory, &options->file, path, &file_size, &file_data); + if (result != cgltf_result_success) + { + return result; + } + + result = cgltf_parse(options, file_data, file_size, out_data); + + if (result != cgltf_result_success) + { + file_release(&options->memory, &options->file, file_data); + return result; + } + + (*out_data)->file_data = file_data; + + return cgltf_result_success; +} + +static void cgltf_combine_paths(char* path, const char* base, const char* uri) +{ + const char* s0 = strrchr(base, '/'); + const char* s1 = strrchr(base, '\\'); + const char* slash = s0 ? (s1 && s1 > s0 ? s1 : s0) : s1; + + if (slash) + { + size_t prefix = slash - base + 1; + + strncpy(path, base, prefix); + strcpy(path + prefix, uri); + } + else + { + strcpy(path, uri); + } +} + +static cgltf_result cgltf_load_buffer_file(const cgltf_options* options, cgltf_size size, const char* uri, const char* gltf_path, void** out_data) +{ + void* (*memory_alloc)(void*, cgltf_size) = options->memory.alloc_func ? options->memory.alloc_func : &cgltf_default_alloc; + void (*memory_free)(void*, void*) = options->memory.free_func ? options->memory.free_func : &cgltf_default_free; + 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; + + char* path = (char*)memory_alloc(options->memory.user_data, strlen(uri) + strlen(gltf_path) + 1); + if (!path) + { + return cgltf_result_out_of_memory; + } + + cgltf_combine_paths(path, gltf_path, uri); + + // after combining, the tail of the resulting path is a uri; decode_uri converts it into path + cgltf_decode_uri(path + strlen(path) - strlen(uri)); + + void* file_data = NULL; + cgltf_result result = file_read(&options->memory, &options->file, path, &size, &file_data); + + memory_free(options->memory.user_data, path); + + *out_data = (result == cgltf_result_success) ? file_data : NULL; + + return result; +} + +cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data) +{ + void* (*memory_alloc)(void*, cgltf_size) = options->memory.alloc_func ? options->memory.alloc_func : &cgltf_default_alloc; + void (*memory_free)(void*, void*) = options->memory.free_func ? options->memory.free_func : &cgltf_default_free; + + unsigned char* data = (unsigned char*)memory_alloc(options->memory.user_data, size); + if (!data) + { + return cgltf_result_out_of_memory; + } + + unsigned int buffer = 0; + unsigned int buffer_bits = 0; + + for (cgltf_size i = 0; i < size; ++i) + { + while (buffer_bits < 8) + { + char ch = *base64++; + + int index = + (unsigned)(ch - 'A') < 26 ? (ch - 'A') : + (unsigned)(ch - 'a') < 26 ? (ch - 'a') + 26 : + (unsigned)(ch - '0') < 10 ? (ch - '0') + 52 : + ch == '+' ? 62 : + ch == '/' ? 63 : + -1; + + if (index < 0) + { + memory_free(options->memory.user_data, data); + return cgltf_result_io_error; + } + + buffer = (buffer << 6) | index; + buffer_bits += 6; + } + + data[i] = (unsigned char)(buffer >> (buffer_bits - 8)); + buffer_bits -= 8; + } + + *out_data = data; + + return cgltf_result_success; +} + +static int cgltf_unhex(char ch) +{ + return + (unsigned)(ch - '0') < 10 ? (ch - '0') : + (unsigned)(ch - 'A') < 6 ? (ch - 'A') + 10 : + (unsigned)(ch - 'a') < 6 ? (ch - 'a') + 10 : + -1; +} + +cgltf_size cgltf_decode_string(char* string) +{ + char* read = string + strcspn(string, "\\"); + if (*read == 0) + { + return read - string; + } + char* write = string; + char* last = string; + + for (;;) + { + // Copy characters since last escaped sequence + cgltf_size written = read - last; + memmove(write, last, written); + write += written; + + if (*read++ == 0) + { + break; + } + + // jsmn already checked that all escape sequences are valid + switch (*read++) + { + case '\"': *write++ = '\"'; break; + case '/': *write++ = '/'; break; + case '\\': *write++ = '\\'; break; + case 'b': *write++ = '\b'; break; + case 'f': *write++ = '\f'; break; + case 'r': *write++ = '\r'; break; + case 'n': *write++ = '\n'; break; + case 't': *write++ = '\t'; break; + case 'u': + { + // UCS-2 codepoint \uXXXX to UTF-8 + int character = 0; + for (cgltf_size i = 0; i < 4; ++i) + { + character = (character << 4) + cgltf_unhex(*read++); + } + + if (character <= 0x7F) + { + *write++ = character & 0xFF; + } + else if (character <= 0x7FF) + { + *write++ = 0xC0 | ((character >> 6) & 0xFF); + *write++ = 0x80 | (character & 0x3F); + } + else + { + *write++ = 0xE0 | ((character >> 12) & 0xFF); + *write++ = 0x80 | ((character >> 6) & 0x3F); + *write++ = 0x80 | (character & 0x3F); + } + break; + } + default: + break; + } + + last = read; + read += strcspn(read, "\\"); + } + + *write = 0; + return write - string; +} + +cgltf_size cgltf_decode_uri(char* uri) +{ + char* write = uri; + char* i = uri; + + while (*i) + { + if (*i == '%') + { + int ch1 = cgltf_unhex(i[1]); + + if (ch1 >= 0) + { + int ch2 = cgltf_unhex(i[2]); + + if (ch2 >= 0) + { + *write++ = (char)(ch1 * 16 + ch2); + i += 3; + continue; + } + } + } + + *write++ = *i++; + } + + *write = 0; + return write - uri; +} + +cgltf_result cgltf_load_buffers(const cgltf_options* options, cgltf_data* data, const char* gltf_path) +{ + if (options == NULL) + { + return cgltf_result_invalid_options; + } + + if (data->buffers_count && data->buffers[0].data == NULL && data->buffers[0].uri == NULL && data->bin) + { + if (data->bin_size < data->buffers[0].size) + { + return cgltf_result_data_too_short; + } + + data->buffers[0].data = (void*)data->bin; + data->buffers[0].data_free_method = cgltf_data_free_method_none; + } + + for (cgltf_size i = 0; i < data->buffers_count; ++i) + { + if (data->buffers[i].data) + { + continue; + } + + const char* uri = data->buffers[i].uri; + + if (uri == NULL) + { + continue; + } + + if (strncmp(uri, "data:", 5) == 0) + { + const char* comma = strchr(uri, ','); + + if (comma && comma - uri >= 7 && strncmp(comma - 7, ";base64", 7) == 0) + { + cgltf_result res = cgltf_load_buffer_base64(options, data->buffers[i].size, comma + 1, &data->buffers[i].data); + data->buffers[i].data_free_method = cgltf_data_free_method_memory_free; + + if (res != cgltf_result_success) + { + return res; + } + } + else + { + return cgltf_result_unknown_format; + } + } + else if (strstr(uri, "://") == NULL && gltf_path) + { + cgltf_result res = cgltf_load_buffer_file(options, data->buffers[i].size, uri, gltf_path, &data->buffers[i].data); + data->buffers[i].data_free_method = cgltf_data_free_method_file_release; + + if (res != cgltf_result_success) + { + return res; + } + } + else + { + return cgltf_result_unknown_format; + } + } + + return cgltf_result_success; +} + +static cgltf_size cgltf_calc_index_bound(cgltf_buffer_view* buffer_view, cgltf_size offset, cgltf_component_type component_type, cgltf_size count) +{ + char* data = (char*)buffer_view->buffer->data + offset + buffer_view->offset; + cgltf_size bound = 0; + + switch (component_type) + { + case cgltf_component_type_r_8u: + for (size_t i = 0; i < count; ++i) + { + cgltf_size v = ((unsigned char*)data)[i]; + bound = bound > v ? bound : v; + } + break; + + case cgltf_component_type_r_16u: + for (size_t i = 0; i < count; ++i) + { + cgltf_size v = ((unsigned short*)data)[i]; + bound = bound > v ? bound : v; + } + break; + + case cgltf_component_type_r_32u: + for (size_t i = 0; i < count; ++i) + { + cgltf_size v = ((unsigned int*)data)[i]; + bound = bound > v ? bound : v; + } + break; + + default: + ; + } + + return bound; +} + +#if CGLTF_VALIDATE_ENABLE_ASSERTS +#define CGLTF_ASSERT_IF(cond, result) assert(!(cond)); if (cond) return result; +#else +#define CGLTF_ASSERT_IF(cond, result) if (cond) return result; +#endif + +cgltf_result cgltf_validate(cgltf_data* data) +{ + for (cgltf_size i = 0; i < data->accessors_count; ++i) + { + cgltf_accessor* accessor = &data->accessors[i]; + + CGLTF_ASSERT_IF(data->accessors[i].component_type == cgltf_component_type_invalid, cgltf_result_invalid_gltf); + CGLTF_ASSERT_IF(data->accessors[i].type == cgltf_type_invalid, cgltf_result_invalid_gltf); + + cgltf_size element_size = cgltf_calc_size(accessor->type, accessor->component_type); + + if (accessor->buffer_view) + { + cgltf_size req_size = accessor->offset + accessor->stride * (accessor->count - 1) + element_size; + + CGLTF_ASSERT_IF(accessor->buffer_view->size < req_size, cgltf_result_data_too_short); + } + + if (accessor->is_sparse) + { + cgltf_accessor_sparse* sparse = &accessor->sparse; + + cgltf_size indices_component_size = cgltf_component_size(sparse->indices_component_type); + cgltf_size indices_req_size = sparse->indices_byte_offset + indices_component_size * sparse->count; + cgltf_size values_req_size = sparse->values_byte_offset + element_size * sparse->count; + + CGLTF_ASSERT_IF(sparse->indices_buffer_view->size < indices_req_size || + sparse->values_buffer_view->size < values_req_size, cgltf_result_data_too_short); + + CGLTF_ASSERT_IF(sparse->indices_component_type != cgltf_component_type_r_8u && + sparse->indices_component_type != cgltf_component_type_r_16u && + sparse->indices_component_type != cgltf_component_type_r_32u, cgltf_result_invalid_gltf); + + if (sparse->indices_buffer_view->buffer->data) + { + cgltf_size index_bound = cgltf_calc_index_bound(sparse->indices_buffer_view, sparse->indices_byte_offset, sparse->indices_component_type, sparse->count); + + CGLTF_ASSERT_IF(index_bound >= accessor->count, cgltf_result_data_too_short); + } + } + } + + for (cgltf_size i = 0; i < data->buffer_views_count; ++i) + { + cgltf_size req_size = data->buffer_views[i].offset + data->buffer_views[i].size; + + CGLTF_ASSERT_IF(data->buffer_views[i].buffer && data->buffer_views[i].buffer->size < req_size, cgltf_result_data_too_short); + + if (data->buffer_views[i].has_meshopt_compression) + { + cgltf_meshopt_compression* mc = &data->buffer_views[i].meshopt_compression; + + CGLTF_ASSERT_IF(mc->buffer == NULL || mc->buffer->size < mc->offset + mc->size, cgltf_result_data_too_short); + + CGLTF_ASSERT_IF(data->buffer_views[i].stride && mc->stride != data->buffer_views[i].stride, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(data->buffer_views[i].size != mc->stride * mc->count, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_invalid, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_attributes && !(mc->stride % 4 == 0 && mc->stride <= 256), cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_triangles && mc->count % 3 != 0, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF((mc->mode == cgltf_meshopt_compression_mode_triangles || mc->mode == cgltf_meshopt_compression_mode_indices) && mc->stride != 2 && mc->stride != 4, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF((mc->mode == cgltf_meshopt_compression_mode_triangles || mc->mode == cgltf_meshopt_compression_mode_indices) && mc->filter != cgltf_meshopt_compression_filter_none, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_octahedral && mc->stride != 4 && mc->stride != 8, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_quaternion && mc->stride != 8, cgltf_result_invalid_gltf); + } + } + + for (cgltf_size i = 0; i < data->meshes_count; ++i) + { + if (data->meshes[i].weights) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives_count && data->meshes[i].primitives[0].targets_count != data->meshes[i].weights_count, cgltf_result_invalid_gltf); + } + + if (data->meshes[i].target_names) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives_count && data->meshes[i].primitives[0].targets_count != data->meshes[i].target_names_count, cgltf_result_invalid_gltf); + } + + for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].type == cgltf_primitive_type_invalid, cgltf_result_invalid_gltf); + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].targets_count != data->meshes[i].primitives[0].targets_count, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].attributes_count == 0, cgltf_result_invalid_gltf); + + cgltf_accessor* first = data->meshes[i].primitives[j].attributes[0].data; + + CGLTF_ASSERT_IF(first->count == 0, cgltf_result_invalid_gltf); + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].attributes[k].data->count != first->count, cgltf_result_invalid_gltf); + } + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) + { + for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].targets[k].attributes[m].data->count != first->count, cgltf_result_invalid_gltf); + } + } + + cgltf_accessor* indices = data->meshes[i].primitives[j].indices; + + CGLTF_ASSERT_IF(indices && + indices->component_type != cgltf_component_type_r_8u && + indices->component_type != cgltf_component_type_r_16u && + indices->component_type != cgltf_component_type_r_32u, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(indices && indices->type != cgltf_type_scalar, cgltf_result_invalid_gltf); + CGLTF_ASSERT_IF(indices && indices->stride != cgltf_component_size(indices->component_type), cgltf_result_invalid_gltf); + + if (indices && indices->buffer_view && indices->buffer_view->buffer->data) + { + cgltf_size index_bound = cgltf_calc_index_bound(indices->buffer_view, indices->offset, indices->component_type, indices->count); + + CGLTF_ASSERT_IF(index_bound >= first->count, cgltf_result_data_too_short); + } + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].mappings[k].variant >= data->variants_count, cgltf_result_invalid_gltf); + } + } + } + + for (cgltf_size i = 0; i < data->nodes_count; ++i) + { + if (data->nodes[i].weights && data->nodes[i].mesh) + { + CGLTF_ASSERT_IF(data->nodes[i].mesh->primitives_count && data->nodes[i].mesh->primitives[0].targets_count != data->nodes[i].weights_count, cgltf_result_invalid_gltf); + } + + if (data->nodes[i].has_mesh_gpu_instancing) + { + CGLTF_ASSERT_IF(data->nodes[i].mesh == NULL, cgltf_result_invalid_gltf); + CGLTF_ASSERT_IF(data->nodes[i].mesh_gpu_instancing.attributes_count == 0, cgltf_result_invalid_gltf); + + cgltf_accessor* first = data->nodes[i].mesh_gpu_instancing.attributes[0].data; + + for (cgltf_size k = 0; k < data->nodes[i].mesh_gpu_instancing.attributes_count; ++k) + { + CGLTF_ASSERT_IF(data->nodes[i].mesh_gpu_instancing.attributes[k].data->count != first->count, cgltf_result_invalid_gltf); + } + } + } + + for (cgltf_size i = 0; i < data->nodes_count; ++i) + { + cgltf_node* p1 = data->nodes[i].parent; + cgltf_node* p2 = p1 ? p1->parent : NULL; + + while (p1 && p2) + { + CGLTF_ASSERT_IF(p1 == p2, cgltf_result_invalid_gltf); + + p1 = p1->parent; + p2 = p2->parent ? p2->parent->parent : NULL; + } + } + + for (cgltf_size i = 0; i < data->scenes_count; ++i) + { + for (cgltf_size j = 0; j < data->scenes[i].nodes_count; ++j) + { + CGLTF_ASSERT_IF(data->scenes[i].nodes[j]->parent, cgltf_result_invalid_gltf); + } + } + + for (cgltf_size i = 0; i < data->animations_count; ++i) + { + for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) + { + cgltf_animation_channel* channel = &data->animations[i].channels[j]; + + if (!channel->target_node) + { + continue; + } + + cgltf_size components = 1; + + if (channel->target_path == cgltf_animation_path_type_weights) + { + CGLTF_ASSERT_IF(!channel->target_node->mesh || !channel->target_node->mesh->primitives_count, cgltf_result_invalid_gltf); + + components = channel->target_node->mesh->primitives[0].targets_count; + } + + cgltf_size values = channel->sampler->interpolation == cgltf_interpolation_type_cubic_spline ? 3 : 1; + + CGLTF_ASSERT_IF(channel->sampler->input->count * components * values != channel->sampler->output->count, cgltf_result_invalid_gltf); + } + } + + for (cgltf_size i = 0; i < data->variants_count; ++i) + { + CGLTF_ASSERT_IF(!data->variants[i].name, cgltf_result_invalid_gltf); + } + + return cgltf_result_success; +} + +cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size) +{ + cgltf_size json_size = extras->end_offset - extras->start_offset; + + if (!dest) + { + if (dest_size) + { + *dest_size = json_size + 1; + return cgltf_result_success; + } + return cgltf_result_invalid_options; + } + + if (*dest_size + 1 < json_size) + { + strncpy(dest, data->json + extras->start_offset, *dest_size - 1); + dest[*dest_size - 1] = 0; + } + else + { + strncpy(dest, data->json + extras->start_offset, json_size); + dest[json_size] = 0; + } + + return cgltf_result_success; +} + +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) + { + data->memory.free_func(data->memory.user_data, extensions[i].name); + data->memory.free_func(data->memory.user_data, extensions[i].data); + } + data->memory.free_func(data->memory.user_data, extensions); +} + +void cgltf_free(cgltf_data* data) +{ + if (!data) + { + return; + } + + 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; + + data->memory.free_func(data->memory.user_data, data->asset.copyright); + data->memory.free_func(data->memory.user_data, data->asset.generator); + data->memory.free_func(data->memory.user_data, data->asset.version); + 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) + { + data->memory.free_func(data->memory.user_data, data->accessors[i].name); + + 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); + + for (cgltf_size i = 0; i < data->buffer_views_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->buffer_views[i].name); + 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); + + for (cgltf_size i = 0; i < data->buffers_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->buffers[i].name); + + if (data->buffers[i].data_free_method == cgltf_data_free_method_file_release) + { + file_release(&data->memory, &data->file, data->buffers[i].data); + } + else if (data->buffers[i].data_free_method == cgltf_data_free_method_memory_free) + { + data->memory.free_func(data->memory.user_data, data->buffers[i].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) + { + data->memory.free_func(data->memory.user_data, data->meshes[i].name); + + for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) + { + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) + { + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].attributes[k].name); + } + + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].attributes); + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) + { + for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) + { + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].targets[k].attributes[m].name); + } + + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].targets[k].attributes); + } + + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].targets); + + if (data->meshes[i].primitives[j].has_draco_mesh_compression) + { + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].draco_mesh_compression.attributes_count; ++k) + { + data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes[k].name); + } + + 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); + data->memory.free_func(data->memory.user_data, data->meshes[i].weights); + + for (cgltf_size j = 0; j < data->meshes[i].target_names_count; ++j) + { + data->memory.free_func(data->memory.user_data, data->meshes[i].target_names[j]); + } + + 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); + } + + data->memory.free_func(data->memory.user_data, data->meshes); + + for (cgltf_size i = 0; i < data->materials_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->materials[i].name); + + 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); + + for (cgltf_size i = 0; i < data->images_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->images[i].name); + data->memory.free_func(data->memory.user_data, data->images[i].uri); + 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); + + 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); + + 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); + + for (cgltf_size i = 0; i < data->skins_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->skins[i].name); + 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); + + 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); + + 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); + + for (cgltf_size i = 0; i < data->nodes_count; ++i) + { + 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); + + for (cgltf_size i = 0; i < data->scenes_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->scenes[i].name); + 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); + + for (cgltf_size i = 0; i < data->animations_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->animations[i].name); + 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); + + 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) + { + data->memory.free_func(data->memory.user_data, data->extensions_used[i]); + } + + data->memory.free_func(data->memory.user_data, data->extensions_used); + + for (cgltf_size i = 0; i < data->extensions_required_count; ++i) + { + data->memory.free_func(data->memory.user_data, data->extensions_required[i]); + } + + data->memory.free_func(data->memory.user_data, data->extensions_required); + + file_release(&data->memory, &data->file, data->file_data); + + data->memory.free_func(data->memory.user_data, data); +} + +void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix) +{ + cgltf_float* lm = out_matrix; + + if (node->has_matrix) + { + memcpy(lm, node->matrix, sizeof(float) * 16); + } + else + { + float tx = node->translation[0]; + float ty = node->translation[1]; + float tz = node->translation[2]; + + float qx = node->rotation[0]; + float qy = node->rotation[1]; + float qz = node->rotation[2]; + float qw = node->rotation[3]; + + float sx = node->scale[0]; + float sy = node->scale[1]; + float sz = node->scale[2]; + + lm[0] = (1 - 2 * qy*qy - 2 * qz*qz) * sx; + lm[1] = (2 * qx*qy + 2 * qz*qw) * sx; + lm[2] = (2 * qx*qz - 2 * qy*qw) * sx; + lm[3] = 0.f; + + lm[4] = (2 * qx*qy - 2 * qz*qw) * sy; + lm[5] = (1 - 2 * qx*qx - 2 * qz*qz) * sy; + lm[6] = (2 * qy*qz + 2 * qx*qw) * sy; + lm[7] = 0.f; + + lm[8] = (2 * qx*qz + 2 * qy*qw) * sz; + lm[9] = (2 * qy*qz - 2 * qx*qw) * sz; + lm[10] = (1 - 2 * qx*qx - 2 * qy*qy) * sz; + lm[11] = 0.f; + + lm[12] = tx; + lm[13] = ty; + lm[14] = tz; + lm[15] = 1.f; + } +} + +void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix) +{ + cgltf_float* lm = out_matrix; + cgltf_node_transform_local(node, lm); + + const cgltf_node* parent = node->parent; + + while (parent) + { + float pm[16]; + cgltf_node_transform_local(parent, pm); + + for (int i = 0; i < 4; ++i) + { + float l0 = lm[i * 4 + 0]; + float l1 = lm[i * 4 + 1]; + float l2 = lm[i * 4 + 2]; + + float r0 = l0 * pm[0] + l1 * pm[4] + l2 * pm[8]; + float r1 = l0 * pm[1] + l1 * pm[5] + l2 * pm[9]; + float r2 = l0 * pm[2] + l1 * pm[6] + l2 * pm[10]; + + lm[i * 4 + 0] = r0; + lm[i * 4 + 1] = r1; + lm[i * 4 + 2] = r2; + } + + lm[12] += pm[12]; + lm[13] += pm[13]; + lm[14] += pm[14]; + + parent = parent->parent; + } +} + +static cgltf_ssize cgltf_component_read_integer(const void* in, cgltf_component_type component_type) +{ + switch (component_type) + { + case cgltf_component_type_r_16: + return *((const int16_t*) in); + case cgltf_component_type_r_16u: + return *((const uint16_t*) in); + case cgltf_component_type_r_32u: + return *((const uint32_t*) in); + case cgltf_component_type_r_8: + return *((const int8_t*) in); + case cgltf_component_type_r_8u: + return *((const uint8_t*) in); + default: + return 0; + } +} + +static cgltf_size cgltf_component_read_index(const void* in, cgltf_component_type component_type) +{ + switch (component_type) + { + case cgltf_component_type_r_16u: + return *((const uint16_t*) in); + case cgltf_component_type_r_32u: + return *((const uint32_t*) in); + case cgltf_component_type_r_8u: + return *((const uint8_t*) in); + default: + return 0; + } +} + +static cgltf_float cgltf_component_read_float(const void* in, cgltf_component_type component_type, cgltf_bool normalized) +{ + if (component_type == cgltf_component_type_r_32f) + { + return *((const float*) in); + } + + if (normalized) + { + switch (component_type) + { + // note: glTF spec doesn't currently define normalized conversions for 32-bit integers + case cgltf_component_type_r_16: + return *((const int16_t*) in) / (cgltf_float)32767; + case cgltf_component_type_r_16u: + return *((const uint16_t*) in) / (cgltf_float)65535; + case cgltf_component_type_r_8: + return *((const int8_t*) in) / (cgltf_float)127; + case cgltf_component_type_r_8u: + return *((const uint8_t*) in) / (cgltf_float)255; + default: + return 0; + } + } + + return (cgltf_float)cgltf_component_read_integer(in, component_type); +} + +static 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) +{ + cgltf_size num_components = cgltf_num_components(type); + + if (element_size < num_components) { + return 0; + } + + // There are three special cases for component extraction, see #data-alignment in the 2.0 spec. + + cgltf_size component_size = cgltf_component_size(component_type); + + if (type == cgltf_type_mat2 && component_size == 1) + { + out[0] = cgltf_component_read_float(element, component_type, normalized); + out[1] = cgltf_component_read_float(element + 1, component_type, normalized); + out[2] = cgltf_component_read_float(element + 4, component_type, normalized); + out[3] = cgltf_component_read_float(element + 5, component_type, normalized); + return 1; + } + + if (type == cgltf_type_mat3 && component_size == 1) + { + out[0] = cgltf_component_read_float(element, component_type, normalized); + out[1] = cgltf_component_read_float(element + 1, component_type, normalized); + out[2] = cgltf_component_read_float(element + 2, component_type, normalized); + out[3] = cgltf_component_read_float(element + 4, component_type, normalized); + out[4] = cgltf_component_read_float(element + 5, component_type, normalized); + out[5] = cgltf_component_read_float(element + 6, component_type, normalized); + out[6] = cgltf_component_read_float(element + 8, component_type, normalized); + out[7] = cgltf_component_read_float(element + 9, component_type, normalized); + out[8] = cgltf_component_read_float(element + 10, component_type, normalized); + return 1; + } + + if (type == cgltf_type_mat3 && component_size == 2) + { + out[0] = cgltf_component_read_float(element, component_type, normalized); + out[1] = cgltf_component_read_float(element + 2, component_type, normalized); + out[2] = cgltf_component_read_float(element + 4, component_type, normalized); + out[3] = cgltf_component_read_float(element + 8, component_type, normalized); + out[4] = cgltf_component_read_float(element + 10, component_type, normalized); + out[5] = cgltf_component_read_float(element + 12, component_type, normalized); + out[6] = cgltf_component_read_float(element + 16, component_type, normalized); + out[7] = cgltf_component_read_float(element + 18, component_type, normalized); + out[8] = cgltf_component_read_float(element + 20, component_type, normalized); + return 1; + } + + for (cgltf_size i = 0; i < num_components; ++i) + { + out[i] = cgltf_component_read_float(element + component_size * i, component_type, normalized); + } + return 1; +} + +const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view) +{ + if (view->data) + return (const uint8_t*)view->data; + + if (!view->buffer->data) + return NULL; + + const uint8_t* result = (const uint8_t*)view->buffer->data; + result += view->offset; + return result; +} + +cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size) +{ + if (accessor->is_sparse) + { + return 0; + } + if (accessor->buffer_view == NULL) + { + memset(out, 0, element_size * sizeof(cgltf_float)); + return 1; + } + const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); + if (element == NULL) + { + return 0; + } + element += accessor->offset + accessor->stride * index; + return cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, out, element_size); +} + +cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count) +{ + cgltf_size floats_per_element = cgltf_num_components(accessor->type); + cgltf_size available_floats = accessor->count * floats_per_element; + if (out == NULL) + { + return available_floats; + } + + float_count = available_floats < float_count ? available_floats : float_count; + cgltf_size element_count = float_count / floats_per_element; + + // First pass: convert each element in the base accessor. + if (accessor->buffer_view == NULL) + { + memset(out, 0, element_count * floats_per_element * sizeof(cgltf_float)); + } + else + { + const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); + if (element == NULL) + { + return 0; + } + element += accessor->offset; + + if (accessor->component_type == cgltf_component_type_r_32f && accessor->stride == floats_per_element * sizeof(cgltf_float)) + { + memcpy(out, element, element_count * floats_per_element * sizeof(cgltf_float)); + } + else + { + cgltf_float* dest = out; + + for (cgltf_size index = 0; index < element_count; index++, dest += floats_per_element, element += accessor->stride) + { + if (!cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, dest, floats_per_element)) + { + return 0; + } + } + } + } + + // Second pass: write out each element in the sparse accessor. + if (accessor->is_sparse) + { + const cgltf_accessor_sparse* sparse = &accessor->sparse; + + const uint8_t* index_data = cgltf_buffer_view_data(sparse->indices_buffer_view); + const uint8_t* reader_head = cgltf_buffer_view_data(sparse->values_buffer_view); + + if (index_data == NULL || reader_head == NULL) + { + return 0; + } + + index_data += sparse->indices_byte_offset; + reader_head += sparse->values_byte_offset; + + cgltf_size index_stride = cgltf_component_size(sparse->indices_component_type); + for (cgltf_size reader_index = 0; reader_index < sparse->count; reader_index++, index_data += index_stride, reader_head += accessor->stride) + { + size_t writer_index = cgltf_component_read_index(index_data, sparse->indices_component_type); + float* writer_head = out + writer_index * floats_per_element; + + if (!cgltf_element_read_float(reader_head, accessor->type, accessor->component_type, accessor->normalized, writer_head, floats_per_element)) + { + return 0; + } + } + } + + return element_count * floats_per_element; +} + +static cgltf_uint cgltf_component_read_uint(const void* in, cgltf_component_type component_type) +{ + switch (component_type) + { + case cgltf_component_type_r_8: + return *((const int8_t*) in); + + case cgltf_component_type_r_8u: + return *((const uint8_t*) in); + + case cgltf_component_type_r_16: + return *((const int16_t*) in); + + case cgltf_component_type_r_16u: + return *((const uint16_t*) in); + + case cgltf_component_type_r_32u: + return *((const uint32_t*) in); + + default: + return 0; + } +} + +static 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) +{ + cgltf_size num_components = cgltf_num_components(type); + + if (element_size < num_components) + { + return 0; + } + + // Reading integer matrices is not a valid use case + if (type == cgltf_type_mat2 || type == cgltf_type_mat3 || type == cgltf_type_mat4) + { + return 0; + } + + cgltf_size component_size = cgltf_component_size(component_type); + + for (cgltf_size i = 0; i < num_components; ++i) + { + out[i] = cgltf_component_read_uint(element + component_size * i, component_type); + } + return 1; +} + +cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size) +{ + if (accessor->is_sparse) + { + return 0; + } + if (accessor->buffer_view == NULL) + { + memset(out, 0, element_size * sizeof( cgltf_uint )); + return 1; + } + const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); + if (element == NULL) + { + return 0; + } + element += accessor->offset + accessor->stride * index; + return cgltf_element_read_uint(element, accessor->type, accessor->component_type, out, element_size); +} + +cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index) +{ + if (accessor->is_sparse) + { + return 0; // This is an error case, but we can't communicate the error with existing interface. + } + if (accessor->buffer_view == NULL) + { + return 0; + } + const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); + if (element == NULL) + { + return 0; // This is an error case, but we can't communicate the error with existing interface. + } + element += accessor->offset + accessor->stride * index; + return cgltf_component_read_index(element, accessor->component_type); +} + +cgltf_size cgltf_mesh_index(const cgltf_data* data, const cgltf_mesh* object) +{ + assert(object && (cgltf_size)(object - data->meshes) < data->meshes_count); + return (cgltf_size)(object - data->meshes); +} + +cgltf_size cgltf_material_index(const cgltf_data* data, const cgltf_material* object) +{ + assert(object && (cgltf_size)(object - data->materials) < data->materials_count); + return (cgltf_size)(object - data->materials); +} + +cgltf_size cgltf_accessor_index(const cgltf_data* data, const cgltf_accessor* object) +{ + assert(object && (cgltf_size)(object - data->accessors) < data->accessors_count); + return (cgltf_size)(object - data->accessors); +} + +cgltf_size cgltf_buffer_view_index(const cgltf_data* data, const cgltf_buffer_view* object) +{ + assert(object && (cgltf_size)(object - data->buffer_views) < data->buffer_views_count); + return (cgltf_size)(object - data->buffer_views); +} + +cgltf_size cgltf_buffer_index(const cgltf_data* data, const cgltf_buffer* object) +{ + assert(object && (cgltf_size)(object - data->buffers) < data->buffers_count); + return (cgltf_size)(object - data->buffers); +} + +cgltf_size cgltf_image_index(const cgltf_data* data, const cgltf_image* object) +{ + assert(object && (cgltf_size)(object - data->images) < data->images_count); + return (cgltf_size)(object - data->images); +} + +cgltf_size cgltf_texture_index(const cgltf_data* data, const cgltf_texture* object) +{ + assert(object && (cgltf_size)(object - data->textures) < data->textures_count); + return (cgltf_size)(object - data->textures); +} + +cgltf_size cgltf_sampler_index(const cgltf_data* data, const cgltf_sampler* object) +{ + assert(object && (cgltf_size)(object - data->samplers) < data->samplers_count); + return (cgltf_size)(object - data->samplers); +} + +cgltf_size cgltf_skin_index(const cgltf_data* data, const cgltf_skin* object) +{ + assert(object && (cgltf_size)(object - data->skins) < data->skins_count); + return (cgltf_size)(object - data->skins); +} + +cgltf_size cgltf_camera_index(const cgltf_data* data, const cgltf_camera* object) +{ + assert(object && (cgltf_size)(object - data->cameras) < data->cameras_count); + return (cgltf_size)(object - data->cameras); +} + +cgltf_size cgltf_light_index(const cgltf_data* data, const cgltf_light* object) +{ + assert(object && (cgltf_size)(object - data->lights) < data->lights_count); + return (cgltf_size)(object - data->lights); +} + +cgltf_size cgltf_node_index(const cgltf_data* data, const cgltf_node* object) +{ + assert(object && (cgltf_size)(object - data->nodes) < data->nodes_count); + return (cgltf_size)(object - data->nodes); +} + +cgltf_size cgltf_scene_index(const cgltf_data* data, const cgltf_scene* object) +{ + assert(object && (cgltf_size)(object - data->scenes) < data->scenes_count); + return (cgltf_size)(object - data->scenes); +} + +cgltf_size cgltf_animation_index(const cgltf_data* data, const cgltf_animation* object) +{ + assert(object && (cgltf_size)(object - data->animations) < data->animations_count); + return (cgltf_size)(object - data->animations); +} + +cgltf_size cgltf_animation_sampler_index(const cgltf_animation* animation, const cgltf_animation_sampler* object) +{ + assert(object && (cgltf_size)(object - animation->samplers) < animation->samplers_count); + return (cgltf_size)(object - animation->samplers); +} + +cgltf_size cgltf_animation_channel_index(const cgltf_animation* animation, const cgltf_animation_channel* object) +{ + assert(object && (cgltf_size)(object - animation->channels) < animation->channels_count); + return (cgltf_size)(object - animation->channels); +} + +cgltf_size cgltf_accessor_unpack_indices(const cgltf_accessor* accessor, void* out, cgltf_size out_component_size, cgltf_size index_count) +{ + if (out == NULL) + { + return accessor->count; + } + + index_count = accessor->count < index_count ? accessor->count : index_count; + cgltf_size index_component_size = cgltf_component_size(accessor->component_type); + + if (accessor->is_sparse) + { + return 0; + } + if (accessor->buffer_view == NULL) + { + return 0; + } + if (index_component_size > out_component_size) + { + return 0; + } + const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); + if (element == NULL) + { + return 0; + } + element += accessor->offset; + + if (index_component_size == out_component_size && accessor->stride == out_component_size) + { + memcpy(out, element, index_count * index_component_size); + return index_count; + } + + // The component size of the output array is larger than the component size of the index data, so index data will be padded. + switch (out_component_size) + { + case 2: + for (cgltf_size index = 0; index < index_count; index++, element += accessor->stride) + { + ((uint16_t*)out)[index] = (uint16_t)cgltf_component_read_index(element, accessor->component_type); + } + break; + case 4: + for (cgltf_size index = 0; index < index_count; index++, element += accessor->stride) + { + ((uint32_t*)out)[index] = (uint32_t)cgltf_component_read_index(element, accessor->component_type); + } + break; + default: + break; + } + + return index_count; +} + +#define CGLTF_ERROR_JSON -1 +#define CGLTF_ERROR_NOMEM -2 +#define CGLTF_ERROR_LEGACY -3 + +#define CGLTF_CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return CGLTF_ERROR_JSON; } +#define CGLTF_CHECK_TOKTYPE_RET(tok_, type_, ret_) if ((tok_).type != (type_)) { return ret_; } +#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 */ + +#define CGLTF_PTRINDEX(type, idx) (type*)((cgltf_size)idx + 1) +#define CGLTF_PTRFIXUP(var, data, size) if (var) { if ((cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1]; } +#define CGLTF_PTRFIXUP_REQ(var, data, size) if (!var || (cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1]; + +static int cgltf_json_strcmp(jsmntok_t const* tok, const uint8_t* json_chunk, const char* str) +{ + CGLTF_CHECK_TOKTYPE(*tok, JSMN_STRING); + size_t const str_len = strlen(str); + size_t const name_length = (size_t)(tok->end - tok->start); + return (str_len == name_length) ? strncmp((const char*)json_chunk + tok->start, str, str_len) : 128; +} + +static int cgltf_json_to_int(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); + char tmp[128]; + int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1); + strncpy(tmp, (const char*)json_chunk + tok->start, size); + tmp[size] = 0; + return CGLTF_ATOI(tmp); +} + +static cgltf_size cgltf_json_to_size(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + CGLTF_CHECK_TOKTYPE_RET(*tok, JSMN_PRIMITIVE, 0); + char tmp[128]; + int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1); + strncpy(tmp, (const char*)json_chunk + tok->start, size); + tmp[size] = 0; + long long res = CGLTF_ATOLL(tmp); + return res < 0 ? 0 : (cgltf_size)res; +} + +static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); + char tmp[128]; + int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1); + strncpy(tmp, (const char*)json_chunk + tok->start, size); + tmp[size] = 0; + return (cgltf_float)CGLTF_ATOF(tmp); +} + +static cgltf_bool cgltf_json_to_bool(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + int size = (int)(tok->end - tok->start); + return size == 4 && memcmp(json_chunk + tok->start, "true", 4) == 0; +} + +static int cgltf_skip_json(jsmntok_t const* tokens, int i) +{ + int end = i + 1; + + while (i < end) + { + switch (tokens[i].type) + { + case JSMN_OBJECT: + end += tokens[i].size * 2; + break; + + case JSMN_ARRAY: + end += tokens[i].size; + break; + + case JSMN_PRIMITIVE: + case JSMN_STRING: + break; + + default: + return -1; + } + + i++; + } + + return i; +} + +static void cgltf_fill_float_array(float* out_array, int size, float value) +{ + for (int j = 0; j < size; ++j) + { + out_array[j] = value; + } +} + +static int cgltf_parse_json_float_array(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, float* out_array, int size) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + if (tokens[i].size != size) + { + return CGLTF_ERROR_JSON; + } + ++i; + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_array[j] = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + return i; +} + +static int cgltf_parse_json_string(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, char** out_string) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING); + if (*out_string) + { + return CGLTF_ERROR_JSON; + } + int size = (int)(tokens[i].end - tokens[i].start); + char* result = (char*)options->memory.alloc_func(options->memory.user_data, size + 1); + if (!result) + { + return CGLTF_ERROR_NOMEM; + } + strncpy(result, (const char*)json_chunk + tokens[i].start, size); + result[size] = 0; + *out_string = result; + return i + 1; +} + +static 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) +{ + (void)json_chunk; + if (tokens[i].type != JSMN_ARRAY) + { + return tokens[i].type == JSMN_OBJECT ? CGLTF_ERROR_LEGACY : CGLTF_ERROR_JSON; + } + if (*out_array) + { + return CGLTF_ERROR_JSON; + } + int size = tokens[i].size; + void* result = cgltf_calloc(options, element_size, size); + if (!result) + { + return CGLTF_ERROR_NOMEM; + } + *out_array = result; + *out_size = size; + return i + 1; +} + +static 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) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(char*), (void**)out_array, out_size); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < *out_size; ++j) + { + i = cgltf_parse_json_string(options, tokens, i, json_chunk, j + (*out_array)); + if (i < 0) + { + return i; + } + } + return i; +} + +static void cgltf_parse_attribute_type(const char* name, cgltf_attribute_type* out_type, int* out_index) +{ + if (*name == '_') + { + *out_type = cgltf_attribute_type_custom; + return; + } + + const char* us = strchr(name, '_'); + size_t len = us ? (size_t)(us - name) : strlen(name); + + if (len == 8 && strncmp(name, "POSITION", 8) == 0) + { + *out_type = cgltf_attribute_type_position; + } + else if (len == 6 && strncmp(name, "NORMAL", 6) == 0) + { + *out_type = cgltf_attribute_type_normal; + } + else if (len == 7 && strncmp(name, "TANGENT", 7) == 0) + { + *out_type = cgltf_attribute_type_tangent; + } + else if (len == 8 && strncmp(name, "TEXCOORD", 8) == 0) + { + *out_type = cgltf_attribute_type_texcoord; + } + else if (len == 5 && strncmp(name, "COLOR", 5) == 0) + { + *out_type = cgltf_attribute_type_color; + } + else if (len == 6 && strncmp(name, "JOINTS", 6) == 0) + { + *out_type = cgltf_attribute_type_joints; + } + else if (len == 7 && strncmp(name, "WEIGHTS", 7) == 0) + { + *out_type = cgltf_attribute_type_weights; + } + else + { + *out_type = cgltf_attribute_type_invalid; + } + + if (us && *out_type != cgltf_attribute_type_invalid) + { + *out_index = CGLTF_ATOI(us + 1); + if (*out_index < 0) + { + *out_type = cgltf_attribute_type_invalid; + *out_index = 0; + } + } +} + +static 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) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + if (*out_attributes) + { + return CGLTF_ERROR_JSON; + } + + *out_attributes_count = tokens[i].size; + *out_attributes = (cgltf_attribute*)cgltf_calloc(options, sizeof(cgltf_attribute), *out_attributes_count); + ++i; + + if (!*out_attributes) + { + return CGLTF_ERROR_NOMEM; + } + + for (cgltf_size j = 0; j < *out_attributes_count; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + i = cgltf_parse_json_string(options, tokens, i, json_chunk, &(*out_attributes)[j].name); + if (i < 0) + { + return CGLTF_ERROR_JSON; + } + + cgltf_parse_attribute_type((*out_attributes)[j].name, &(*out_attributes)[j].type, &(*out_attributes)[j].index); + + (*out_attributes)[j].data = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + + return i; +} + +static int cgltf_parse_json_extras(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras) +{ + 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; +} + +static int cgltf_parse_json_unprocessed_extension(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extension* out_extension) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING); + CGLTF_CHECK_TOKTYPE(tokens[i+1], JSMN_OBJECT); + if (out_extension->name) + { + return CGLTF_ERROR_JSON; + } + + cgltf_size name_length = tokens[i].end - tokens[i].start; + out_extension->name = (char*)options->memory.alloc_func(options->memory.user_data, name_length + 1); + if (!out_extension->name) + { + return CGLTF_ERROR_NOMEM; + } + strncpy(out_extension->name, (const char*)json_chunk + tokens[i].start, name_length); + out_extension->name[name_length] = 0; + i++; + + size_t start = tokens[i].start; + size_t size = tokens[i].end - start; + out_extension->data = (char*)options->memory.alloc_func(options->memory.user_data, size + 1); + if (!out_extension->data) + { + return CGLTF_ERROR_NOMEM; + } + strncpy(out_extension->data, (const char*)json_chunk + start, size); + out_extension->data[size] = '\0'; + + i = cgltf_skip_json(tokens, i); + + return i; +} + +static 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) +{ + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(*out_extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + *out_extensions_count = 0; + *out_extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!*out_extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + + for (int j = 0; j < extensions_size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + cgltf_size extension_index = (*out_extensions_count)++; + cgltf_extension* extension = &((*out_extensions)[extension_index]); + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, extension); + + if (i < 0) + { + return i; + } + } + return i; +} + +static 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) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "attributes") == 0) + { + i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_draco_mesh_compression->attributes, &out_draco_mesh_compression->attributes_count); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "bufferView") == 0) + { + ++i; + out_draco_mesh_compression->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_mesh_gpu_instancing(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_mesh_gpu_instancing* out_mesh_gpu_instancing) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "attributes") == 0) + { + i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_mesh_gpu_instancing->attributes, &out_mesh_gpu_instancing->attributes_count); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material_mapping* out_mappings, cgltf_size* offset) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int obj_size = tokens[i].size; + ++i; + + int material = -1; + int variants_tok = -1; + int extras_tok = -1; + + for (int k = 0; k < obj_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "material") == 0) + { + ++i; + material = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "variants") == 0) + { + variants_tok = i+1; + CGLTF_CHECK_TOKTYPE(tokens[variants_tok], JSMN_ARRAY); + + i = cgltf_skip_json(tokens, i+1); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + extras_tok = i + 1; + i = cgltf_skip_json(tokens, extras_tok); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + if (material < 0 || variants_tok < 0) + { + return CGLTF_ERROR_JSON; + } + + if (out_mappings) + { + for (int k = 0; k < tokens[variants_tok].size; ++k) + { + int variant = cgltf_json_to_int(&tokens[variants_tok + 1 + k], json_chunk); + if (variant < 0) + return variant; + + out_mappings[*offset].material = CGLTF_PTRINDEX(cgltf_material, material); + out_mappings[*offset].variant = variant; + + 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)++; + } + } + else + { + (*offset) += tokens[variants_tok].size; + } + } + + return i; +} + +static int cgltf_parse_json_material_mappings(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_primitive* out_prim) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "mappings") == 0) + { + if (out_prim->mappings) + { + return CGLTF_ERROR_JSON; + } + + cgltf_size mappings_offset = 0; + int k = cgltf_parse_json_material_mapping_data(options, tokens, i + 1, json_chunk, NULL, &mappings_offset); + if (k < 0) + { + return k; + } + + out_prim->mappings_count = mappings_offset; + out_prim->mappings = (cgltf_material_mapping*)cgltf_calloc(options, sizeof(cgltf_material_mapping), out_prim->mappings_count); + + mappings_offset = 0; + i = cgltf_parse_json_material_mapping_data(options, tokens, i + 1, json_chunk, out_prim->mappings, &mappings_offset); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static cgltf_primitive_type cgltf_json_to_primitive_type(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + int type = cgltf_json_to_int(tok, json_chunk); + + switch (type) + { + case 0: + return cgltf_primitive_type_points; + case 1: + return cgltf_primitive_type_lines; + case 2: + return cgltf_primitive_type_line_loop; + case 3: + return cgltf_primitive_type_line_strip; + case 4: + return cgltf_primitive_type_triangles; + case 5: + return cgltf_primitive_type_triangle_strip; + case 6: + return cgltf_primitive_type_triangle_fan; + default: + return cgltf_primitive_type_invalid; + } +} + +static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_primitive* out_prim) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_prim->type = cgltf_primitive_type_triangles; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0) + { + ++i; + out_prim->type = cgltf_json_to_primitive_type(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0) + { + ++i; + out_prim->indices = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "material") == 0) + { + ++i; + out_prim->material = CGLTF_PTRINDEX(cgltf_material, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "attributes") == 0) + { + i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_prim->attributes, &out_prim->attributes_count); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "targets") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_morph_target), (void**)&out_prim->targets, &out_prim->targets_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_prim->targets_count; ++k) + { + i = cgltf_parse_json_attribute_list(options, tokens, i, json_chunk, &out_prim->targets[k].attributes, &out_prim->targets[k].attributes_count); + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + 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) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_prim->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + out_prim->extensions_count = 0; + out_prim->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!out_prim->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_draco_mesh_compression") == 0) + { + out_prim->has_draco_mesh_compression = 1; + i = cgltf_parse_json_draco_mesh_compression(options, tokens, i + 1, json_chunk, &out_prim->draco_mesh_compression); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_variants") == 0) + { + i = cgltf_parse_json_material_mappings(options, tokens, i + 1, json_chunk, out_prim); + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_prim->extensions[out_prim->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_mesh(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_mesh* out_mesh) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_mesh->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "primitives") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_primitive), (void**)&out_mesh->primitives, &out_mesh->primitives_count); + if (i < 0) + { + return i; + } + + for (cgltf_size prim_index = 0; prim_index < out_mesh->primitives_count; ++prim_index) + { + i = cgltf_parse_json_primitive(options, tokens, i, json_chunk, &out_mesh->primitives[prim_index]); + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "weights") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_float), (void**)&out_mesh->weights, &out_mesh->weights_count); + if (i < 0) + { + return i; + } + + i = cgltf_parse_json_float_array(tokens, i - 1, json_chunk, out_mesh->weights, (int)out_mesh->weights_count); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + ++i; + + out_mesh->extras.start_offset = tokens[i].start; + out_mesh->extras.end_offset = tokens[i].end; + + if (tokens[i].type == JSMN_OBJECT) + { + int extras_size = tokens[i].size; + ++i; + + for (int k = 0; k < extras_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "targetNames") == 0 && tokens[i+1].type == JSMN_ARRAY) + { + i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_mesh->target_names, &out_mesh->target_names_count); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i); + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_mesh->extensions_count, &out_mesh->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_meshes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_mesh), (void**)&out_data->meshes, &out_data->meshes_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->meshes_count; ++j) + { + i = cgltf_parse_json_mesh(options, tokens, i, json_chunk, &out_data->meshes[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static cgltf_component_type cgltf_json_to_component_type(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + int type = cgltf_json_to_int(tok, json_chunk); + + switch (type) + { + case 5120: + return cgltf_component_type_r_8; + case 5121: + return cgltf_component_type_r_8u; + case 5122: + return cgltf_component_type_r_16; + case 5123: + return cgltf_component_type_r_16u; + case 5125: + return cgltf_component_type_r_32u; + case 5126: + return cgltf_component_type_r_32f; + default: + return cgltf_component_type_invalid; + } +} + +static int cgltf_parse_json_accessor_sparse(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_accessor_sparse* out_sparse) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) + { + ++i; + out_sparse->count = cgltf_json_to_size(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int indices_size = tokens[i].size; + ++i; + + for (int k = 0; k < indices_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_sparse->indices_buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_sparse->indices_byte_offset = cgltf_json_to_size(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0) + { + ++i; + out_sparse->indices_component_type = cgltf_json_to_component_type(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "values") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int values_size = tokens[i].size; + ++i; + + for (int k = 0; k < values_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_sparse->values_buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_sparse->values_byte_offset = cgltf_json_to_size(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_accessor(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_accessor* out_accessor) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_accessor->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_accessor->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_accessor->offset = + cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0) + { + ++i; + out_accessor->component_type = cgltf_json_to_component_type(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "normalized") == 0) + { + ++i; + out_accessor->normalized = cgltf_json_to_bool(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) + { + ++i; + out_accessor->count = cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens+i, json_chunk, "SCALAR") == 0) + { + out_accessor->type = cgltf_type_scalar; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC2") == 0) + { + out_accessor->type = cgltf_type_vec2; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC3") == 0) + { + out_accessor->type = cgltf_type_vec3; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC4") == 0) + { + out_accessor->type = cgltf_type_vec4; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT2") == 0) + { + out_accessor->type = cgltf_type_mat2; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT3") == 0) + { + out_accessor->type = cgltf_type_mat3; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT4") == 0) + { + out_accessor->type = cgltf_type_mat4; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "min") == 0) + { + ++i; + out_accessor->has_min = 1; + // note: we can't parse the precise number of elements since type may not have been computed yet + int min_size = tokens[i].size > 16 ? 16 : tokens[i].size; + i = cgltf_parse_json_float_array(tokens, i, json_chunk, out_accessor->min, min_size); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "max") == 0) + { + ++i; + out_accessor->has_max = 1; + // note: we can't parse the precise number of elements since type may not have been computed yet + int max_size = tokens[i].size > 16 ? 16 : tokens[i].size; + i = cgltf_parse_json_float_array(tokens, i, json_chunk, out_accessor->max, max_size); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "sparse") == 0) + { + out_accessor->is_sparse = 1; + i = cgltf_parse_json_accessor_sparse(tokens, i + 1, json_chunk, &out_accessor->sparse); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + 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) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_accessor->extensions_count, &out_accessor->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_texture_transform(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_transform* out_texture_transform) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "offset") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_texture_transform->offset, 2); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "rotation") == 0) + { + ++i; + out_texture_transform->rotation = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_texture_transform->scale, 2); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0) + { + ++i; + out_texture_transform->has_texcoord = 1; + out_texture_transform->texcoord = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static 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) +{ + (void)options; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_texture_view->scale = 1.0f; + cgltf_fill_float_array(out_texture_view->transform.scale, 2, 1.0f); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "index") == 0) + { + ++i; + out_texture_view->texture = CGLTF_PTRINDEX(cgltf_texture, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0) + { + ++i; + out_texture_view->texcoord = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) + { + ++i; + out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "strength") == 0) + { + ++i; + out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int extensions_size = tokens[i].size; + + ++i; + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_texture_transform") == 0) + { + out_texture_view->has_transform = 1; + i = cgltf_parse_json_texture_transform(tokens, i + 1, json_chunk, &out_texture_view->transform); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static 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) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "metallicFactor") == 0) + { + ++i; + out_pbr->metallic_factor = + cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "roughnessFactor") == 0) + { + ++i; + out_pbr->roughness_factor = + cgltf_json_to_float(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->base_color_factor, 4); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->base_color_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "metallicRoughnessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->metallic_roughness_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static 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) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "diffuseFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->diffuse_factor, 4); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->specular_factor, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "glossinessFactor") == 0) + { + ++i; + out_pbr->glossiness_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "diffuseTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->diffuse_texture); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularGlossinessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->specular_glossiness_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_clearcoat(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_clearcoat* out_clearcoat) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatFactor") == 0) + { + ++i; + out_clearcoat->clearcoat_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatRoughnessFactor") == 0) + { + ++i; + out_clearcoat->clearcoat_roughness_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_texture); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatRoughnessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_roughness_texture); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatNormalTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_normal_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_ior(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_ior* out_ior) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + // Default values + out_ior->ior = 1.5f; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "ior") == 0) + { + ++i; + out_ior->ior = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_specular(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_specular* out_specular) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + // Default values + out_specular->specular_factor = 1.0f; + cgltf_fill_float_array(out_specular->specular_color_factor, 3, 1.0f); + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "specularFactor") == 0) + { + ++i; + out_specular->specular_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularColorFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_specular->specular_color_factor, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_specular->specular_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "specularColorTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_specular->specular_color_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_transmission(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_transmission* out_transmission) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "transmissionFactor") == 0) + { + ++i; + out_transmission->transmission_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "transmissionTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_transmission->transmission_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_volume(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_volume* out_volume) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "thicknessFactor") == 0) + { + ++i; + out_volume->thickness_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "thicknessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_volume->thickness_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "attenuationColor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_volume->attenuation_color, 3); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "attenuationDistance") == 0) + { + ++i; + out_volume->attenuation_distance = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_sheen(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_sheen* out_sheen) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenColorFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_sheen->sheen_color_factor, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenColorTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_sheen->sheen_color_texture); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenRoughnessFactor") == 0) + { + ++i; + out_sheen->sheen_roughness_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenRoughnessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_sheen->sheen_roughness_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_emissive_strength(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_emissive_strength* out_emissive_strength) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + // Default + out_emissive_strength->emissive_strength = 1.f; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveStrength") == 0) + { + ++i; + out_emissive_strength->emissive_strength = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_iridescence(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_iridescence* out_iridescence) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + // Default + out_iridescence->iridescence_ior = 1.3f; + out_iridescence->iridescence_thickness_min = 100.f; + out_iridescence->iridescence_thickness_max = 400.f; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceFactor") == 0) + { + ++i; + out_iridescence->iridescence_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_iridescence->iridescence_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceIor") == 0) + { + ++i; + out_iridescence->iridescence_ior = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceThicknessMinimum") == 0) + { + ++i; + out_iridescence->iridescence_thickness_min = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceThicknessMaximum") == 0) + { + ++i; + out_iridescence->iridescence_thickness_max = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "iridescenceThicknessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_iridescence->iridescence_thickness_texture); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_anisotropy(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_anisotropy* out_anisotropy) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "anisotropyStrength") == 0) + { + ++i; + out_anisotropy->anisotropy_strength = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "anisotropyRotation") == 0) + { + ++i; + out_anisotropy->anisotropy_rotation = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "anisotropyTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_anisotropy->anisotropy_texture); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_dispersion(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_dispersion* out_dispersion) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "dispersion") == 0) + { + ++i; + out_dispersion->dispersion = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_image* out_image) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "uri") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->uri); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_image->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "mimeType") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->mime_type); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + 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) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_image->extensions_count, &out_image->extensions); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_sampler* out_sampler) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_sampler->wrap_s = 10497; + out_sampler->wrap_t = 10497; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_sampler->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "magFilter") == 0) + { + ++i; + out_sampler->mag_filter + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "minFilter") == 0) + { + ++i; + out_sampler->min_filter + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapS") == 0) + { + ++i; + out_sampler->wrap_s + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapT") == 0) + { + ++i; + out_sampler->wrap_t + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + 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) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sampler->extensions_count, &out_sampler->extensions); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture* out_texture) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_texture->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "sampler") == 0) + { + ++i; + out_texture->sampler = CGLTF_PTRINDEX(cgltf_sampler, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) + { + ++i; + out_texture->image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + 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) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if (out_texture->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + ++i; + out_texture->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + out_texture->extensions_count = 0; + + if (!out_texture->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_texture_basisu") == 0) + { + out_texture->has_basisu = 1; + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int num_properties = tokens[i].size; + ++i; + + for (int t = 0; t < num_properties; ++t) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) + { + ++i; + out_texture->basisu_image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "EXT_texture_webp") == 0) + { + out_texture->has_webp = 1; + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int num_properties = tokens[i].size; + ++i; + + for (int t = 0; t < num_properties; ++t) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) + { + ++i; + out_texture->webp_image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_texture->extensions[out_texture->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material* out_material) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + cgltf_fill_float_array(out_material->pbr_metallic_roughness.base_color_factor, 4, 1.0f); + out_material->pbr_metallic_roughness.metallic_factor = 1.0f; + out_material->pbr_metallic_roughness.roughness_factor = 1.0f; + + cgltf_fill_float_array(out_material->pbr_specular_glossiness.diffuse_factor, 4, 1.0f); + cgltf_fill_float_array(out_material->pbr_specular_glossiness.specular_factor, 3, 1.0f); + out_material->pbr_specular_glossiness.glossiness_factor = 1.0f; + + cgltf_fill_float_array(out_material->volume.attenuation_color, 3, 1.0f); + out_material->volume.attenuation_distance = FLT_MAX; + + out_material->alpha_cutoff = 0.5f; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_material->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "pbrMetallicRoughness") == 0) + { + out_material->has_pbr_metallic_roughness = 1; + i = cgltf_parse_json_pbr_metallic_roughness(options, tokens, i + 1, json_chunk, &out_material->pbr_metallic_roughness); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "emissiveFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_material->emissive_factor, 3); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "normalTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, + &out_material->normal_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "occlusionTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, + &out_material->occlusion_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, + &out_material->emissive_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "alphaMode") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens + i, json_chunk, "OPAQUE") == 0) + { + out_material->alpha_mode = cgltf_alpha_mode_opaque; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "MASK") == 0) + { + out_material->alpha_mode = cgltf_alpha_mode_mask; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "BLEND") == 0) + { + out_material->alpha_mode = cgltf_alpha_mode_blend; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "alphaCutoff") == 0) + { + ++i; + out_material->alpha_cutoff = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "doubleSided") == 0) + { + ++i; + out_material->double_sided = + cgltf_json_to_bool(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + 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) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_material->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + ++i; + out_material->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + out_material->extensions_count= 0; + + if (!out_material->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_pbrSpecularGlossiness") == 0) + { + out_material->has_pbr_specular_glossiness = 1; + i = cgltf_parse_json_pbr_specular_glossiness(options, tokens, i + 1, json_chunk, &out_material->pbr_specular_glossiness); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_unlit") == 0) + { + out_material->unlit = 1; + i = cgltf_skip_json(tokens, i+1); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_clearcoat") == 0) + { + out_material->has_clearcoat = 1; + i = cgltf_parse_json_clearcoat(options, tokens, i + 1, json_chunk, &out_material->clearcoat); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_ior") == 0) + { + out_material->has_ior = 1; + i = cgltf_parse_json_ior(tokens, i + 1, json_chunk, &out_material->ior); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_specular") == 0) + { + out_material->has_specular = 1; + i = cgltf_parse_json_specular(options, tokens, i + 1, json_chunk, &out_material->specular); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_transmission") == 0) + { + out_material->has_transmission = 1; + i = cgltf_parse_json_transmission(options, tokens, i + 1, json_chunk, &out_material->transmission); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_volume") == 0) + { + out_material->has_volume = 1; + i = cgltf_parse_json_volume(options, tokens, i + 1, json_chunk, &out_material->volume); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_sheen") == 0) + { + out_material->has_sheen = 1; + i = cgltf_parse_json_sheen(options, tokens, i + 1, json_chunk, &out_material->sheen); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_emissive_strength") == 0) + { + out_material->has_emissive_strength = 1; + i = cgltf_parse_json_emissive_strength(tokens, i + 1, json_chunk, &out_material->emissive_strength); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_iridescence") == 0) + { + out_material->has_iridescence = 1; + i = cgltf_parse_json_iridescence(options, tokens, i + 1, json_chunk, &out_material->iridescence); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_anisotropy") == 0) + { + out_material->has_anisotropy = 1; + i = cgltf_parse_json_anisotropy(options, tokens, i + 1, json_chunk, &out_material->anisotropy); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_dispersion") == 0) + { + out_material->has_dispersion = 1; + i = cgltf_parse_json_dispersion(tokens, i + 1, json_chunk, &out_material->dispersion); + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_material->extensions[out_material->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_accessors(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_accessor), (void**)&out_data->accessors, &out_data->accessors_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->accessors_count; ++j) + { + i = cgltf_parse_json_accessor(options, tokens, i, json_chunk, &out_data->accessors[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_materials(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_material), (void**)&out_data->materials, &out_data->materials_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->materials_count; ++j) + { + i = cgltf_parse_json_material(options, tokens, i, json_chunk, &out_data->materials[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_images(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_image), (void**)&out_data->images, &out_data->images_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->images_count; ++j) + { + i = cgltf_parse_json_image(options, tokens, i, json_chunk, &out_data->images[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_textures(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_texture), (void**)&out_data->textures, &out_data->textures_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->textures_count; ++j) + { + i = cgltf_parse_json_texture(options, tokens, i, json_chunk, &out_data->textures[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_samplers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_sampler), (void**)&out_data->samplers, &out_data->samplers_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->samplers_count; ++j) + { + i = cgltf_parse_json_sampler(options, tokens, i, json_chunk, &out_data->samplers[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_meshopt_compression(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_meshopt_compression* out_meshopt_compression) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0) + { + ++i; + out_meshopt_compression->buffer = CGLTF_PTRINDEX(cgltf_buffer, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_meshopt_compression->offset = cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) + { + ++i; + out_meshopt_compression->size = cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0) + { + ++i; + out_meshopt_compression->stride = cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) + { + ++i; + out_meshopt_compression->count = cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens+i, json_chunk, "ATTRIBUTES") == 0) + { + out_meshopt_compression->mode = cgltf_meshopt_compression_mode_attributes; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "TRIANGLES") == 0) + { + out_meshopt_compression->mode = cgltf_meshopt_compression_mode_triangles; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "INDICES") == 0) + { + out_meshopt_compression->mode = cgltf_meshopt_compression_mode_indices; + } + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "filter") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens+i, json_chunk, "NONE") == 0) + { + out_meshopt_compression->filter = cgltf_meshopt_compression_filter_none; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "OCTAHEDRAL") == 0) + { + out_meshopt_compression->filter = cgltf_meshopt_compression_filter_octahedral; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "QUATERNION") == 0) + { + out_meshopt_compression->filter = cgltf_meshopt_compression_filter_quaternion; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "EXPONENTIAL") == 0) + { + out_meshopt_compression->filter = cgltf_meshopt_compression_filter_exponential; + } + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static 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) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer_view->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0) + { + ++i; + out_buffer_view->buffer = CGLTF_PTRINDEX(cgltf_buffer, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_buffer_view->offset = + cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) + { + ++i; + out_buffer_view->size = + cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0) + { + ++i; + out_buffer_view->stride = + cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0) + { + ++i; + int type = cgltf_json_to_int(tokens+i, json_chunk); + switch (type) + { + case 34962: + type = cgltf_buffer_view_type_vertices; + break; + case 34963: + type = cgltf_buffer_view_type_indices; + break; + default: + type = cgltf_buffer_view_type_invalid; + break; + } + out_buffer_view->type = (cgltf_buffer_view_type)type; + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + 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) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_buffer_view->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + out_buffer_view->extensions_count = 0; + out_buffer_view->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!out_buffer_view->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "EXT_meshopt_compression") == 0) + { + out_buffer_view->has_meshopt_compression = 1; + i = cgltf_parse_json_meshopt_compression(options, tokens, i + 1, json_chunk, &out_buffer_view->meshopt_compression); + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_buffer_view->extensions[out_buffer_view->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_buffer_views(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_buffer_view), (void**)&out_data->buffer_views, &out_data->buffer_views_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->buffer_views_count; ++j) + { + i = cgltf_parse_json_buffer_view(options, tokens, i, json_chunk, &out_data->buffer_views[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_buffer(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_buffer* out_buffer) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) + { + ++i; + out_buffer->size = + cgltf_json_to_size(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "uri") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer->uri); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + 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) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_buffer->extensions_count, &out_buffer->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_buffers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_buffer), (void**)&out_data->buffers, &out_data->buffers_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->buffers_count; ++j) + { + i = cgltf_parse_json_buffer(options, tokens, i, json_chunk, &out_data->buffers[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_skin(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_skin* out_skin) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_skin->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "joints") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_skin->joints, &out_skin->joints_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_skin->joints_count; ++k) + { + out_skin->joints[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "skeleton") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_skin->skeleton = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "inverseBindMatrices") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_skin->inverse_bind_matrices = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + 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) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_skin->extensions_count, &out_skin->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_skins(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_skin), (void**)&out_data->skins, &out_data->skins_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->skins_count; ++j) + { + i = cgltf_parse_json_skin(options, tokens, i, json_chunk, &out_data->skins[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_camera* out_camera) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_camera->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "perspective") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + 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) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "aspectRatio") == 0) + { + ++i; + out_camera->data.perspective.has_aspect_ratio = 1; + out_camera->data.perspective.aspect_ratio = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "yfov") == 0) + { + ++i; + out_camera->data.perspective.yfov = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0) + { + ++i; + out_camera->data.perspective.has_zfar = 1; + out_camera->data.perspective.zfar = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0) + { + ++i; + out_camera->data.perspective.znear = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.perspective.extras); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "orthographic") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + 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) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "xmag") == 0) + { + ++i; + out_camera->data.orthographic.xmag = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "ymag") == 0) + { + ++i; + out_camera->data.orthographic.ymag = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0) + { + ++i; + out_camera->data.orthographic.zfar = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0) + { + ++i; + out_camera->data.orthographic.znear = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + 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) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_camera->extensions_count, &out_camera->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_cameras(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_camera), (void**)&out_data->cameras, &out_data->cameras_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->cameras_count; ++j) + { + i = cgltf_parse_json_camera(options, tokens, i, json_chunk, &out_data->cameras[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_light(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_light* out_light) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_light->color[0] = 1.f; + out_light->color[1] = 1.f; + out_light->color[2] = 1.f; + out_light->intensity = 1.f; + + out_light->spot_inner_cone_angle = 0.f; + out_light->spot_outer_cone_angle = 3.1415926535f / 4.0f; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_light->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "color") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_light->color, 3); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "intensity") == 0) + { + ++i; + out_light->intensity = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens + i, json_chunk, "directional") == 0) + { + out_light->type = cgltf_light_type_directional; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "point") == 0) + { + out_light->type = cgltf_light_type_point; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "spot") == 0) + { + out_light->type = cgltf_light_type_spot; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "range") == 0) + { + ++i; + out_light->range = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "spot") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + for (int k = 0; k < data_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "innerConeAngle") == 0) + { + ++i; + out_light->spot_inner_cone_angle = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "outerConeAngle") == 0) + { + ++i; + out_light->spot_outer_cone_angle = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_light->extras); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_lights(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_light), (void**)&out_data->lights, &out_data->lights_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->lights_count; ++j) + { + i = cgltf_parse_json_light(options, tokens, i, json_chunk, &out_data->lights[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_node(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_node* out_node) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_node->rotation[3] = 1.0f; + out_node->scale[0] = 1.0f; + out_node->scale[1] = 1.0f; + out_node->scale[2] = 1.0f; + out_node->matrix[0] = 1.0f; + out_node->matrix[5] = 1.0f; + out_node->matrix[10] = 1.0f; + out_node->matrix[15] = 1.0f; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_node->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "children") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_node->children, &out_node->children_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_node->children_count; ++k) + { + out_node->children[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "mesh") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_node->mesh = CGLTF_PTRINDEX(cgltf_mesh, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "skin") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_node->skin = CGLTF_PTRINDEX(cgltf_skin, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "camera") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_node->camera = CGLTF_PTRINDEX(cgltf_camera, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "translation") == 0) + { + out_node->has_translation = 1; + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->translation, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "rotation") == 0) + { + out_node->has_rotation = 1; + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->rotation, 4); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "scale") == 0) + { + out_node->has_scale = 1; + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->scale, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "matrix") == 0) + { + out_node->has_matrix = 1; + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->matrix, 16); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "weights") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_float), (void**)&out_node->weights, &out_node->weights_count); + if (i < 0) + { + return i; + } + + i = cgltf_parse_json_float_array(tokens, i - 1, json_chunk, out_node->weights, (int)out_node->weights_count); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + 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) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_node->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + out_node->extensions_count= 0; + out_node->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!out_node->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_lights_punctual") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + for (int m = 0; m < data_size; ++m) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "light") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_node->light = CGLTF_PTRINDEX(cgltf_light, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "EXT_mesh_gpu_instancing") == 0) + { + out_node->has_mesh_gpu_instancing = 1; + i = cgltf_parse_json_mesh_gpu_instancing(options, tokens, i + 1, json_chunk, &out_node->mesh_gpu_instancing); + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_node->extensions[out_node->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_nodes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_node), (void**)&out_data->nodes, &out_data->nodes_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->nodes_count; ++j) + { + i = cgltf_parse_json_node(options, tokens, i, json_chunk, &out_data->nodes[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_scene(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_scene* out_scene) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_scene->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "nodes") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_scene->nodes, &out_scene->nodes_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_scene->nodes_count; ++k) + { + out_scene->nodes[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + 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) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_scene->extensions_count, &out_scene->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_scenes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_scene), (void**)&out_data->scenes, &out_data->scenes_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->scenes_count; ++j) + { + i = cgltf_parse_json_scene(options, tokens, i, json_chunk, &out_data->scenes[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static 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) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "input") == 0) + { + ++i; + out_sampler->input = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "output") == 0) + { + ++i; + out_sampler->output = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "interpolation") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens + i, json_chunk, "LINEAR") == 0) + { + out_sampler->interpolation = cgltf_interpolation_type_linear; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "STEP") == 0) + { + out_sampler->interpolation = cgltf_interpolation_type_step; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "CUBICSPLINE") == 0) + { + out_sampler->interpolation = cgltf_interpolation_type_cubic_spline; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + 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) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sampler->extensions_count, &out_sampler->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static 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) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "sampler") == 0) + { + ++i; + out_channel->sampler = CGLTF_PTRINDEX(cgltf_animation_sampler, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int target_size = tokens[i].size; + ++i; + + for (int k = 0; k < target_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "node") == 0) + { + ++i; + out_channel->target_node = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "path") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens+i, json_chunk, "translation") == 0) + { + out_channel->target_path = cgltf_animation_path_type_translation; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "rotation") == 0) + { + out_channel->target_path = cgltf_animation_path_type_rotation; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "scale") == 0) + { + out_channel->target_path = cgltf_animation_path_type_scale; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "weights") == 0) + { + out_channel->target_path = cgltf_animation_path_type_weights; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + 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) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_channel->extensions_count, &out_channel->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_animation(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation* out_animation) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_animation->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "samplers") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_animation_sampler), (void**)&out_animation->samplers, &out_animation->samplers_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_animation->samplers_count; ++k) + { + i = cgltf_parse_json_animation_sampler(options, tokens, i, json_chunk, &out_animation->samplers[k]); + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "channels") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_animation_channel), (void**)&out_animation->channels, &out_animation->channels_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_animation->channels_count; ++k) + { + i = cgltf_parse_json_animation_channel(options, tokens, i, json_chunk, &out_animation->channels[k]); + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + 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) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_animation->extensions_count, &out_animation->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_animations(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_animation), (void**)&out_data->animations, &out_data->animations_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->animations_count; ++j) + { + i = cgltf_parse_json_animation(options, tokens, i, json_chunk, &out_data->animations[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_variant(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material_variant* out_variant) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_variant->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_variant->extras); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_variants(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_material_variant), (void**)&out_data->variants, &out_data->variants_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->variants_count; ++j) + { + i = cgltf_parse_json_variant(options, tokens, i, json_chunk, &out_data->variants[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_asset(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_asset* out_asset) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "copyright") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->copyright); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "generator") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->generator); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "version") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->version); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "minVersion") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->min_version); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + 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) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_asset->extensions_count, &out_asset->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + if (out_asset->version && CGLTF_ATOF(out_asset->version) < 2) + { + return CGLTF_ERROR_LEGACY; + } + + return i; +} + +cgltf_size cgltf_num_components(cgltf_type type) { + switch (type) + { + case cgltf_type_vec2: + return 2; + case cgltf_type_vec3: + return 3; + case cgltf_type_vec4: + return 4; + case cgltf_type_mat2: + return 4; + case cgltf_type_mat3: + return 9; + case cgltf_type_mat4: + return 16; + case cgltf_type_invalid: + case cgltf_type_scalar: + default: + return 1; + } +} + +cgltf_size cgltf_component_size(cgltf_component_type component_type) { + switch (component_type) + { + case cgltf_component_type_r_8: + case cgltf_component_type_r_8u: + return 1; + case cgltf_component_type_r_16: + case cgltf_component_type_r_16u: + return 2; + case cgltf_component_type_r_32u: + case cgltf_component_type_r_32f: + return 4; + case cgltf_component_type_invalid: + default: + return 0; + } +} + +cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type) +{ + cgltf_size component_size = cgltf_component_size(component_type); + if (type == cgltf_type_mat2 && component_size == 1) + { + return 8 * component_size; + } + else if (type == cgltf_type_mat3 && (component_size == 1 || component_size == 2)) + { + return 12 * component_size; + } + return component_size * cgltf_num_components(type); +} + +static int cgltf_fixup_pointers(cgltf_data* out_data); + +static int cgltf_parse_json_root(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "asset") == 0) + { + i = cgltf_parse_json_asset(options, tokens, i + 1, json_chunk, &out_data->asset); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "meshes") == 0) + { + i = cgltf_parse_json_meshes(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "accessors") == 0) + { + i = cgltf_parse_json_accessors(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "bufferViews") == 0) + { + i = cgltf_parse_json_buffer_views(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "buffers") == 0) + { + i = cgltf_parse_json_buffers(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "materials") == 0) + { + i = cgltf_parse_json_materials(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "images") == 0) + { + i = cgltf_parse_json_images(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "textures") == 0) + { + i = cgltf_parse_json_textures(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "samplers") == 0) + { + i = cgltf_parse_json_samplers(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "skins") == 0) + { + i = cgltf_parse_json_skins(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "cameras") == 0) + { + i = cgltf_parse_json_cameras(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "nodes") == 0) + { + i = cgltf_parse_json_nodes(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scenes") == 0) + { + i = cgltf_parse_json_scenes(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scene") == 0) + { + ++i; + out_data->scene = CGLTF_PTRINDEX(cgltf_scene, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "animations") == 0) + { + i = cgltf_parse_json_animations(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "extras") == 0) + { + 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) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_data->data_extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + out_data->data_extensions_count = 0; + out_data->data_extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!out_data->data_extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_lights_punctual") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + for (int m = 0; m < data_size; ++m) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "lights") == 0) + { + i = cgltf_parse_json_lights(options, tokens, i + 1, json_chunk, out_data); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_variants") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + for (int m = 0; m < data_size; ++m) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "variants") == 0) + { + i = cgltf_parse_json_variants(options, tokens, i + 1, json_chunk, out_data); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_data->data_extensions[out_data->data_extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensionsUsed") == 0) + { + i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_data->extensions_used, &out_data->extensions_used_count); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensionsRequired") == 0) + { + i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_data->extensions_required, &out_data->extensions_required_count); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data** out_data) +{ + jsmn_parser parser = { 0, 0, 0 }; + + if (options->json_token_count == 0) + { + int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, NULL, 0); + + if (token_count <= 0) + { + return cgltf_result_invalid_json; + } + + options->json_token_count = token_count; + } + + jsmntok_t* tokens = (jsmntok_t*)options->memory.alloc_func(options->memory.user_data, sizeof(jsmntok_t) * (options->json_token_count + 1)); + + if (!tokens) + { + return cgltf_result_out_of_memory; + } + + jsmn_init(&parser); + + int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, tokens, options->json_token_count); + + if (token_count <= 0) + { + options->memory.free_func(options->memory.user_data, tokens); + return cgltf_result_invalid_json; + } + + // this makes sure that we always have an UNDEFINED token at the end of the stream + // for invalid JSON inputs this makes sure we don't perform out of bound reads of token data + tokens[token_count].type = JSMN_UNDEFINED; + + cgltf_data* data = (cgltf_data*)options->memory.alloc_func(options->memory.user_data, sizeof(cgltf_data)); + + if (!data) + { + options->memory.free_func(options->memory.user_data, tokens); + return cgltf_result_out_of_memory; + } + + memset(data, 0, sizeof(cgltf_data)); + data->memory = options->memory; + data->file = options->file; + + int i = cgltf_parse_json_root(options, tokens, 0, json_chunk, data); + + options->memory.free_func(options->memory.user_data, tokens); + + if (i < 0) + { + cgltf_free(data); + + switch (i) + { + case CGLTF_ERROR_NOMEM: return cgltf_result_out_of_memory; + case CGLTF_ERROR_LEGACY: return cgltf_result_legacy_gltf; + default: return cgltf_result_invalid_gltf; + } + } + + if (cgltf_fixup_pointers(data) < 0) + { + cgltf_free(data); + return cgltf_result_invalid_gltf; + } + + data->json = (const char*)json_chunk; + data->json_size = size; + + *out_data = data; + + return cgltf_result_success; +} + +static int cgltf_fixup_pointers(cgltf_data* data) +{ + for (cgltf_size i = 0; i < data->meshes_count; ++i) + { + for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) + { + CGLTF_PTRFIXUP(data->meshes[i].primitives[j].indices, data->accessors, data->accessors_count); + CGLTF_PTRFIXUP(data->meshes[i].primitives[j].material, data->materials, data->materials_count); + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].attributes[k].data, data->accessors, data->accessors_count); + } + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) + { + for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].targets[k].attributes[m].data, data->accessors, data->accessors_count); + } + } + + if (data->meshes[i].primitives[j].has_draco_mesh_compression) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].draco_mesh_compression.buffer_view, data->buffer_views, data->buffer_views_count); + for (cgltf_size m = 0; m < data->meshes[i].primitives[j].draco_mesh_compression.attributes_count; ++m) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].draco_mesh_compression.attributes[m].data, data->accessors, data->accessors_count); + } + } + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].mappings[k].material, data->materials, data->materials_count); + } + } + } + + for (cgltf_size i = 0; i < data->accessors_count; ++i) + { + CGLTF_PTRFIXUP(data->accessors[i].buffer_view, data->buffer_views, data->buffer_views_count); + + if (data->accessors[i].is_sparse) + { + CGLTF_PTRFIXUP_REQ(data->accessors[i].sparse.indices_buffer_view, data->buffer_views, data->buffer_views_count); + CGLTF_PTRFIXUP_REQ(data->accessors[i].sparse.values_buffer_view, data->buffer_views, data->buffer_views_count); + } + + if (data->accessors[i].buffer_view) + { + data->accessors[i].stride = data->accessors[i].buffer_view->stride; + } + + if (data->accessors[i].stride == 0) + { + data->accessors[i].stride = cgltf_calc_size(data->accessors[i].type, data->accessors[i].component_type); + } + } + + for (cgltf_size i = 0; i < data->textures_count; ++i) + { + CGLTF_PTRFIXUP(data->textures[i].image, data->images, data->images_count); + CGLTF_PTRFIXUP(data->textures[i].basisu_image, data->images, data->images_count); + CGLTF_PTRFIXUP(data->textures[i].webp_image, data->images, data->images_count); + CGLTF_PTRFIXUP(data->textures[i].sampler, data->samplers, data->samplers_count); + } + + for (cgltf_size i = 0; i < data->images_count; ++i) + { + CGLTF_PTRFIXUP(data->images[i].buffer_view, data->buffer_views, data->buffer_views_count); + } + + for (cgltf_size i = 0; i < data->materials_count; ++i) + { + CGLTF_PTRFIXUP(data->materials[i].normal_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].emissive_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].occlusion_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].pbr_metallic_roughness.base_color_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].pbr_specular_glossiness.diffuse_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_roughness_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_normal_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].specular.specular_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].specular.specular_color_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].transmission.transmission_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].volume.thickness_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].sheen.sheen_color_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].sheen.sheen_roughness_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].iridescence.iridescence_thickness_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].anisotropy.anisotropy_texture.texture, data->textures, data->textures_count); + } + + for (cgltf_size i = 0; i < data->buffer_views_count; ++i) + { + CGLTF_PTRFIXUP_REQ(data->buffer_views[i].buffer, data->buffers, data->buffers_count); + + if (data->buffer_views[i].has_meshopt_compression) + { + CGLTF_PTRFIXUP_REQ(data->buffer_views[i].meshopt_compression.buffer, data->buffers, data->buffers_count); + } + } + + for (cgltf_size i = 0; i < data->skins_count; ++i) + { + for (cgltf_size j = 0; j < data->skins[i].joints_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->skins[i].joints[j], data->nodes, data->nodes_count); + } + + CGLTF_PTRFIXUP(data->skins[i].skeleton, data->nodes, data->nodes_count); + CGLTF_PTRFIXUP(data->skins[i].inverse_bind_matrices, data->accessors, data->accessors_count); + } + + for (cgltf_size i = 0; i < data->nodes_count; ++i) + { + for (cgltf_size j = 0; j < data->nodes[i].children_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->nodes[i].children[j], data->nodes, data->nodes_count); + + if (data->nodes[i].children[j]->parent) + { + return CGLTF_ERROR_JSON; + } + + data->nodes[i].children[j]->parent = &data->nodes[i]; + } + + CGLTF_PTRFIXUP(data->nodes[i].mesh, data->meshes, data->meshes_count); + CGLTF_PTRFIXUP(data->nodes[i].skin, data->skins, data->skins_count); + CGLTF_PTRFIXUP(data->nodes[i].camera, data->cameras, data->cameras_count); + CGLTF_PTRFIXUP(data->nodes[i].light, data->lights, data->lights_count); + + if (data->nodes[i].has_mesh_gpu_instancing) + { + for (cgltf_size m = 0; m < data->nodes[i].mesh_gpu_instancing.attributes_count; ++m) + { + CGLTF_PTRFIXUP_REQ(data->nodes[i].mesh_gpu_instancing.attributes[m].data, data->accessors, data->accessors_count); + } + } + } + + for (cgltf_size i = 0; i < data->scenes_count; ++i) + { + for (cgltf_size j = 0; j < data->scenes[i].nodes_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->scenes[i].nodes[j], data->nodes, data->nodes_count); + + if (data->scenes[i].nodes[j]->parent) + { + return CGLTF_ERROR_JSON; + } + } + } + + CGLTF_PTRFIXUP(data->scene, data->scenes, data->scenes_count); + + for (cgltf_size i = 0; i < data->animations_count; ++i) + { + for (cgltf_size j = 0; j < data->animations[i].samplers_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->animations[i].samplers[j].input, data->accessors, data->accessors_count); + CGLTF_PTRFIXUP_REQ(data->animations[i].samplers[j].output, data->accessors, data->accessors_count); + } + + for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->animations[i].channels[j].sampler, data->animations[i].samplers, data->animations[i].samplers_count); + CGLTF_PTRFIXUP(data->animations[i].channels[j].target_node, data->nodes, data->nodes_count); + } + } + + return 0; +} + +/* + * -- jsmn.c start -- + * Source: https://github.com/zserge/jsmn + * License: MIT + * + * Copyright (c) 2010 Serge A. Zaitsev + + * Permission 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: + + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + + * THE 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. + */ + +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, + ptrdiff_t start, ptrdiff_t end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + ptrdiff_t start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + + ptrdiff_t start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, size_t num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if(token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) return JSMN_ERROR_INVAL; + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +static void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} +/* + * -- jsmn.c end -- + */ + +#endif /* #ifdef CGLTF_IMPLEMENTATION */ + +/* cgltf is distributed under MIT license: + * + * Copyright (c) 2018-2021 Johannes Kuhlmann + + * Permission 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: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE 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/convmesh.c b/convmesh.c index e559824..1c6545d 100644 --- a/convmesh.c +++ b/convmesh.c @@ -1,229 +1,218 @@ #include #include -#include -/* px py pz nx ny nz u v */ -#define elpv 8 - -#define safe_malloc(p, s) \ - p = malloc(s); \ - if (!p) { \ - fprintf( \ - stderr, \ - "Out of memory.\n" \ - ); \ - return 3; \ - } +#define CGLTF_IMPLEMENTATION +#include "cgltf.h" typedef struct { unsigned short p, n, t; -} Vert; +} Vertex; -typedef struct { +struct { int vc, pc, nc, tc; + int* p, * n, * t; + Vertex* verts; +} result = { 0 }; + +struct { + unsigned short* ind; float* p, * n, * t; - Vert* verts; -} Model; + int ic, pc, nc, tc; +} intern = { 0 }; -int line_len(const char* line) { - const char* c; - int r = 0; - for (c = line; *c && *c != '\n'; c++, r++); - return r; +void parse_attrib( + float** d, + int* c, + int ec, + const cgltf_accessor* a +) { + int count, es = ec * 4; + const cgltf_buffer_view* v = a->buffer_view; + if (a->component_type != cgltf_component_type_r_32f) { + fprintf(stderr, "Only float attributes are supported.\n"); + exit(33); + } + if (ec * 4 != a->stride) { + fprintf(stderr, "Attribute stride doesn't match component count.\n"); + exit(34); + } + count = *c + a->count; + *d = realloc(*d, count * es); + memcpy( + &(*d)[*c], + &((char*)v->buffer->data)[v->offset], + a->count * es + ); + *c = count; } -const char* next_line(const char* line) { - const char* c; - for (c = line; *c != '\n'; c++) { - if (!*c) return 0; +void parse_prim_attribs(const cgltf_primitive* p) { + int i; + unsigned m = 0; + for (i = 0; i < p->attributes_count; i++) { + const cgltf_attribute* a = &p->attributes[i]; + switch (a->type) { + case cgltf_attribute_type_position: + parse_attrib(&intern.p, &intern.pc, 3, a->data); + m |= 0x1; + break; + case cgltf_attribute_type_normal: + parse_attrib(&intern.n, &intern.nc, 3, a->data); + m |= 0x2; + break; + case cgltf_attribute_type_texcoord: + parse_attrib(&intern.t, &intern.tc, 2, a->data); + m |= 0x4; + break; + default: break; + } + } + if (m != 0x7) { + fprintf(stderr, "Not all attributes were provided.\n"); + exit(32); } - return c + 1; } -int read_model(Model* m, const char* src) { - const char* line; - int pc = 0, nc = 0, tc = 0, vc = 0; - for (line = src; line; line = next_line(line)) { - if (line_len(line) < 6) continue; - if (!memcmp("vn", line, 2)) - nc++; - else if (!memcmp("vt", line, 2)) - tc++; - else if (!memcmp("v", line, 1)) - pc++; - else if (!memcmp("f", line, 1)) - vc++; +void parse_prim(const cgltf_primitive* p) { + int i, c, s; + const cgltf_accessor* a; + const cgltf_buffer_view* v; + if (p->type != cgltf_primitive_type_triangles) { + fprintf(stderr, "Only triangle meshes are supported.\n"); + exit(30); } - safe_malloc(m->p, pc * 3 * sizeof *m->p); - safe_malloc(m->n, nc * 3 * sizeof *m->n); - safe_malloc(m->t, tc * 2 * sizeof *m->t); - safe_malloc(m->verts, vc * 3 * sizeof *m->verts); - pc = - nc = - tc = 0; - m->vc = 0; - for (line = src; line; line = next_line(line)) { - int idx; - char* end; - if (!memcmp("vn", line, 2)) { - idx = nc++; - m->n[idx * 3 + 0] = (float)strtod(line + 3, &end); - m->n[idx * 3 + 1] = (float)strtod(end, &end); - m->n[idx * 3 + 2] = (float)strtod(end, &end); - } else if (!memcmp("vt", line, 2)) { - idx = tc++; - m->t[idx * 2 + 0] = (float)strtod(line + 3, &end); - m->t[idx * 2 + 1] = (float)strtod(end, &end); - } else if (!memcmp("v", line, 1)) { - idx = pc++; - m->p[idx * 3 + 0] = (float)strtod(line + 2, &end); - m->p[idx * 3 + 1] = (float)strtod(end, &end); - m->p[idx * 3 + 2] = (float)strtod(end, &end); - } else if (!memcmp("f", line, 1)) { - idx = m->vc++; - m->verts[idx].p = (int)strtol(line + 1, &end, 10) - 1; - m->verts[idx].t = (int)strtol(end + 1, &end, 10) - 1; - m->verts[idx].n = (int)strtol(end + 1, &end, 10) - 1; - idx = m->vc++; - m->verts[idx].p = (int)strtol(end + 1, &end, 10) - 1; - m->verts[idx].t = (int)strtol(end + 1, &end, 10) - 1; - m->verts[idx].n = (int)strtol(end + 1, &end, 10) - 1; - idx = m->vc++; - m->verts[idx].p = (int)strtol(end + 1, &end, 10) - 1; - m->verts[idx].t = (int)strtol(end + 1, &end, 10) - 1; - m->verts[idx].n = (int)strtol(end + 1, &end, 10) - 1; - } + a = p->indices; + if (!a) { + fprintf(stderr, "Meshes must be indexed.\n"); + exit(35); } - m->pc = pc; - m->nc = nc; - m->tc = tc; - return 0; -} -/* -int expand_model(Model* m, float** overt, int* ovc) { - int i, c, o; - float* v; - c = m->vc; - safe_malloc(v, c * elpv * sizeof *v); - for (i = o = 0; i < m->vc; i++, o += elpv) { - v[o + 0] = m->p[m->verts[i].p * 3 + 0]; - v[o + 1] = m->p[m->verts[i].p * 3 + 1]; - v[o + 2] = m->p[m->verts[i].p * 3 + 2]; - v[o + 3] = m->n[m->verts[i].n * 3 + 0]; - v[o + 4] = m->n[m->verts[i].n * 3 + 1]; - v[o + 5] = m->n[m->verts[i].n * 3 + 2]; - v[o + 6] = m->t[m->verts[i].t * 2 + 0]; - v[o + 7] = m->t[m->verts[i].t * 2 + 1]; + if (a->stride != 2) { + fprintf(stderr, "Only two byte indices are supported.\n"); + exit(31); } - *overt = v; - *ovc = c; - return 0; + v = a->buffer_view; + c = intern.ic + a->count; + intern.ind = realloc(intern.ind, c * sizeof *intern.ind); + memcpy( + &intern.ind[intern.ic], + &((char*)v->buffer->data)[v->offset], + a->count * sizeof *intern.ind + ); + intern.ic = c; + parse_prim_attribs(p); +} + +void parse_mesh(const cgltf_mesh* m) { + int i; + for (i = 0; i < m->primitives_count; i++) + parse_prim(&m->primitives[i]); } -void vert_cpy(float* dst, const float* src) { +int parse(const char* name) { int i; - for (i = 0; i < elpv; i++) - dst[i] = src[i]; + cgltf_options opt = { 0 }; + cgltf_data* d; + cgltf_result r; + r = cgltf_parse_file(&opt, name, &d); + if (r) return r; + r = cgltf_load_buffers(&opt, d, name); + if (r) return r; + for (i = 0; i < d->meshes_count; i++) + parse_mesh(&d->meshes[i]); + cgltf_free(d); + return 0; +} + +int to_fixed(float v) { + return (int)(v * 512.0f); } -int verts_eq(const float* a, const float* b) { +int attrib_eq(float* a, int* b, int ec) { int i; -#define absf(v) \ - ((v) < 0.0f? -(v): (v)) - for (i = 0; i < elpv; i++) - if (absf(a[i] - b[i]) > 0.0001f) + for (i = 0; i < ec; i++) { + int fpv = to_fixed(a[i]); + if (fpv != b[i]) return 0; + } return 1; -#undef absf } -int index_model( - float* vert, - int vc, - float** overt, - unsigned short** oind, - int* ovc, - int* oic +void convert_attrib( + float* s, + int ec, + unsigned short* di, + int* dc, + int* d ) { - float* ivert; - unsigned short* ind; - int i, ic, ivc; - safe_malloc(ivert, vc * elpv * sizeof *ivert); - safe_malloc(ind, vc * sizeof *ind); - for (i = ic = ivc = 0; i < vc; i++) { - int j, idx; - for (j = 0; j < ivc; j++) { - if (verts_eq(&vert[i * elpv], &ivert[j * elpv])) { - ind[ic++] = (unsigned short)j; - goto cont; - } + int i, idx, c = *dc; + for (i = 0; i < c; i++) { + int ei = i * ec; + if (attrib_eq(s, &d[ei], ec)) { + *di = i; + return; } - idx = ivc++; - vert_cpy(&ivert[idx * elpv], &vert[i * elpv]); - ind[ic++] = (unsigned short)idx; - cont: continue; } - *overt = ivert; - *oind = ind; - *ovc = ivc; - *oic = ic; - return 0; + idx = c++; + *dc = c; + *di = (unsigned short)idx; + for (i = 0; i < ec; i++) { + int iidx = i + idx * ec; + d[iidx] = to_fixed(s[i]); + } } -int write_model( - float* vert, - unsigned short* ind, - int vc, - int ic, - FILE* f -) { - int i, c; -#define write(b, s)\ - if (fwrite(b, 1, s, f) != s) return 42; - write(&vc, 4); - write(&ic, 4); - c = vc * elpv; - for (i = 0; i < c; i++) { - int fv = (int)(vert[i] * 512.0f); - write(&fv, 4); - } - write(ind, ic * sizeof *ind); - return 0; -}*/ +void convert_vert(int i) { + Vertex* d = &result.verts[result.vc++]; + int idx = intern.ind[i]; + convert_attrib( + &intern.p[idx * 3], + 3, + &d->p, + &result.pc, + result.p + ); + convert_attrib( + &intern.n[idx * 3], + 3, + &d->n, + &result.nc, + result.n + ); + convert_attrib( + &intern.t[idx * 2], + 2, + &d->t, + &result.tc, + result.t + ); +} -int write_model( - Model* m, - FILE* f -) { - int i, c, pad[8] = { 0 }; -#define write(b, s)\ - if (fwrite(b, 1, s, f) != s) return 42; - write(&m->vc, 4); - write(&m->pc, 4); - write(&m->nc, 4); - write(&m->tc, 4); - write(pad, 32); - for (i = 0, c = m->pc * 3; i < c; i++) { - int fv = (int)(m->p[i] * 512.0f); - write(&fv, 4); - } - for (i = 0, c = m->nc * 3; i < c; i++) { - int fv = (int)(m->n[i] * 512.0f); - write(&fv, 4); +void convert(void) { + int i; + result.p = malloc(intern.pc * sizeof *result.p * 3); + result.n = malloc(intern.nc * sizeof *result.n * 3); + result.t = malloc(intern.tc * sizeof *result.t * 2); + result.verts = malloc(intern.ic * sizeof *result.verts); + for (i = 0; i < intern.ic; i++) { + convert_vert(i); } - for (i = 0, c = m->tc * 2; i < c; i++) { - int fv = (int)(m->t[i] * 512.0f); - write(&fv, 4); +} + +void dump(const char* name) { + FILE* outfile = fopen(name, "wb"); + if (!outfile) { + fprintf(stderr, "Failed to open %s\n", name); + exit(35); } - write(m->verts, m->vc * sizeof *m->verts); - return 0; + fwrite(&result, 1, sizeof result, outfile); + fwrite(result.p, result.pc, sizeof* result.p * 3, outfile); + fwrite(result.n, result.nc, sizeof* result.n * 3, outfile); + fwrite(result.t, result.tc, sizeof* result.t * 2, outfile); + fwrite(result.verts, result.vc, sizeof* result.verts, outfile); } int main(int argc, const char** argv) { - FILE* infile, * outfile; - char* src; - Model m; - int s, r; + int r; if (argc < 3) { fprintf( stderr, @@ -232,36 +221,9 @@ int main(int argc, const char** argv) { ); return 1; } - infile = fopen(argv[1], "r"); - if (!infile) { - fprintf( - stderr, - "Can't open %s.\n", - argv[1] - ); - return 2; - } - fseek(infile, 0, SEEK_END); - s = ftell(infile); - rewind(infile); - safe_malloc(src, s + 1); - s = fread(src, 1, s, infile); - src[s] = 0; - if ((r = read_model(&m, src))) - return r; - free(src); - outfile = fopen(argv[2], "wb"); - if (!outfile) { - fprintf( - stderr, - "Can't open %s.\n", - argv[2] - ); - return 2; - } - if ((r = write_model(&m, outfile))) - return r; - fclose(infile); - fclose(outfile); + r = parse(argv[1]); + if (r) return r; + convert(); + dump(argv[2]); return 0; } diff --git a/intermediate/cube.obj b/intermediate/cube.obj deleted file mode 100644 index ba883db..0000000 --- a/intermediate/cube.obj +++ /dev/null @@ -1,45 +0,0 @@ -# Blender 4.0.2 -# www.blender.org -mtllib cube.mtl -o Cube -v -1.000000 -1.000000 1.000000 -v -1.000000 1.000000 1.000000 -v -1.000000 -1.000000 -1.000000 -v -1.000000 1.000000 -1.000000 -v 1.000000 -1.000000 1.000000 -v 1.000000 1.000000 1.000000 -v 1.000000 -1.000000 -1.000000 -v 1.000000 1.000000 -1.000000 -vn -1.0000 -0.0000 -0.0000 -vn -0.0000 -0.0000 -1.0000 -vn 1.0000 -0.0000 -0.0000 -vn -0.0000 -0.0000 1.0000 -vn -0.0000 -1.0000 -0.0000 -vn -0.0000 1.0000 -0.0000 -vt 0.625000 0.000000 -vt 0.375000 0.250000 -vt 0.375000 0.000000 -vt 0.625000 0.250000 -vt 0.375000 0.500000 -vt 0.625000 0.500000 -vt 0.375000 0.750000 -vt 0.625000 0.750000 -vt 0.375000 1.000000 -vt 0.125000 0.750000 -vt 0.125000 0.500000 -vt 0.875000 0.500000 -vt 0.625000 1.000000 -vt 0.875000 0.750000 -s 0 -f 2/1/1 3/2/1 1/3/1 -f 4/4/2 7/5/2 3/2/2 -f 8/6/3 5/7/3 7/5/3 -f 6/8/4 1/9/4 5/7/4 -f 7/5/5 1/10/5 3/11/5 -f 4/12/6 6/8/6 8/6/6 -f 2/1/1 4/4/1 3/2/1 -f 4/4/2 8/6/2 7/5/2 -f 8/6/3 6/8/3 5/7/3 -f 6/8/4 2/13/4 1/9/4 -f 7/5/5 5/7/5 1/10/5 -f 4/12/6 2/14/6 6/8/6 diff --git a/intermediate/gun.glb b/intermediate/gun.glb new file mode 100644 index 0000000..94dd57f Binary files /dev/null and b/intermediate/gun.glb differ diff --git a/intermediate/gun.obj b/intermediate/gun.obj deleted file mode 100644 index 62749e1..0000000 --- a/intermediate/gun.obj +++ /dev/null @@ -1,194 +0,0 @@ -# Blender 4.0.2 -# www.blender.org -o Cube -v -1.000000 -1.414214 1.427537 -v -1.000000 0.000000 2.841751 -v -1.000000 0.000000 0.000035 -v -0.018807 1.414214 1.427537 -v 9.078833 -1.414214 1.427537 -v 9.078833 0.000000 2.841751 -v 9.078833 0.000000 0.000035 -v 9.078833 1.414214 1.427537 -v -1.000000 -1.414214 -1.427468 -v -1.000000 0.000000 -2.841682 -v -0.018807 1.414214 -1.427468 -v 9.078833 -1.414214 -1.427468 -v 9.078833 0.000000 -2.841682 -v 9.078833 1.414214 -1.427468 -v 2.118911 1.414214 1.427537 -v 2.118911 1.414214 -1.427468 -v 4.303145 0.000000 0.000035 -v -1.577792 0.000003 -1.427468 -v -1.577792 -0.000023 1.427537 -v -0.018807 1.414214 0.227655 -v 2.118911 1.414214 0.227655 -v -0.018807 1.414214 -0.251127 -v 2.118911 1.414214 -0.251127 -v -0.018807 1.508344 0.053232 -v 1.741786 1.508344 0.053232 -v -0.018807 1.508344 -0.076704 -v 1.741786 1.508344 -0.076704 -vn -0.8659 -0.3538 0.3538 -vn 1.0000 -0.0000 -0.0000 -vn -0.0000 -0.7071 0.7071 -vn -0.0000 -0.7104 -0.7038 -vn -0.0000 0.7071 0.7071 -vn -0.8669 -0.3542 0.3509 -vn -0.0000 0.7071 -0.7071 -vn -0.0000 -0.7104 0.7038 -vn -0.0000 -0.7071 -0.7071 -vn -0.0000 0.7104 0.7038 -vn 0.5435 0.8394 -0.0000 -vn -0.0000 1.0000 -0.0000 -vn -0.0000 0.7104 -0.7038 -vn -0.8216 0.5700 -0.0000 -vn -0.6483 0.7147 -0.2624 -vn -0.6479 0.7142 -0.2647 -vn -0.6479 0.7142 0.2647 -vn -0.8659 -0.3538 -0.3538 -vn -0.6483 0.7147 0.2624 -vn -0.8669 -0.3542 -0.3509 -vn -0.0000 0.8800 0.4749 -vn -0.0000 0.8800 -0.4749 -vn -1.0000 -0.0000 -0.0000 -vn 0.2422 0.9702 -0.0000 -vt 0.255386 0.825942 -vt 0.127693 0.832658 -vt 0.182932 0.757520 -vt 0.568322 0.821496 -vt 0.437429 0.952390 -vt 0.436814 0.820881 -vt 0.738170 0.659627 -vt 0.869064 0.000000 -vt 0.869064 0.659627 -vt 0.344258 0.312553 -vt 0.475767 -0.000000 -vt 0.475767 0.659627 -vt 0.213994 0.241937 -vt 0.344258 0.037815 -vt 0.344258 0.697442 -vt -0.000000 0.825942 -vt 0.000000 0.697442 -vt 0.072455 0.757519 -vt 0.130264 0.102031 -vt 0.130264 0.241937 -vt 0.000000 0.697442 -vt 0.720608 0.800149 -vt 0.851502 0.931042 -vt 0.720608 0.931042 -vt 0.475768 0.659627 -vt 0.475768 0.347074 -vt 0.607277 -0.000000 -vt 0.607277 0.000000 -vt 0.607277 0.659627 -vt 0.869063 0.455504 -vt 1.000000 0.142951 -vt 1.000000 0.455504 -vt 0.436814 0.820881 -vt 0.526359 0.723278 -vt 0.500819 0.705124 -vt 0.781308 0.659627 -vt 0.720608 0.799534 -vt 0.720608 0.659627 -vt 1.000000 0.768058 -vt 0.869063 0.455504 -vt 1.000000 0.455504 -vt 0.344258 0.738155 -vt 0.344258 0.769490 -vt 0.436814 0.753052 -vt 0.209078 0.217256 -vt 0.202505 0.102031 -vt 0.209078 0.102031 -vt 0.720608 0.659627 -vt 0.720608 0.805463 -vt 0.654859 0.757278 -vt 0.000000 0.037815 -vt 0.071542 -0.000000 -vt 0.272716 -0.000000 -vt 0.213994 0.102031 -vt 0.127693 0.832658 -vt 0.654859 0.757277 -vt 0.589109 0.805463 -vt 0.589109 0.659627 -vt 0.255386 0.697442 -vt 0.781308 0.799534 -vt 0.794040 0.684309 -vt 0.794040 0.799534 -vt 0.189773 0.241937 -vt 0.189773 0.102031 -vt 0.202505 0.217256 -vt 0.261258 0.728776 -vt 0.255386 0.708857 -vt 0.261258 0.697442 -vt 0.213994 0.241937 -vt 0.568322 0.952390 -vt 0.738170 0.000000 -vt 0.344258 0.659627 -vt 0.344258 -0.000000 -vt 0.213994 0.697442 -vt 0.130264 0.697442 -vt 0.852117 0.799534 -vt 0.475768 0.000000 -vt 0.738170 0.000000 -vt 0.869063 0.000000 -vt 0.589109 0.767882 -vt 0.436814 0.659627 -vt 0.781308 0.799534 -vt 0.869064 0.911009 -vt 0.344258 0.659627 -vt 0.344258 0.846478 -vt 0.781308 0.659627 -vt 0.255386 0.717361 -s 0 -f 1/1/1 2/2/1 19/3/1 -f 8/4/2 5/5/2 7/6/2 -f 6/7/3 1/8/3 5/9/3 -f 17/10/4 5/11/4 1/12/4 -f 15/13/5 2/14/5 6/15/5 -f 9/16/6 3/17/6 18/18/6 -f 11/19/7 16/20/7 13/21/7 -f 14/22/2 12/23/2 13/24/2 -f 7/25/8 17/26/8 9/27/8 -f 13/28/9 9/7/9 10/29/9 -f 14/30/10 17/31/10 7/32/10 -f 17/33/11 23/34/11 21/35/11 -f 21/36/12 4/37/12 15/38/12 -f 17/39/13 8/40/13 7/41/13 -f 20/42/14 22/43/14 3/44/14 -f 25/45/12 26/46/12 24/47/12 -f 4/48/15 3/49/15 19/50/15 -f 11/19/16 10/51/16 18/52/16 -f 19/53/17 2/14/17 4/54/17 -f 18/18/18 10/55/18 9/16/18 -f 18/56/19 3/57/19 11/58/19 -f 19/3/20 3/59/20 1/1/20 -f 20/60/21 25/61/21 24/62/21 -f 23/63/12 11/19/12 22/64/12 -f 22/64/22 27/65/22 23/63/22 -f 20/66/23 26/67/23 22/68/23 -f 23/63/24 25/45/24 21/69/24 -f 8/4/2 6/70/2 5/5/2 -f 6/7/3 2/71/3 1/8/3 -f 1/12/4 3/72/4 17/10/4 -f 17/10/4 7/73/4 5/11/4 -f 6/15/5 8/74/5 15/13/5 -f 15/13/5 4/54/5 2/14/5 -f 13/21/7 10/51/7 11/19/7 -f 16/20/7 14/75/7 13/21/7 -f 14/22/2 7/76/2 12/23/2 -f 9/27/8 12/29/8 7/25/8 -f 17/26/8 3/77/8 9/27/8 -f 13/28/9 12/78/9 9/7/9 -f 14/30/10 16/79/10 17/31/10 -f 17/33/11 16/80/11 23/34/11 -f 21/35/11 15/81/11 17/33/11 -f 21/36/12 20/82/12 4/37/12 -f 17/39/13 15/83/13 8/40/13 -f 3/44/14 4/84/14 20/42/14 -f 22/43/14 11/85/14 3/44/14 -f 25/45/12 27/65/12 26/46/12 -f 20/60/21 21/86/21 25/61/21 -f 23/63/12 16/20/12 11/19/12 -f 22/64/22 26/46/22 27/65/22 -f 20/66/23 24/87/23 26/67/23 -f 23/63/24 27/65/24 25/45/24 diff --git a/intermediate/guy.fbx b/intermediate/guy.fbx deleted file mode 100644 index f144048..0000000 Binary files a/intermediate/guy.fbx and /dev/null differ diff --git a/intermediate/guy.glb b/intermediate/guy.glb new file mode 100644 index 0000000..3540c47 Binary files /dev/null and b/intermediate/guy.glb differ diff --git a/intermediate/monkey.obj b/intermediate/monkey.obj deleted file mode 100644 index 994a633..0000000 --- a/intermediate/monkey.obj +++ /dev/null @@ -1,2541 +0,0 @@ -# Blender 4.0.2 -# www.blender.org -mtllib monkey.mtl -o Suzanne -v 0.437500 0.164062 0.765625 -v -0.437500 0.164062 0.765625 -v 0.500000 0.093750 0.687500 -v -0.500000 0.093750 0.687500 -v 0.546875 0.054688 0.578125 -v -0.546875 0.054688 0.578125 -v 0.351562 -0.023438 0.617188 -v -0.351562 -0.023438 0.617188 -v 0.351562 0.031250 0.718750 -v -0.351562 0.031250 0.718750 -v 0.351562 0.132812 0.781250 -v -0.351562 0.132812 0.781250 -v 0.273438 0.164062 0.796875 -v -0.273438 0.164062 0.796875 -v 0.203125 0.093750 0.742188 -v -0.203125 0.093750 0.742188 -v 0.156250 0.054688 0.648438 -v -0.156250 0.054688 0.648438 -v 0.078125 0.242188 0.656250 -v -0.078125 0.242188 0.656250 -v 0.140625 0.242188 0.742188 -v -0.140625 0.242188 0.742188 -v 0.242188 0.242188 0.796875 -v -0.242188 0.242188 0.796875 -v 0.273438 0.328125 0.796875 -v -0.273438 0.328125 0.796875 -v 0.203125 0.390625 0.742188 -v -0.203125 0.390625 0.742188 -v 0.156250 0.437500 0.648438 -v -0.156250 0.437500 0.648438 -v 0.351562 0.515625 0.617188 -v -0.351562 0.515625 0.617188 -v 0.351562 0.453125 0.718750 -v -0.351562 0.453125 0.718750 -v 0.351562 0.359375 0.781250 -v -0.351562 0.359375 0.781250 -v 0.437500 0.328125 0.765625 -v -0.437500 0.328125 0.765625 -v 0.500000 0.390625 0.687500 -v -0.500000 0.390625 0.687500 -v 0.546875 0.437500 0.578125 -v -0.546875 0.437500 0.578125 -v 0.625000 0.242188 0.562500 -v -0.625000 0.242188 0.562500 -v 0.562500 0.242188 0.671875 -v -0.562500 0.242188 0.671875 -v 0.468750 0.242188 0.757812 -v -0.468750 0.242188 0.757812 -v 0.476562 0.242188 0.773438 -v -0.476562 0.242188 0.773438 -v 0.445312 0.335938 0.781250 -v -0.445312 0.335938 0.781250 -v 0.351562 0.375000 0.804688 -v -0.351562 0.375000 0.804688 -v 0.265625 0.335938 0.820312 -v -0.265625 0.335938 0.820312 -v 0.226562 0.242188 0.820312 -v -0.226562 0.242188 0.820312 -v 0.265625 0.156250 0.820312 -v -0.265625 0.156250 0.820312 -v 0.351562 0.242188 0.828125 -v -0.351562 0.242188 0.828125 -v 0.351562 0.117188 0.804688 -v -0.351562 0.117188 0.804688 -v 0.445312 0.156250 0.781250 -v -0.445312 0.156250 0.781250 -v 0.000000 0.429688 0.742188 -v 0.000000 0.351562 0.820312 -v 0.000000 -0.679688 0.734375 -v 0.000000 -0.320312 0.781250 -v 0.000000 -0.187500 0.796875 -v 0.000000 -0.773438 0.718750 -v 0.000000 0.406250 0.601562 -v 0.000000 0.570312 0.570312 -v 0.000000 0.898438 -0.546875 -v 0.000000 0.562500 -0.851562 -v 0.000000 0.070312 -0.828125 -v 0.000000 -0.382812 -0.351562 -v 0.203125 -0.187500 0.562500 -v -0.203125 -0.187500 0.562500 -v 0.312500 -0.437500 0.570312 -v -0.312500 -0.437500 0.570312 -v 0.351562 -0.695312 0.570312 -v -0.351562 -0.695312 0.570312 -v 0.367188 -0.890625 0.531250 -v -0.367188 -0.890625 0.531250 -v 0.328125 -0.945312 0.523438 -v -0.328125 -0.945312 0.523438 -v 0.179688 -0.968750 0.554688 -v -0.179688 -0.968750 0.554688 -v 0.000000 -0.984375 0.578125 -v 0.437500 -0.140625 0.531250 -v -0.437500 -0.140625 0.531250 -v 0.632812 -0.039062 0.539062 -v -0.632812 -0.039062 0.539062 -v 0.828125 0.148438 0.445312 -v -0.828125 0.148438 0.445312 -v 0.859375 0.429688 0.593750 -v -0.859375 0.429688 0.593750 -v 0.710938 0.484375 0.625000 -v -0.710938 0.484375 0.625000 -v 0.492188 0.601562 0.687500 -v -0.492188 0.601562 0.687500 -v 0.320312 0.757812 0.734375 -v -0.320312 0.757812 0.734375 -v 0.156250 0.718750 0.757812 -v -0.156250 0.718750 0.757812 -v 0.062500 0.492188 0.750000 -v -0.062500 0.492188 0.750000 -v 0.164062 0.414062 0.773438 -v -0.164062 0.414062 0.773438 -v 0.125000 0.304688 0.765625 -v -0.125000 0.304688 0.765625 -v 0.203125 0.093750 0.742188 -v -0.203125 0.093750 0.742188 -v 0.375000 0.015625 0.703125 -v -0.375000 0.015625 0.703125 -v 0.492188 0.062500 0.671875 -v -0.492188 0.062500 0.671875 -v 0.625000 0.187500 0.648438 -v -0.625000 0.187500 0.648438 -v 0.640625 0.296875 0.648438 -v -0.640625 0.296875 0.648438 -v 0.601562 0.375000 0.664062 -v -0.601562 0.375000 0.664062 -v 0.429688 0.437500 0.718750 -v -0.429688 0.437500 0.718750 -v 0.250000 0.468750 0.757812 -v -0.250000 0.468750 0.757812 -v 0.000000 -0.765625 0.734375 -v 0.109375 -0.718750 0.734375 -v -0.109375 -0.718750 0.734375 -v 0.117188 -0.835938 0.710938 -v -0.117188 -0.835938 0.710938 -v 0.062500 -0.882812 0.695312 -v -0.062500 -0.882812 0.695312 -v 0.000000 -0.890625 0.687500 -v 0.000000 -0.195312 0.750000 -v 0.000000 -0.140625 0.742188 -v 0.101562 -0.148438 0.742188 -v -0.101562 -0.148438 0.742188 -v 0.125000 -0.226562 0.750000 -v -0.125000 -0.226562 0.750000 -v 0.085938 -0.289062 0.742188 -v -0.085938 -0.289062 0.742188 -v 0.398438 -0.046875 0.671875 -v -0.398438 -0.046875 0.671875 -v 0.617188 0.054688 0.625000 -v -0.617188 0.054688 0.625000 -v 0.726562 0.203125 0.601562 -v -0.726562 0.203125 0.601562 -v 0.742188 0.375000 0.656250 -v -0.742188 0.375000 0.656250 -v 0.687500 0.414062 0.726562 -v -0.687500 0.414062 0.726562 -v 0.437500 0.546875 0.796875 -v -0.437500 0.546875 0.796875 -v 0.312500 0.640625 0.835938 -v -0.312500 0.640625 0.835938 -v 0.203125 0.617188 0.851562 -v -0.203125 0.617188 0.851562 -v 0.101562 0.429688 0.843750 -v -0.101562 0.429688 0.843750 -v 0.125000 -0.101562 0.812500 -v -0.125000 -0.101562 0.812500 -v 0.210938 -0.445312 0.710938 -v -0.210938 -0.445312 0.710938 -v 0.250000 -0.703125 0.687500 -v -0.250000 -0.703125 0.687500 -v 0.265625 -0.820312 0.664062 -v -0.265625 -0.820312 0.664062 -v 0.234375 -0.914062 0.632812 -v -0.234375 -0.914062 0.632812 -v 0.164062 -0.929688 0.632812 -v -0.164062 -0.929688 0.632812 -v 0.000000 -0.945312 0.640625 -v 0.000000 0.046875 0.726562 -v 0.000000 0.210938 0.765625 -v 0.328125 0.476562 0.742188 -v -0.328125 0.476562 0.742188 -v 0.164062 0.140625 0.750000 -v -0.164062 0.140625 0.750000 -v 0.132812 0.210938 0.757812 -v -0.132812 0.210938 0.757812 -v 0.117188 -0.687500 0.734375 -v -0.117188 -0.687500 0.734375 -v 0.078125 -0.445312 0.750000 -v -0.078125 -0.445312 0.750000 -v 0.000000 -0.445312 0.750000 -v 0.000000 -0.328125 0.742188 -v 0.093750 -0.273438 0.781250 -v -0.093750 -0.273438 0.781250 -v 0.132812 -0.226562 0.796875 -v -0.132812 -0.226562 0.796875 -v 0.109375 -0.132812 0.781250 -v -0.109375 -0.132812 0.781250 -v 0.039062 -0.125000 0.781250 -v -0.039062 -0.125000 0.781250 -v 0.000000 -0.203125 0.828125 -v 0.046875 -0.148438 0.812500 -v -0.046875 -0.148438 0.812500 -v 0.093750 -0.156250 0.812500 -v -0.093750 -0.156250 0.812500 -v 0.109375 -0.226562 0.828125 -v -0.109375 -0.226562 0.828125 -v 0.078125 -0.250000 0.804688 -v -0.078125 -0.250000 0.804688 -v 0.000000 -0.289062 0.804688 -v 0.257812 -0.312500 0.554688 -v -0.257812 -0.312500 0.554688 -v 0.164062 -0.242188 0.710938 -v -0.164062 -0.242188 0.710938 -v 0.179688 -0.312500 0.710938 -v -0.179688 -0.312500 0.710938 -v 0.234375 -0.250000 0.554688 -v -0.234375 -0.250000 0.554688 -v 0.000000 -0.875000 0.687500 -v 0.046875 -0.867188 0.687500 -v -0.046875 -0.867188 0.687500 -v 0.093750 -0.820312 0.710938 -v -0.093750 -0.820312 0.710938 -v 0.093750 -0.742188 0.726562 -v -0.093750 -0.742188 0.726562 -v 0.000000 -0.781250 0.656250 -v 0.093750 -0.750000 0.664062 -v -0.093750 -0.750000 0.664062 -v 0.093750 -0.812500 0.640625 -v -0.093750 -0.812500 0.640625 -v 0.046875 -0.851562 0.632812 -v -0.046875 -0.851562 0.632812 -v 0.000000 -0.859375 0.632812 -v 0.171875 0.218750 0.781250 -v -0.171875 0.218750 0.781250 -v 0.187500 0.156250 0.773438 -v -0.187500 0.156250 0.773438 -v 0.335938 0.429688 0.757812 -v -0.335938 0.429688 0.757812 -v 0.273438 0.421875 0.773438 -v -0.273438 0.421875 0.773438 -v 0.421875 0.398438 0.773438 -v -0.421875 0.398438 0.773438 -v 0.562500 0.351562 0.695312 -v -0.562500 0.351562 0.695312 -v 0.585938 0.289062 0.687500 -v -0.585938 0.289062 0.687500 -v 0.578125 0.195312 0.679688 -v -0.578125 0.195312 0.679688 -v 0.476562 0.101562 0.718750 -v -0.476562 0.101562 0.718750 -v 0.375000 0.062500 0.742188 -v -0.375000 0.062500 0.742188 -v 0.226562 0.109375 0.781250 -v -0.226562 0.109375 0.781250 -v 0.179688 0.296875 0.781250 -v -0.179688 0.296875 0.781250 -v 0.210938 0.375000 0.781250 -v -0.210938 0.375000 0.781250 -v 0.234375 0.359375 0.757812 -v -0.234375 0.359375 0.757812 -v 0.195312 0.296875 0.757812 -v -0.195312 0.296875 0.757812 -v 0.242188 0.125000 0.757812 -v -0.242188 0.125000 0.757812 -v 0.375000 0.085938 0.726562 -v -0.375000 0.085938 0.726562 -v 0.460938 0.117188 0.703125 -v -0.460938 0.117188 0.703125 -v 0.546875 0.210938 0.671875 -v -0.546875 0.210938 0.671875 -v 0.554688 0.281250 0.671875 -v -0.554688 0.281250 0.671875 -v 0.531250 0.335938 0.679688 -v -0.531250 0.335938 0.679688 -v 0.414062 0.390625 0.750000 -v -0.414062 0.390625 0.750000 -v 0.281250 0.398438 0.765625 -v -0.281250 0.398438 0.765625 -v 0.335938 0.406250 0.750000 -v -0.335938 0.406250 0.750000 -v 0.203125 0.171875 0.750000 -v -0.203125 0.171875 0.750000 -v 0.195312 0.226562 0.750000 -v -0.195312 0.226562 0.750000 -v 0.109375 0.460938 0.609375 -v -0.109375 0.460938 0.609375 -v 0.195312 0.664062 0.617188 -v -0.195312 0.664062 0.617188 -v 0.335938 0.687500 0.593750 -v -0.335938 0.687500 0.593750 -v 0.484375 0.554688 0.554688 -v -0.484375 0.554688 0.554688 -v 0.679688 0.453125 0.492188 -v -0.679688 0.453125 0.492188 -v 0.796875 0.406250 0.460938 -v -0.796875 0.406250 0.460938 -v 0.773438 0.164062 0.375000 -v -0.773438 0.164062 0.375000 -v 0.601562 0.000000 0.414062 -v -0.601562 0.000000 0.414062 -v 0.437500 -0.093750 0.468750 -v -0.437500 -0.093750 0.468750 -v 0.000000 0.898438 0.289062 -v 0.000000 0.984375 -0.078125 -v 0.000000 -0.195312 -0.671875 -v 0.000000 -0.460938 0.187500 -v 0.000000 -0.976562 0.460938 -v 0.000000 -0.804688 0.343750 -v 0.000000 -0.570312 0.320312 -v 0.000000 -0.484375 0.281250 -v 0.851562 0.234375 0.054688 -v -0.851562 0.234375 0.054688 -v 0.859375 0.320312 -0.046875 -v -0.859375 0.320312 -0.046875 -v 0.773438 0.265625 -0.437500 -v -0.773438 0.265625 -0.437500 -v 0.460938 0.437500 -0.703125 -v -0.460938 0.437500 -0.703125 -v 0.734375 -0.046875 0.070312 -v -0.734375 -0.046875 0.070312 -v 0.593750 -0.125000 -0.164062 -v -0.593750 -0.125000 -0.164062 -v 0.640625 -0.007812 -0.429688 -v -0.640625 -0.007812 -0.429688 -v 0.335938 0.054688 -0.664062 -v -0.335938 0.054688 -0.664062 -v 0.234375 -0.351562 0.406250 -v -0.234375 -0.351562 0.406250 -v 0.179688 -0.414062 0.257812 -v -0.179688 -0.414062 0.257812 -v 0.289062 -0.710938 0.382812 -v -0.289062 -0.710938 0.382812 -v 0.250000 -0.500000 0.390625 -v -0.250000 -0.500000 0.390625 -v 0.328125 -0.914062 0.398438 -v -0.328125 -0.914062 0.398438 -v 0.140625 -0.757812 0.367188 -v -0.140625 -0.757812 0.367188 -v 0.125000 -0.539062 0.359375 -v -0.125000 -0.539062 0.359375 -v 0.164062 -0.945312 0.437500 -v -0.164062 -0.945312 0.437500 -v 0.218750 -0.281250 0.429688 -v -0.218750 -0.281250 0.429688 -v 0.210938 -0.226562 0.468750 -v -0.210938 -0.226562 0.468750 -v 0.203125 -0.171875 0.500000 -v -0.203125 -0.171875 0.500000 -v 0.210938 -0.390625 0.164062 -v -0.210938 -0.390625 0.164062 -v 0.296875 -0.312500 -0.265625 -v -0.296875 -0.312500 -0.265625 -v 0.343750 -0.148438 -0.539062 -v -0.343750 -0.148438 -0.539062 -v 0.453125 0.867188 -0.382812 -v -0.453125 0.867188 -0.382812 -v 0.453125 0.929688 -0.070312 -v -0.453125 0.929688 -0.070312 -v 0.453125 0.851562 0.234375 -v -0.453125 0.851562 0.234375 -v 0.460938 0.523438 0.429688 -v -0.460938 0.523438 0.429688 -v 0.726562 0.406250 0.335938 -v -0.726562 0.406250 0.335938 -v 0.632812 0.453125 0.281250 -v -0.632812 0.453125 0.281250 -v 0.640625 0.703125 0.054688 -v -0.640625 0.703125 0.054688 -v 0.796875 0.562500 0.125000 -v -0.796875 0.562500 0.125000 -v 0.796875 0.617188 -0.117188 -v -0.796875 0.617188 -0.117188 -v 0.640625 0.750000 -0.195312 -v -0.640625 0.750000 -0.195312 -v 0.640625 0.679688 -0.445312 -v -0.640625 0.679688 -0.445312 -v 0.796875 0.539062 -0.359375 -v -0.796875 0.539062 -0.359375 -v 0.617188 0.328125 -0.585938 -v -0.617188 0.328125 -0.585938 -v 0.484375 0.023438 -0.546875 -v -0.484375 0.023438 -0.546875 -v 0.820312 0.328125 -0.203125 -v -0.820312 0.328125 -0.203125 -v 0.406250 -0.171875 0.148438 -v -0.406250 -0.171875 0.148438 -v 0.429688 -0.195312 -0.210938 -v -0.429688 -0.195312 -0.210938 -v 0.890625 0.406250 -0.234375 -v -0.890625 0.406250 -0.234375 -v 0.773438 -0.140625 -0.125000 -v -0.773438 -0.140625 -0.125000 -v 1.039062 -0.101562 -0.328125 -v -1.039062 -0.101562 -0.328125 -v 1.281250 0.054688 -0.429688 -v -1.281250 0.054688 -0.429688 -v 1.351562 0.320312 -0.421875 -v -1.351562 0.320312 -0.421875 -v 1.234375 0.507812 -0.421875 -v -1.234375 0.507812 -0.421875 -v 1.023438 0.476562 -0.312500 -v -1.023438 0.476562 -0.312500 -v 1.015625 0.414062 -0.289062 -v -1.015625 0.414062 -0.289062 -v 1.187500 0.437500 -0.390625 -v -1.187500 0.437500 -0.390625 -v 1.265625 0.289062 -0.406250 -v -1.265625 0.289062 -0.406250 -v 1.210938 0.078125 -0.406250 -v -1.210938 0.078125 -0.406250 -v 1.031250 -0.039062 -0.304688 -v -1.031250 -0.039062 -0.304688 -v 0.828125 -0.070312 -0.132812 -v -0.828125 -0.070312 -0.132812 -v 0.921875 0.359375 -0.218750 -v -0.921875 0.359375 -0.218750 -v 0.945312 0.304688 -0.289062 -v -0.945312 0.304688 -0.289062 -v 0.882812 -0.023438 -0.210938 -v -0.882812 -0.023438 -0.210938 -v 1.039062 0.000000 -0.367188 -v -1.039062 0.000000 -0.367188 -v 1.187500 0.093750 -0.445312 -v -1.187500 0.093750 -0.445312 -v 1.234375 0.250000 -0.445312 -v -1.234375 0.250000 -0.445312 -v 1.171875 0.359375 -0.437500 -v -1.171875 0.359375 -0.437500 -v 1.023438 0.343750 -0.359375 -v -1.023438 0.343750 -0.359375 -v 0.843750 0.289062 -0.210938 -v -0.843750 0.289062 -0.210938 -v 0.835938 0.171875 -0.273438 -v -0.835938 0.171875 -0.273438 -v 0.757812 0.093750 -0.273438 -v -0.757812 0.093750 -0.273438 -v 0.820312 0.085938 -0.273438 -v -0.820312 0.085938 -0.273438 -v 0.843750 0.015625 -0.273438 -v -0.843750 0.015625 -0.273438 -v 0.812500 -0.015625 -0.273438 -v -0.812500 -0.015625 -0.273438 -v 0.726562 0.000000 -0.070312 -v -0.726562 0.000000 -0.070312 -v 0.718750 -0.023438 -0.171875 -v -0.718750 -0.023438 -0.171875 -v 0.718750 0.039062 -0.187500 -v -0.718750 0.039062 -0.187500 -v 0.796875 0.203125 -0.210938 -v -0.796875 0.203125 -0.210938 -v 0.890625 0.242188 -0.265625 -v -0.890625 0.242188 -0.265625 -v 0.890625 0.234375 -0.320312 -v -0.890625 0.234375 -0.320312 -v 0.812500 -0.015625 -0.320312 -v -0.812500 -0.015625 -0.320312 -v 0.851562 0.015625 -0.320312 -v -0.851562 0.015625 -0.320312 -v 0.828125 0.078125 -0.320312 -v -0.828125 0.078125 -0.320312 -v 0.765625 0.093750 -0.320312 -v -0.765625 0.093750 -0.320312 -v 0.843750 0.171875 -0.320312 -v -0.843750 0.171875 -0.320312 -v 1.039062 0.328125 -0.414062 -v -1.039062 0.328125 -0.414062 -v 1.187500 0.343750 -0.484375 -v -1.187500 0.343750 -0.484375 -v 1.257812 0.242188 -0.492188 -v -1.257812 0.242188 -0.492188 -v 1.210938 0.085938 -0.484375 -v -1.210938 0.085938 -0.484375 -v 1.046875 0.000000 -0.421875 -v -1.046875 0.000000 -0.421875 -v 0.882812 -0.015625 -0.265625 -v -0.882812 -0.015625 -0.265625 -v 0.953125 0.289062 -0.343750 -v -0.953125 0.289062 -0.343750 -v 0.890625 0.109375 -0.328125 -v -0.890625 0.109375 -0.328125 -v 0.937500 0.062500 -0.335938 -v -0.937500 0.062500 -0.335938 -v 1.000000 0.125000 -0.367188 -v -1.000000 0.125000 -0.367188 -v 0.960938 0.171875 -0.351562 -v -0.960938 0.171875 -0.351562 -v 1.015625 0.234375 -0.375000 -v -1.015625 0.234375 -0.375000 -v 1.054688 0.187500 -0.382812 -v -1.054688 0.187500 -0.382812 -v 1.109375 0.210938 -0.390625 -v -1.109375 0.210938 -0.390625 -v 1.085938 0.273438 -0.390625 -v -1.085938 0.273438 -0.390625 -v 1.023438 0.437500 -0.484375 -v -1.023438 0.437500 -0.484375 -v 1.250000 0.468750 -0.546875 -v -1.250000 0.468750 -0.546875 -v 1.367188 0.296875 -0.500000 -v -1.367188 0.296875 -0.500000 -v 1.312500 0.054688 -0.531250 -v -1.312500 0.054688 -0.531250 -v 1.039062 -0.085938 -0.492188 -v -1.039062 -0.085938 -0.492188 -v 0.789062 -0.125000 -0.328125 -v -0.789062 -0.125000 -0.328125 -v 0.859375 0.382812 -0.382812 -v -0.859375 0.382812 -0.382812 -vn 0.9693 -0.0118 0.2456 -vn 0.6076 -0.5104 0.6085 -vn 0.8001 -0.0029 0.5999 -vn -0.6076 -0.5104 0.6085 -vn -0.9693 -0.0118 0.2456 -vn -0.8001 -0.0029 0.5999 -vn 0.6802 -0.5463 0.4888 -vn 0.8682 -0.0048 0.4961 -vn -0.6802 -0.5463 0.4888 -vn -0.8682 -0.0048 0.4961 -vn 0.1193 -0.8712 0.4763 -vn -0.1193 -0.8712 0.4763 -vn 0.7290 -0.6566 0.1934 -vn 0.0995 -0.7515 0.6522 -vn -0.0995 -0.7515 0.6522 -vn -0.7290 -0.6566 0.1934 -vn 0.0314 -0.9670 0.2529 -vn -0.4563 -0.5362 0.7101 -vn 0.4563 -0.5362 0.7101 -vn -0.0314 -0.9670 0.2529 -vn -0.5539 -0.6332 0.5406 -vn 0.5539 -0.6332 0.5406 -vn -0.6899 -0.0041 0.7239 -vn 0.6899 -0.0041 0.7239 -vn 0.8097 -0.0070 0.5868 -vn -0.6506 -0.6883 0.3210 -vn 0.6506 -0.6883 0.3210 -vn -0.9521 -0.0102 0.3057 -vn -0.4560 0.5222 0.7207 -vn 0.4560 0.5222 0.7207 -vn 0.9521 -0.0102 0.3057 -vn -0.8097 -0.0070 0.5868 -vn 0.5306 0.6258 0.5717 -vn 0.1031 0.7402 0.6644 -vn -0.5306 0.6258 0.5717 -vn -0.1031 0.7402 0.6644 -vn -0.1257 0.8416 0.5253 -vn 0.0258 0.9726 0.2312 -vn -0.6644 0.6821 0.3056 -vn -0.0258 0.9726 0.2312 -vn 0.7364 0.6521 0.1803 -vn -0.7364 0.6521 0.1803 -vn -0.6102 0.4956 0.6181 -vn 0.6102 0.4956 0.6181 -vn 0.1257 0.8416 0.5253 -vn -0.6682 0.5371 0.5148 -vn 0.6682 0.5371 0.5148 -vn 0.9645 -0.0127 0.2639 -vn -0.9645 -0.0127 0.2639 -vn -0.7216 0.6556 0.2224 -vn 0.7216 0.6556 0.2224 -vn -0.0432 0.9389 0.3415 -vn 0.0432 0.9389 0.3415 -vn 0.6644 0.6821 0.3056 -vn 0.6237 0.6285 0.4647 -vn -0.6237 0.6285 0.4647 -vn 0.9270 -0.0130 0.3749 -vn -0.6159 -0.6366 0.4641 -vn -0.9270 -0.0130 0.3749 -vn 0.6159 -0.6366 0.4641 -vn 0.0426 -0.9404 0.3375 -vn -0.0426 -0.9404 0.3375 -vn 0.7152 -0.6625 0.2227 -vn -0.7152 -0.6625 0.2227 -vn 0.1836 -0.0053 0.9830 -vn -0.1836 -0.0053 0.9830 -vn 0.1554 -0.7590 0.6323 -vn -0.0000 -0.9677 0.2523 -vn 0.1596 -0.9753 0.1529 -vn -0.1554 -0.7590 0.6323 -vn -0.0000 -0.7753 0.6316 -vn 0.3502 -0.6392 0.6847 -vn 0.5267 -0.8347 0.1611 -vn -0.3502 -0.6392 0.6847 -vn -0.1596 -0.9753 0.1529 -vn 0.9457 -0.2579 0.1977 -vn -0.9457 -0.2579 0.1977 -vn -0.5267 -0.8347 0.1611 -vn 0.9728 0.1003 0.2087 -vn 0.5557 -0.2264 0.8000 -vn -0.5557 -0.2264 0.8000 -vn -0.9728 0.1003 0.2087 -vn 0.9557 0.2492 0.1565 -vn 0.5652 -0.0297 0.8244 -vn -0.5652 -0.0297 0.8244 -vn -0.9557 0.2492 0.1565 -vn 0.8916 -0.3307 0.3095 -vn 0.3842 -0.5671 0.7286 -vn 0.0402 -0.2722 0.9614 -vn -0.3842 -0.5671 0.7286 -vn -0.8916 -0.3307 0.3095 -vn -0.0402 -0.2722 0.9614 -vn 0.5875 -0.7849 0.1970 -vn 0.3489 -0.9371 -0.0082 -vn -0.5875 -0.7849 0.1970 -vn -0.4991 -0.3761 0.7807 -vn 0.5666 -0.3188 0.7598 -vn 0.4991 -0.3761 0.7807 -vn -0.5666 -0.3188 0.7598 -vn 0.8451 0.4434 0.2985 -vn 0.9070 -0.4009 -0.1290 -vn -0.8451 0.4434 0.2985 -vn -0.4607 -0.1448 0.8757 -vn 0.5171 0.8291 0.2125 -vn 0.4607 -0.1448 0.8757 -vn -0.5171 0.8291 0.2125 -vn -0.4801 -0.1833 0.8578 -vn 0.5976 0.7847 0.1646 -vn 0.4801 -0.1833 0.8578 -vn -0.5976 0.7847 0.1646 -vn -0.3085 0.0039 0.9512 -vn 0.2666 0.2166 0.9392 -vn 0.3085 0.0039 0.9512 -vn -0.2666 0.2166 0.9392 -vn -0.6051 0.7680 0.2098 -vn 0.2313 0.9570 0.1751 -vn 0.6051 0.7680 0.2098 -vn 0.1574 0.1660 0.9735 -vn -0.8242 0.5468 0.1473 -vn -0.1574 0.1660 0.9735 -vn 0.8242 0.5468 0.1473 -vn 0.0611 -0.0253 0.9978 -vn -0.0000 0.9636 0.2673 -vn -0.0611 -0.0253 0.9978 -vn -0.0000 -0.0827 0.9966 -vn 0.2582 -0.1265 0.9578 -vn 0.3679 -0.2836 0.8856 -vn -0.2582 -0.1265 0.9578 -vn 0.1490 -0.1542 0.9767 -vn 0.2190 0.0372 0.9750 -vn -0.1490 -0.1542 0.9767 -vn 0.2254 -0.3608 0.9050 -vn -0.2190 0.0372 0.9750 -vn 0.3588 -0.1192 0.9258 -vn -0.2254 -0.3608 0.9050 -vn 0.4602 -0.1651 0.8723 -vn -0.3588 -0.1192 0.9258 -vn 0.4279 -0.3895 0.8156 -vn -0.4602 -0.1651 0.8723 -vn 0.3322 -0.3667 0.8690 -vn -0.4279 -0.3895 0.8156 -vn -0.1522 -0.2549 0.9549 -vn -0.3322 -0.3667 0.8690 -vn -0.0000 0.0643 0.9979 -vn 0.1522 -0.2549 0.9549 -vn 0.0316 -0.1782 0.9835 -vn -0.0316 -0.1782 0.9835 -vn -0.0000 -0.2220 0.9750 -vn -0.2006 -0.1350 0.9703 -vn 0.2006 -0.1350 0.9703 -vn -0.2393 -0.3012 0.9230 -vn 0.2393 -0.3012 0.9230 -vn -0.0589 -0.3784 0.9238 -vn 0.0589 -0.3784 0.9238 -vn 0.1307 -0.3187 0.9388 -vn -0.1307 -0.3187 0.9388 -vn 0.1460 -0.1202 0.9820 -vn 0.5937 0.1082 0.7974 -vn 0.1815 -0.0452 0.9823 -vn -0.1815 -0.0452 0.9823 -vn -0.5937 0.1082 0.7974 -vn -0.1460 -0.1202 0.9820 -vn -0.0000 -0.4760 0.8795 -vn 0.1341 0.0063 0.9909 -vn 0.5003 -0.4293 0.7520 -vn -0.1341 0.0063 0.9909 -vn -0.0000 -0.0000 1.0000 -vn -0.0000 -0.0341 0.9994 -vn -0.0000 -0.5870 0.8096 -vn 0.9304 -0.1242 0.3448 -vn 0.5836 -0.6929 0.4235 -vn -0.5836 -0.6929 0.4235 -vn -0.9304 -0.1242 0.3448 -vn -0.5003 -0.4293 0.7520 -vn 0.4931 -0.3412 0.8002 -vn 0.9306 -0.2353 0.2804 -vn -0.9306 -0.2353 0.2804 -vn -0.4931 -0.3412 0.8002 -vn -0.2405 0.9491 0.2036 -vn -0.1770 0.5084 0.8427 -vn 0.2405 0.9491 0.2036 -vn -0.6286 0.7688 0.1177 -vn -0.1950 0.8128 0.5489 -vn -1.0000 -0.0000 -0.0000 -vn -0.0000 -0.8654 0.5011 -vn -0.0000 -0.4815 0.8764 -vn -0.1833 -0.5864 0.7890 -vn -0.1858 0.5956 0.7815 -vn 0.1858 0.5956 0.7815 -vn 0.3611 0.4713 0.8047 -vn 0.6286 0.7688 0.1177 -vn -0.3611 0.4713 0.8047 -vn -0.4488 -0.3147 0.8364 -vn 0.1833 -0.5864 0.7890 -vn 0.4488 -0.3147 0.8364 -vn -0.0000 0.1578 0.9875 -vn 0.7752 0.0387 0.6306 -vn -0.7752 0.0387 0.6306 -vn -0.6507 0.1488 0.7447 -vn 0.6507 0.1488 0.7447 -vn 0.9278 0.3530 0.1209 -vn -0.9278 0.3530 0.1209 -vn 0.9306 0.3435 0.1263 -vn -0.9306 0.3435 0.1263 -vn -0.1369 -0.5273 0.8386 -vn 0.1369 -0.5273 0.8386 -vn -0.0000 -0.9619 0.2732 -vn -0.6351 0.0428 0.7712 -vn 0.6351 0.0428 0.7712 -vn -0.4141 0.5798 0.7016 -vn 0.4141 0.5798 0.7016 -vn -0.0000 -0.3465 0.9380 -vn -0.0000 0.5588 0.8293 -vn -0.0000 0.5334 0.8459 -vn 0.2959 0.4750 0.8288 -vn -0.6738 0.1155 0.7299 -vn -0.2959 0.4750 0.8288 -vn 0.6738 0.1155 0.7299 -vn -0.5177 -0.7041 0.4860 -vn 0.5177 -0.7041 0.4860 -vn -0.0000 -0.6989 0.7152 -vn -0.0101 -0.0700 0.9975 -vn 0.1581 -0.0843 0.9838 -vn 0.0101 -0.0700 0.9975 -vn -0.1581 -0.0843 0.9838 -vn 0.2934 -0.0602 0.9541 -vn -0.2934 -0.0602 0.9541 -vn 0.1588 -0.1065 0.9816 -vn -0.1588 -0.1065 0.9816 -vn 0.0317 -0.2198 0.9750 -vn 0.1845 -0.1863 0.9650 -vn -0.0317 -0.2198 0.9750 -vn -0.1845 -0.1863 0.9650 -vn 0.2990 -0.0356 0.9536 -vn -0.2990 -0.0356 0.9536 -vn 0.2943 -0.1020 0.9502 -vn -0.2943 -0.1020 0.9502 -vn 0.1776 -0.0608 0.9822 -vn -0.1776 -0.0608 0.9822 -vn -0.2944 0.0046 0.9557 -vn 0.2944 0.0046 0.9557 -vn -0.0887 -0.1272 0.9879 -vn 0.2036 0.1032 0.9736 -vn 0.0887 -0.1272 0.9879 -vn -0.2036 0.1032 0.9736 -vn 0.1435 0.0966 0.9849 -vn -0.1435 0.0966 0.9849 -vn 0.2886 -0.2786 0.9160 -vn -0.2886 -0.2786 0.9160 -vn -0.4508 -0.4658 0.7614 -vn 0.1133 -0.3142 0.9426 -vn -0.1133 -0.3142 0.9426 -vn -0.2741 -0.8556 0.4391 -vn 0.2741 -0.8556 0.4391 -vn -0.1423 -0.5826 0.8002 -vn 0.1423 -0.5826 0.8002 -vn -0.4229 -0.1078 0.8997 -vn 0.4229 -0.1078 0.8997 -vn -0.1921 0.1914 0.9625 -vn 0.1921 0.1914 0.9625 -vn -0.1653 0.6098 0.7751 -vn 0.1653 0.6098 0.7751 -vn 0.1431 0.5587 0.8169 -vn -0.1431 0.5587 0.8169 -vn 0.4323 0.5833 0.6877 -vn -0.4323 0.5833 0.6877 -vn 0.6881 0.2985 0.6614 -vn -0.6881 0.2985 0.6614 -vn 0.7894 -0.2032 0.5793 -vn 0.4508 -0.4658 0.7614 -vn -0.7894 -0.2032 0.5793 -vn 0.8016 0.0110 0.5977 -vn -0.8016 0.0110 0.5977 -vn -0.4603 0.8619 0.2127 -vn -0.0000 0.8592 0.5116 -vn 0.4603 0.8619 0.2127 -vn -0.4792 0.5120 -0.7129 -vn 0.4792 0.5120 -0.7129 -vn -0.2313 0.9570 0.1751 -vn -0.1217 0.6503 -0.7499 -vn 0.1217 0.6503 -0.7499 -vn -0.2275 0.8745 -0.4283 -vn 0.2275 0.8745 -0.4283 -vn -0.3456 0.9125 -0.2192 -vn 0.6957 0.5814 -0.4218 -vn 0.3456 0.9125 -0.2192 -vn -0.6957 0.5814 -0.4218 -vn -0.9070 -0.4009 -0.1290 -vn -0.9302 -0.3062 -0.2024 -vn 0.5444 -0.8372 -0.0533 -vn 0.9302 -0.3062 -0.2024 -vn -0.5444 -0.8372 -0.0533 -vn 0.4720 -0.8637 -0.1768 -vn -0.4720 -0.8637 -0.1768 -vn -0.0000 -0.7711 -0.6367 -vn 0.2771 -0.3147 -0.9078 -vn -0.0000 -0.2133 -0.9770 -vn -0.2771 -0.3147 -0.9078 -vn -0.6894 -0.6687 -0.2786 -vn 0.1514 -0.1510 -0.9769 -vn -0.0000 -0.2974 -0.9548 -vn -0.1514 -0.1510 -0.9769 -vn 0.0675 -0.7832 -0.6181 -vn -0.0000 -0.8818 -0.4716 -vn -0.0675 -0.7832 -0.6181 -vn 0.5551 -0.4762 -0.6820 -vn -0.5551 -0.4762 -0.6820 -vn 0.6204 0.0835 -0.7798 -vn -0.6204 0.0835 -0.7798 -vn 0.7799 -0.0105 -0.6259 -vn -0.7799 -0.0105 -0.6259 -vn 0.6894 -0.6687 -0.2786 -vn 0.8957 0.2578 -0.3624 -vn -0.8957 0.2578 -0.3624 -vn 0.9787 -0.1959 0.0615 -vn -0.9787 -0.1959 0.0615 -vn -0.8872 -0.1577 0.4336 -vn 0.7857 -0.5715 0.2368 -vn -0.7857 -0.5715 0.2368 -vn -0.3489 -0.9371 -0.0082 -vn 0.4455 -0.3584 -0.8204 -vn -0.0000 -0.6913 -0.7226 -vn -0.0000 -0.3049 -0.9524 -vn -0.4455 -0.3584 -0.8204 -vn -0.5223 -0.6536 -0.5477 -vn 0.5223 -0.6536 -0.5477 -vn -0.0000 -0.9417 -0.3365 -vn -0.5071 -0.8376 -0.2033 -vn 0.5727 -0.8197 0.0120 -vn -0.0000 -0.9831 -0.1833 -vn -0.5727 -0.8197 0.0120 -vn 0.7211 -0.6898 0.0651 -vn 0.9850 -0.1605 0.0631 -vn -0.7211 -0.6898 0.0651 -vn -0.9850 -0.1605 0.0631 -vn 0.4730 0.1763 -0.8632 -vn -0.0000 0.3650 -0.9310 -vn -0.4730 0.1763 -0.8632 -vn 0.4442 0.7244 0.5271 -vn -0.0000 0.9997 0.0226 -vn -0.0000 0.8306 0.5568 -vn -0.4442 0.7244 0.5271 -vn -0.4135 0.9096 0.0395 -vn 0.3913 0.8153 -0.4268 -vn -0.0000 0.8343 -0.5514 -vn -0.3913 0.8153 -0.4268 -vn 0.7717 0.6311 0.0785 -vn 0.4444 0.7886 0.4250 -vn -0.7717 0.6311 0.0785 -vn -0.4444 0.7886 0.4250 -vn 0.7418 0.5164 0.4279 -vn 0.6682 0.6719 0.3195 -vn -0.7418 0.5164 0.4279 -vn -0.6682 0.6719 0.3195 -vn 0.8486 0.5288 -0.0140 -vn 0.6784 0.7314 -0.0695 -vn -0.8486 0.5288 -0.0140 -vn -0.6784 0.7314 -0.0695 -vn 0.8722 0.3146 -0.3747 -vn 0.6075 0.5696 -0.5536 -vn -0.8722 0.3146 -0.3747 -vn -0.6075 0.5696 -0.5536 -vn 0.6197 -0.0605 -0.7825 -vn 0.6708 -0.0453 -0.7403 -vn -0.6197 -0.0605 -0.7825 -vn 0.4135 0.9096 0.0395 -vn 0.3406 0.8832 0.3223 -vn -0.3406 0.8832 0.3223 -vn -0.0000 0.5293 0.8485 -vn 0.9983 -0.0283 -0.0502 -vn -0.9983 -0.0283 -0.0502 -vn 0.8403 0.4934 0.2246 -vn -0.8403 0.4934 0.2246 -vn 0.5071 -0.8376 -0.2033 -vn 0.5790 -0.8027 0.1427 -vn -0.5790 -0.8027 0.1427 -vn -0.5633 -0.8173 -0.1213 -vn 0.3123 -0.9500 0.0012 -vn -0.3123 -0.9500 0.0012 -vn 0.8872 -0.1577 0.4336 -vn 0.3255 -0.6029 -0.7284 -vn -0.3255 -0.6029 -0.7284 -vn -0.5292 -0.5051 -0.6817 -vn 0.5633 -0.8173 -0.1213 -vn 0.5292 -0.5051 -0.6817 -vn -0.2793 0.7683 0.5759 -vn 0.5512 -0.0788 0.8307 -vn 0.0188 0.8723 0.4887 -vn 0.2793 0.7683 0.5759 -vn -0.5512 -0.0788 0.8307 -vn -0.4493 -0.0383 0.8926 -vn 0.3215 -0.0923 0.9424 -vn 0.3836 0.8630 0.3288 -vn -0.3215 -0.0923 0.9424 -vn -0.0188 0.8723 0.4887 -vn -0.3836 0.8630 0.3288 -vn 0.7788 0.1678 0.6044 -vn -0.7788 0.1678 0.6044 -vn 0.1545 -0.1239 0.9802 -vn -0.1545 -0.1239 0.9802 -vn 0.6526 -0.4768 0.5888 -vn -0.6526 -0.4768 0.5888 -vn 0.0411 0.3108 0.9496 -vn -0.0411 0.3108 0.9496 -vn 0.5029 -0.7810 0.3703 -vn -0.5029 -0.7810 0.3703 -vn -0.5384 0.2953 0.7893 -vn 0.3300 0.3157 0.8896 -vn 0.0295 -0.6350 0.7719 -vn -0.3300 0.3157 0.8896 -vn -0.0295 -0.6350 0.7719 -vn 0.5384 0.2953 0.7893 -vn 0.1629 0.8581 0.4870 -vn -0.1629 0.8581 0.4870 -vn -0.1868 0.9538 0.2351 -vn 0.1868 0.9538 0.2351 -vn -0.9848 -0.0996 0.1426 -vn 0.9848 -0.0996 0.1426 -vn 0.7622 0.6471 -0.0193 -vn -0.1496 -0.7455 0.6495 -vn 0.1496 -0.7455 0.6495 -vn 0.5605 -0.6609 0.4991 -vn -0.5605 -0.6609 0.4991 -vn 0.6842 -0.5558 0.4722 -vn -0.6842 -0.5558 0.4722 -vn 0.8572 -0.4931 -0.1483 -vn -0.8572 -0.4931 -0.1483 -vn -0.7312 0.1144 0.6725 -vn 0.7312 0.1144 0.6725 -vn 0.4493 -0.0383 0.8926 -vn 0.5998 0.5131 0.6139 -vn -0.5998 0.5131 0.6139 -vn 0.9610 -0.1188 0.2499 -vn 0.8420 -0.1763 0.5098 -vn -0.9610 -0.1188 0.2499 -vn 0.8515 0.0414 0.5228 -vn 0.4814 0.6344 0.6048 -vn -0.8420 -0.1763 0.5098 -vn -0.8515 0.0414 0.5228 -vn -0.4814 0.6344 0.6048 -vn 0.8303 -0.4790 0.2850 -vn 0.6864 -0.6234 0.3746 -vn -0.8303 -0.4790 0.2850 -vn 0.7261 -0.4989 0.4732 -vn 0.7949 -0.2332 0.5601 -vn -0.7261 -0.4989 0.4732 -vn -0.6864 -0.6234 0.3746 -vn -0.7949 -0.2332 0.5601 -vn 0.6593 -0.4685 0.5881 -vn 0.6482 -0.4206 0.6347 -vn -0.6593 -0.4685 0.5881 -vn -0.6482 -0.4206 0.6347 -vn -0.5725 -0.4189 0.7048 -vn 0.7584 0.2665 0.5948 -vn 0.5725 -0.4189 0.7048 -vn -0.7584 0.2665 0.5948 -vn -0.4492 0.3799 0.8086 -vn 0.4492 0.3799 0.8086 -vn -0.2929 0.3709 0.8813 -vn 0.6450 0.3102 0.6984 -vn 0.2929 0.3709 0.8813 -vn -0.6450 0.3102 0.6984 -vn -0.0331 0.9449 0.3256 -vn 0.0331 0.9449 0.3256 -vn 0.4618 -0.3291 0.8237 -vn -0.4618 -0.3291 0.8237 -vn -0.2624 -0.5331 0.8043 -vn 0.2624 -0.5331 0.8043 -vn -0.7529 -0.0338 0.6573 -vn 0.7529 -0.0338 0.6573 -vn -0.5831 0.4999 0.6403 -vn -0.7622 0.6471 -0.0193 -vn 0.5831 0.4999 0.6403 -vn 0.0650 0.7039 0.7074 -vn -0.0650 0.7039 0.7074 -vn 0.1951 0.0390 0.9800 -vn -0.1951 0.0390 0.9800 -vn -0.4085 0.1273 0.9039 -vn 0.4085 0.1273 0.9039 -vn 0.3347 -0.0046 0.9423 -vn -0.3347 -0.0046 0.9423 -vn -0.4448 -0.0937 0.8907 -vn 0.3144 -0.1038 0.9436 -vn 0.3343 0.1068 0.9364 -vn -0.3144 -0.1038 0.9436 -vn -0.3343 0.1068 0.9364 -vn 0.2897 0.3158 0.9035 -vn -0.2897 0.3158 0.9035 -vn -0.3831 -0.0685 0.9211 -vn 0.3831 -0.0685 0.9211 -vn -0.0989 -0.8408 -0.5322 -vn -0.0253 -0.6796 -0.7331 -vn 0.0989 -0.8408 -0.5322 -vn 0.0253 -0.6796 -0.7331 -vn 0.6366 -0.5043 -0.5834 -vn -0.6366 -0.5043 -0.5834 -vn 0.9253 0.0918 -0.3680 -vn -0.9253 0.0918 -0.3680 -vn 0.2870 0.5978 -0.7485 -vn -0.2870 0.5978 -0.7485 -vn -0.4142 0.5509 -0.7245 -vn 0.4142 0.5509 -0.7245 -vn -0.6501 0.5847 -0.4854 -vn 0.6501 0.5847 -0.4854 -vn -0.6708 -0.0453 -0.7403 -vn -0.3679 -0.2836 0.8856 -vn 0.4448 -0.0937 0.8907 -vt 0.898516 0.615168 -vt 0.917217 0.563713 -vt 0.938774 0.614911 -vt 0.816475 0.815667 -vt 0.834061 0.865960 -vt 0.795398 0.865726 -vt 0.951453 0.532635 -vt 0.985674 0.614766 -vt 0.781112 0.783437 -vt 0.746891 0.865568 -vt 0.865901 0.498414 -vt 0.866664 0.749216 -vt 0.889924 0.593688 -vt 0.866019 0.542156 -vt 0.866533 0.794590 -vt 0.842653 0.844479 -vt 0.866296 0.585096 -vt 0.814821 0.563713 -vt 0.916591 0.815667 -vt 0.866281 0.835888 -vt 0.780348 0.532635 -vt 0.952217 0.783437 -vt 0.793264 0.614911 -vt 0.937668 0.865726 -vt 0.986437 0.865568 -vt 0.844815 0.593688 -vt 0.887762 0.844479 -vt 0.836223 0.615168 -vt 0.814821 0.666108 -vt 0.916591 0.915784 -vt 0.896353 0.865960 -vt 0.746127 0.614766 -vt 0.952217 0.951120 -vt 0.866019 0.687665 -vt 0.780348 0.700318 -vt 0.866533 0.936861 -vt 0.866664 0.985341 -vt 0.866296 0.647388 -vt 0.844815 0.638796 -vt 0.866281 0.898180 -vt 0.889924 0.638796 -vt 0.842653 0.889588 -vt 0.816475 0.915784 -vt 0.917217 0.666108 -vt 0.865901 0.734539 -vt 0.781112 0.951120 -vt 0.951453 0.700318 -vt 0.890680 0.615278 -vt 0.841887 0.866078 -vt 0.847994 0.884400 -vt 0.884573 0.633599 -vt 0.866315 0.892033 -vt 0.866252 0.641233 -vt 0.887762 0.889588 -vt 0.883109 0.884400 -vt 0.849457 0.633599 -vt 0.890743 0.866078 -vt 0.849457 0.598483 -vt 0.841823 0.615278 -vt 0.883109 0.849284 -vt 0.866252 0.590849 -vt 0.866315 0.841650 -vt 0.884573 0.598483 -vt 0.847994 0.849284 -vt 0.866252 0.615278 -vt 0.866314 0.866078 -vt 0.515511 0.068449 -vt 0.499226 0.054411 -vt 0.519175 0.060448 -vt 0.482972 0.068490 -vt 0.499236 0.064527 -vt 0.521101 0.070625 -vt 0.528933 0.064984 -vt 0.477388 0.070678 -vt 0.479289 0.060503 -vt 0.531361 0.067741 -vt 0.467122 0.067822 -vt 0.469543 0.065062 -vt 0.543230 0.082916 -vt 0.524816 0.079366 -vt 0.473699 0.079422 -vt 0.455305 0.083023 -vt 0.564295 0.115333 -vt 0.527778 0.091040 -vt 0.470772 0.091098 -vt 0.434353 0.115495 -vt 0.585741 0.171832 -vt 0.599470 0.238064 -vt 0.534775 0.215506 -vt 0.399530 0.238288 -vt 0.413084 0.172042 -vt 0.464115 0.215568 -vt 0.649992 0.239957 -vt 0.625570 0.207851 -vt 0.349092 0.240294 -vt 0.363038 0.261221 -vt 0.655084 0.284130 -vt 0.636068 0.260938 -vt 0.344104 0.284413 -vt 0.683826 0.317538 -vt 0.688725 0.268431 -vt 0.315483 0.317795 -vt 0.340493 0.313152 -vt 0.670653 0.334491 -vt 0.658753 0.312921 -vt 0.328643 0.334694 -vt 0.350893 0.322437 -vt 0.636036 0.370629 -vt 0.648344 0.322238 -vt 0.363225 0.370732 -vt 0.386364 0.353108 -vt 0.593375 0.371207 -vt 0.612842 0.353000 -vt 0.405822 0.371277 -vt 0.580245 0.398434 -vt 0.601484 0.395542 -vt 0.418956 0.398468 -vt 0.422649 0.373341 -vt 0.531177 0.387347 -vt 0.576532 0.373285 -vt 0.467984 0.387365 -vt 0.460183 0.354339 -vt 0.499576 0.382860 -vt 0.538956 0.354297 -vt 0.499559 0.340417 -vt 0.578201 0.335550 -vt 0.558263 0.334249 -vt 0.420943 0.335646 -vt 0.591245 0.332772 -vt 0.605328 0.325379 -vt 0.407910 0.332885 -vt 0.627435 0.311447 -vt 0.393837 0.325516 -vt 0.634730 0.300287 -vt 0.371749 0.311636 -vt 0.630627 0.283646 -vt 0.364449 0.300507 -vt 0.610403 0.264181 -vt 0.368516 0.283889 -vt 0.591398 0.254700 -vt 0.388669 0.264411 -vt 0.558690 0.267603 -vt 0.407628 0.254897 -vt 0.499486 0.252445 -vt 0.440331 0.267717 -vt 0.543096 0.314455 -vt 0.455999 0.314523 -vt 0.499529 0.295691 -vt 0.542701 0.293295 -vt 0.456363 0.293371 -vt 0.549915 0.277739 -vt 0.449122 0.277832 -vt 0.505317 0.075368 -vt 0.493181 0.075382 -vt 0.510294 0.080611 -vt 0.488218 0.080633 -vt 0.510719 0.093440 -vt 0.536201 0.127706 -vt 0.512434 0.097171 -vt 0.486126 0.097191 -vt 0.462464 0.127779 -vt 0.487828 0.093457 -vt 0.499367 0.157864 -vt 0.512928 0.133399 -vt 0.518981 0.164589 -vt 0.485743 0.133422 -vt 0.499336 0.134352 -vt 0.499284 0.100063 -vt 0.499269 0.091346 -vt 0.529057 0.180195 -vt 0.516438 0.172335 -vt 0.482334 0.172353 -vt 0.469742 0.180239 -vt 0.479774 0.164615 -vt 0.522516 0.198783 -vt 0.520697 0.181536 -vt 0.478099 0.181560 -vt 0.476327 0.198817 -vt 0.507044 0.197287 -vt 0.499425 0.203867 -vt 0.491789 0.197295 -vt 0.481043 0.194728 -vt 0.499409 0.192167 -vt 0.499416 0.197278 -vt 0.499375 0.165170 -vt 0.499383 0.171718 -vt 0.486462 0.176949 -vt 0.507638 0.192399 -vt 0.491182 0.192407 -vt 0.514048 0.190694 -vt 0.517786 0.194706 -vt 0.484768 0.190709 -vt 0.483092 0.181873 -vt 0.512319 0.176937 -vt 0.515702 0.181858 -vt 0.499401 0.186182 -vt 0.542999 0.172734 -vt 0.455791 0.172814 -vt 0.458673 0.154899 -vt 0.540067 0.154824 -vt 0.577388 0.138981 -vt 0.421339 0.139176 -vt 0.582019 0.153952 -vt 0.416755 0.154155 -vt 0.508449 0.090711 -vt 0.490089 0.090724 -vt 0.499267 0.089977 -vt 0.508228 0.082219 -vt 0.490287 0.082236 -vt 0.503697 0.077074 -vt 0.494804 0.077083 -vt 0.499246 0.073497 -vt 0.499248 0.075473 -vt 0.499254 0.080320 -vt 0.496555 0.080840 -vt 0.504281 0.083193 -vt 0.501954 0.080835 -vt 0.494235 0.083202 -vt 0.505348 0.086781 -vt 0.493178 0.086791 -vt 0.499261 0.085637 -vt 0.561415 0.284224 -vt 0.558262 0.294780 -vt 0.437639 0.284338 -vt 0.440810 0.294882 -vt 0.562274 0.307050 -vt 0.436820 0.307151 -vt 0.569573 0.318594 -vt 0.429542 0.318697 -vt 0.430757 0.278245 -vt 0.588970 0.271589 -vt 0.568290 0.278113 -vt 0.410086 0.271772 -vt 0.601274 0.276977 -vt 0.397810 0.277180 -vt 0.616235 0.286959 -vt 0.382890 0.287175 -vt 0.617221 0.299000 -vt 0.381926 0.299197 -vt 0.384129 0.307438 -vt 0.615028 0.307259 -vt 0.401576 0.313725 -vt 0.589965 0.319671 -vt 0.597564 0.313582 -vt 0.409172 0.319797 -vt 0.581259 0.320877 -vt 0.417870 0.320991 -vt 0.583117 0.312767 -vt 0.416002 0.312892 -vt 0.421385 0.309394 -vt 0.588782 0.312203 -vt 0.410344 0.312336 -vt 0.595478 0.309103 -vt 0.403653 0.309249 -vt 0.605147 0.302344 -vt 0.393986 0.302517 -vt 0.606862 0.297335 -vt 0.392265 0.297520 -vt 0.606100 0.291021 -vt 0.393015 0.291216 -vt 0.597364 0.282892 -vt 0.401725 0.283083 -vt 0.588939 0.279989 -vt 0.410134 0.280167 -vt 0.575416 0.284480 -vt 0.423648 0.284624 -vt 0.571900 0.289783 -vt 0.427169 0.289915 -vt 0.572399 0.303223 -vt 0.577724 0.309274 -vt 0.426693 0.303342 -vt 0.571526 0.295591 -vt 0.427553 0.295716 -vt 0.528820 0.428638 -vt 0.499582 0.428449 -vt 0.470353 0.428636 -vt 0.583835 0.428591 -vt 0.415384 0.428598 -vt 0.397739 0.395588 -vt 0.377699 0.417889 -vt 0.621564 0.417863 -vt 0.333786 0.388954 -vt 0.665541 0.388861 -vt 0.298519 0.344467 -vt 0.711534 0.315224 -vt 0.700852 0.344252 -vt 0.287837 0.315527 -vt 0.310499 0.268805 -vt 0.295695 0.260225 -vt 0.672036 0.224472 -vt 0.703543 0.259804 -vt 0.327053 0.224880 -vt 0.639461 0.198488 -vt 0.359503 0.198841 -vt 0.623531 0.028944 -vt 0.597102 0.056491 -vt 0.601923 0.023020 -vt 0.374872 0.029461 -vt 0.401383 0.056841 -vt 0.364247 0.082198 -vt 0.554761 0.040719 -vt 0.545071 0.009261 -vt 0.396447 0.023452 -vt 0.443646 0.040906 -vt 0.525595 0.049432 -vt 0.499203 0.033223 -vt 0.453219 0.009442 -vt 0.472841 0.049514 -vt 0.535690 0.060692 -vt 0.462778 0.060794 -vt 0.556987 0.064968 -vt 0.441501 0.065137 -vt 0.586699 0.084908 -vt 0.411873 0.085177 -vt 0.634365 0.081724 -vt 0.605919 0.113619 -vt 0.392761 0.113940 -vt 0.611410 0.133352 -vt 0.387337 0.133675 -vt 0.394533 0.151503 -vt 0.599523 0.162726 -vt 0.399292 0.162985 -vt 0.373395 0.208159 -vt 0.858848 0.116790 -vt 0.860246 0.025793 -vt 0.921561 0.062683 -vt 0.140353 0.117994 -vt 0.138658 0.027230 -vt 0.176713 0.101559 -vt 0.822344 0.100435 -vt 0.773234 0.009669 -vt 0.225396 0.010837 -vt 0.234137 0.087001 -vt 0.660621 0.086994 -vt 0.645431 0.028106 -vt 0.338048 0.087561 -vt 0.352990 0.028716 -vt 0.742764 0.197916 -vt 0.765725 0.232421 -vt 0.256408 0.198572 -vt 0.233590 0.233065 -vt 0.888944 0.176456 -vt 0.987287 0.152728 -vt 0.110522 0.177602 -vt 0.077626 0.064228 -vt 0.844154 0.346818 -vt 0.959159 0.356531 -vt 0.900013 0.420388 -vt 0.155683 0.347370 -vt 0.041037 0.357385 -vt 0.118831 0.309153 -vt 0.903058 0.260405 -vt 0.999860 0.252255 -vt 0.096698 0.261367 -vt 0.000140 0.253531 -vt 0.012367 0.154243 -vt 0.724230 0.339961 -vt 0.744163 0.304222 -vt 0.765650 0.319075 -vt 0.255275 0.304629 -vt 0.275237 0.340212 -vt 0.233887 0.319494 -vt 0.792936 0.279898 -vt 0.826311 0.297169 -vt 0.206573 0.280495 -vt 0.173337 0.297810 -vt 0.823141 0.253863 -vt 0.854975 0.267153 -vt 0.176380 0.254612 -vt 0.144669 0.267956 -vt 0.835330 0.224364 -vt 0.867888 0.232592 -vt 0.164140 0.225225 -vt 0.131691 0.233525 -vt 0.850667 0.180124 -vt 0.812814 0.189821 -vt 0.148712 0.181145 -vt 0.881002 0.308389 -vt 0.771964 0.373733 -vt 0.744181 0.389903 -vt 0.227737 0.374010 -vt 0.824891 0.470849 -vt 0.100193 0.420886 -vt 0.792153 0.464650 -vt 0.726584 0.432597 -vt 0.273145 0.432574 -vt 0.207859 0.464714 -vt 0.255481 0.390057 -vt 0.175235 0.470987 -vt 0.795638 0.489991 -vt 0.204362 0.489991 -vt 0.785486 0.227075 -vt 0.213863 0.227788 -vt 0.802275 0.202593 -vt 0.197050 0.203415 -vt 0.764738 0.086041 -vt 0.690398 0.146106 -vt 0.308516 0.146686 -vt 0.230666 0.128771 -vt 0.779285 0.168550 -vt 0.219887 0.169386 -vt 0.604255 0.151220 -vt 0.803020 0.170278 -vt 0.196211 0.171181 -vt 0.169083 0.139518 -vt 0.768354 0.127888 -vt 0.830115 0.138457 -vt 0.449940 0.959188 -vt 0.530867 0.884581 -vt 0.560056 0.957116 -vt 0.285528 0.959188 -vt 0.204610 0.884582 -vt 0.272722 0.891853 -vt 0.590562 0.846978 -vt 0.701323 0.897070 -vt 0.144915 0.846977 -vt 0.175421 0.957118 -vt 0.034155 0.897069 -vt 0.669298 0.757013 -vt 0.066170 0.757009 -vt 0.144687 0.784410 -vt 0.590787 0.784411 -vt 0.603293 0.719157 -vt 0.132179 0.719155 -vt 0.175212 0.744211 -vt 0.560262 0.744213 -vt 0.554154 0.682132 -vt 0.181319 0.682131 -vt 0.215052 0.712528 -vt 0.468433 0.686792 -vt 0.493965 0.641024 -vt 0.267042 0.686791 -vt 0.241508 0.641024 -vt 0.520423 0.712528 -vt 0.464308 0.719593 -vt 0.271168 0.719592 -vt 0.498843 0.736977 -vt 0.236633 0.736977 -vt 0.543027 0.789442 -vt 0.192449 0.789442 -vt 0.208931 0.760754 -vt 0.539269 0.821849 -vt 0.196208 0.821849 -vt 0.510099 0.842380 -vt 0.225378 0.842380 -vt 0.470621 0.845320 -vt 0.264856 0.845322 -vt 0.769420 0.190471 -vt 0.229792 0.191225 -vt 0.213850 0.181127 -vt 0.395036 0.674023 -vt 0.393578 0.587505 -vt 0.340437 0.674021 -vt 0.385726 0.910555 -vt 0.462753 0.891851 -vt 0.349744 0.910558 -vt 0.429058 0.713731 -vt 0.306418 0.713731 -vt 0.378240 0.710772 -vt 0.415153 0.728622 -vt 0.357237 0.710776 -vt 0.408407 0.754607 -vt 0.418068 0.746526 -vt 0.320324 0.728623 -vt 0.327070 0.754610 -vt 0.317409 0.746528 -vt 0.377006 0.815186 -vt 0.413147 0.796172 -vt 0.358471 0.815189 -vt 0.433705 0.830975 -vt 0.399584 0.878316 -vt 0.301772 0.830977 -vt 0.322330 0.796174 -vt 0.335895 0.878320 -vt 0.801893 0.198928 -vt 0.197420 0.199758 -vt 0.797000 0.194826 -vt 0.202290 0.195652 -vt 0.210391 0.184671 -vt 0.785370 0.180300 -vt 0.788848 0.183842 -vt 0.451471 0.804090 -vt 0.475926 0.816075 -vt 0.284007 0.804092 -vt 0.259551 0.816076 -vt 0.302007 0.783922 -vt 0.424174 0.764959 -vt 0.433470 0.783920 -vt 0.311304 0.764961 -vt 0.304833 0.756352 -vt 0.430644 0.756350 -vt 0.300230 0.743459 -vt 0.441735 0.732686 -vt 0.435247 0.743457 -vt 0.293742 0.732687 -vt 0.460899 0.740871 -vt 0.274578 0.740871 -vt 0.499639 0.817768 -vt 0.235838 0.817769 -vt 0.513389 0.808597 -vt 0.222088 0.808598 -vt 0.515507 0.790754 -vt 0.219970 0.790754 -vt 0.504048 0.772385 -vt 0.526545 0.760754 -vt 0.231428 0.772385 -vt 0.484096 0.755033 -vt 0.251380 0.755033 -vt 0.445088 0.770458 -vt 0.290389 0.770460 -vt 0.282020 0.756172 -vt 0.453457 0.756171 -vt 0.462782 0.785392 -vt 0.272695 0.785393 -vt 0.262882 0.769899 -vt 0.490045 0.782999 -vt 0.481973 0.797121 -vt 0.245432 0.782999 -vt 0.253504 0.797122 -vt 0.499142 0.804019 -vt 0.236336 0.804020 -vt 0.233912 0.792591 -vt 0.501565 0.792591 -vt 0.559696 0.607669 -vt 0.598936 0.655817 -vt 0.175778 0.607667 -vt 0.136537 0.655816 -vt 0.629457 0.693271 -vt 0.106015 0.693269 -vt 0.653588 0.712098 -vt 0.081883 0.712096 -vt 0.662010 0.686010 -vt 0.712556 0.698473 -vt 0.073462 0.686010 -vt 0.652439 0.640199 -vt 0.715231 0.624373 -vt 0.022915 0.698478 -vt 0.083034 0.640197 -vt 0.633578 0.594596 -vt 0.706720 0.572934 -vt 0.020244 0.624367 -vt 0.101895 0.594596 -vt 0.703133 0.538140 -vt 0.615458 0.557583 -vt 0.032336 0.538145 -vt 0.028756 0.572934 -vt 0.512305 0.539237 -vt 0.120011 0.557584 -vt 0.223171 0.539233 -vt 0.341892 0.587509 -vt 0.440865 0.334326 -vt 0.186501 0.190705 -vt 0.742036 0.461609 -vt 0.257819 0.461543 -vt 0.472595 0.769899 -s 1 -f 47/1/1 3/2/2 45/3/3 -f 4/4/4 48/5/5 46/6/6 -f 45/3/3 5/7/7 43/8/8 -f 6/9/9 46/6/6 44/10/10 -f 3/2/2 7/11/11 5/7/7 -f 8/12/12 4/4/4 6/9/9 -f 1/13/13 9/14/14 3/2/2 -f 10/15/15 2/16/16 4/4/4 -f 11/17/17 15/18/18 9/14/14 -f 16/19/19 12/20/20 10/15/15 -f 9/14/14 17/21/21 7/11/11 -f 18/22/22 10/15/15 8/12/12 -f 21/23/23 17/21/21 15/18/18 -f 22/24/24 18/22/22 20/25/25 -f 13/26/26 21/23/23 15/18/18 -f 22/24/24 14/27/27 16/19/19 -f 23/28/28 27/29/29 21/23/23 -f 28/30/30 24/31/31 22/24/24 -f 27/29/29 19/32/32 21/23/23 -f 28/30/30 20/25/25 30/33/33 -f 33/34/34 29/35/35 27/29/29 -f 34/36/36 30/33/33 32/37/37 -f 35/38/38 27/29/29 25/39/39 -f 36/40/40 28/30/30 34/36/36 -f 37/41/41 33/34/34 35/38/38 -f 38/42/42 34/36/36 40/43/43 -f 39/44/44 31/45/45 33/34/34 -f 40/43/43 32/37/37 42/46/46 -f 45/3/3 41/47/47 39/44/44 -f 46/6/6 42/46/46 44/10/10 -f 47/1/1 39/44/44 37/41/41 -f 48/5/5 40/43/43 46/6/6 -f 37/41/41 49/48/48 47/1/1 -f 38/42/42 50/49/49 52/50/50 -f 35/38/38 51/51/51 37/41/41 -f 36/40/40 52/50/50 54/52/52 -f 25/39/39 53/53/53 35/38/38 -f 26/54/54 54/52/52 56/55/55 -f 23/28/28 55/56/56 25/39/39 -f 24/31/31 56/55/55 58/57/57 -f 23/28/28 59/58/58 57/59/59 -f 60/60/60 24/31/31 58/57/57 -f 13/26/26 63/61/61 59/58/58 -f 64/62/62 14/27/27 60/60/60 -f 11/17/17 65/63/63 63/61/61 -f 66/64/64 12/20/20 64/62/62 -f 1/13/13 49/48/48 65/63/63 -f 50/49/49 2/16/16 66/64/64 -f 61/65/65 65/63/63 49/48/48 -f 50/49/49 66/64/64 62/66/66 -f 63/61/61 65/63/63 61/65/65 -f 62/66/66 66/64/64 64/62/62 -f 61/65/65 59/58/58 63/61/61 -f 64/62/62 60/60/60 62/66/66 -f 61/65/65 57/59/59 59/58/58 -f 60/60/60 58/57/57 62/66/66 -f 61/65/65 55/56/56 57/59/59 -f 58/57/57 56/55/55 62/66/66 -f 61/65/65 53/53/53 55/56/56 -f 56/55/55 54/52/52 62/66/66 -f 61/65/65 51/51/51 53/53/53 -f 54/52/52 52/50/50 62/66/66 -f 61/65/65 49/48/48 51/51/51 -f 52/50/50 50/49/49 62/66/66 -f 174/67/67 91/68/68 89/69/69 -f 175/70/70 91/68/68 176/71/71 -f 172/72/72 89/69/69 87/73/73 -f 173/74/74 90/75/75 175/70/70 -f 85/76/76 172/72/72 87/73/73 -f 173/74/74 86/77/77 88/78/78 -f 83/79/79 170/80/80 85/76/76 -f 171/81/81 84/82/82 86/77/77 -f 81/83/83 168/84/84 83/79/79 -f 169/85/85 82/86/86 84/82/82 -f 79/87/87 146/88/88 164/89/89 -f 147/90/90 80/91/91 165/92/92 -f 94/93/93 146/88/88 92/94/94 -f 95/95/95 147/90/90 149/96/96 -f 94/93/93 150/97/97 148/98/98 -f 151/99/99 95/95/95 149/96/96 -f 98/100/100 150/97/97 96/101/101 -f 99/102/102 151/99/99 153/103/103 -f 100/104/104 152/105/105 98/100/100 -f 101/106/106 153/103/103 155/107/107 -f 102/108/108 154/109/109 100/104/104 -f 103/110/110 155/107/107 157/111/111 -f 102/108/108 158/112/112 156/113/113 -f 159/114/114 103/110/110 157/111/111 -f 106/115/115 158/112/112 104/116/116 -f 107/117/117 159/114/114 161/118/118 -f 108/119/119 160/120/120 106/115/115 -f 109/121/121 161/118/118 163/122/122 -f 67/123/123 162/124/124 108/119/119 -f 67/123/123 163/122/122 68/125/125 -f 128/126/126 162/124/124 110/127/127 -f 129/128/128 163/122/122 161/118/118 -f 128/126/126 158/112/112 160/120/120 -f 159/114/114 129/128/128 161/118/118 -f 156/113/113 179/129/129 126/130/130 -f 157/111/111 180/131/131 159/114/114 -f 154/109/109 126/130/130 124/132/132 -f 155/107/107 127/133/133 157/111/111 -f 152/105/105 124/132/132 122/134/134 -f 153/103/103 125/135/135 155/107/107 -f 150/97/97 122/134/134 120/136/136 -f 151/99/99 123/137/137 153/103/103 -f 148/98/98 120/136/136 118/138/138 -f 149/96/96 121/139/139 151/99/99 -f 146/88/88 118/138/138 116/140/140 -f 147/90/90 119/141/141 149/96/96 -f 164/89/89 116/140/140 114/142/142 -f 165/92/92 117/143/143 147/90/90 -f 114/142/142 177/144/144 164/89/89 -f 177/144/144 115/145/145 165/92/92 -f 162/124/124 112/146/146 110/127/127 -f 163/122/122 113/147/147 68/125/125 -f 112/146/146 178/148/148 183/149/149 -f 178/148/148 113/147/147 184/150/150 -f 181/151/151 178/148/148 177/144/144 -f 182/152/152 178/148/148 184/150/150 -f 135/153/153 176/71/71 174/67/67 -f 176/71/71 136/154/154 175/70/70 -f 133/155/155 174/67/67 172/72/72 -f 175/70/70 134/156/156 173/74/74 -f 133/155/155 170/80/80 131/157/157 -f 134/156/156 171/81/81 173/74/74 -f 166/158/158 185/159/159 168/84/84 -f 186/160/160 167/161/161 169/85/85 -f 131/157/157 168/84/84 185/159/159 -f 169/85/85 132/162/162 186/160/160 -f 190/163/163 187/164/164 144/165/165 -f 190/163/163 188/166/166 189/167/167 -f 187/164/164 69/168/168 185/159/159 -f 188/166/166 69/168/168 189/167/167 -f 131/157/157 69/168/168 130/169/169 -f 132/162/162 69/168/168 186/160/160 -f 142/170/170 191/171/171 144/165/165 -f 192/172/172 143/173/173 145/174/174 -f 140/175/175 193/176/176 142/170/170 -f 194/177/177 141/178/178 143/173/173 -f 197/179/179 140/175/175 139/180/180 -f 198/181/181 141/178/178 196/182/182 -f 71/183/183 139/180/180 138/184/184 -f 71/183/183 139/180/180 198/181/181 -f 144/165/165 70/185/185 190/163/163 -f 145/174/174 70/185/185 192/172/172 -f 191/171/171 208/186/186 70/185/185 -f 192/172/172 208/186/186 207/187/187 -f 71/183/183 200/188/188 197/179/179 -f 201/189/189 71/183/183 198/181/181 -f 197/179/179 202/190/190 195/191/191 -f 203/192/192 198/181/181 196/182/182 -f 202/190/190 193/176/176 195/191/191 -f 203/192/192 194/177/177 205/193/193 -f 193/176/176 206/194/194 191/171/171 -f 207/187/187 194/177/177 192/172/172 -f 204/195/195 200/188/188 199/196/196 -f 205/193/193 201/189/189 203/192/192 -f 199/196/196 206/194/194 204/195/195 -f 207/187/187 199/196/196 205/193/193 -f 139/180/180 164/89/89 177/144/144 -f 165/92/92 139/180/180 177/144/144 -f 140/175/175 211/197/197 164/89/89 -f 212/198/198 141/178/178 165/92/92 -f 144/165/165 211/197/197 142/170/170 -f 145/174/174 212/198/198 214/199/199 -f 187/164/164 213/200/200 144/165/165 -f 188/166/166 214/199/199 167/161/161 -f 209/201/201 166/158/158 81/83/83 -f 210/202/202 167/161/161 214/199/199 -f 215/203/203 213/200/200 209/201/201 -f 216/204/204 214/199/199 212/198/198 -f 79/87/87 211/197/197 215/203/203 -f 212/198/198 80/91/91 216/204/204 -f 130/169/169 222/205/205 131/157/157 -f 130/169/169 223/206/206 72/207/207 -f 133/155/155 222/205/205 220/208/208 -f 223/206/206 134/156/156 221/209/209 -f 135/153/153 220/208/208 218/210/210 -f 221/209/209 136/154/154 219/211/211 -f 137/212/212 218/210/210 217/213/213 -f 219/211/211 137/212/212 217/213/213 -f 218/210/210 231/214/214 217/213/213 -f 219/211/211 231/214/214 230/215/215 -f 218/210/210 227/216/216 229/217/217 -f 228/218/218 219/211/211 230/215/215 -f 220/208/208 225/219/219 227/216/216 -f 226/220/220 221/209/209 228/218/218 -f 72/207/207 225/219/219 222/205/205 -f 72/207/207 226/220/220 224/221/221 -f 224/221/221 229/217/217 225/219/219 -f 230/215/215 224/221/221 226/220/220 -f 225/219/219 229/217/217 227/216/216 -f 228/218/218 230/215/215 226/220/220 -f 183/149/149 234/222/222 232/223/223 -f 235/224/224 184/150/150 233/225/225 -f 112/146/146 232/223/223 254/226/226 -f 233/225/225 113/147/147 255/227/227 -f 112/146/146 256/228/228 110/127/127 -f 113/147/147 257/229/229 255/227/227 -f 114/142/142 234/222/222 181/151/151 -f 115/145/145 235/224/224 253/230/230 -f 114/142/142 250/231/231 252/232/232 -f 251/233/233 115/145/145 253/230/230 -f 116/140/140 248/234/234 250/231/231 -f 249/235/235 117/143/143 251/233/233 -f 118/138/138 246/236/236 248/234/234 -f 247/237/237 119/141/141 249/235/235 -f 120/136/136 244/238/238 246/236/236 -f 245/239/239 121/139/139 247/237/237 -f 124/132/132 244/238/238 122/134/134 -f 125/135/135 245/239/239 243/240/240 -f 126/130/130 242/241/241 124/132/132 -f 127/133/133 243/240/240 241/242/242 -f 126/130/130 236/243/243 240/244/244 -f 237/245/245 127/133/133 241/242/242 -f 179/129/129 238/246/246 236/243/243 -f 239/247/247 180/131/131 237/245/245 -f 128/126/126 256/228/228 238/246/246 -f 257/229/229 129/128/128 239/247/247 -f 256/228/228 276/248/248 238/246/246 -f 257/229/229 277/249/249 259/250/250 -f 236/243/243 276/248/248 278/251/251 -f 277/249/249 237/245/245 279/252/252 -f 236/243/243 274/253/253 240/244/244 -f 237/245/245 275/254/254 279/252/252 -f 240/244/244 272/255/255 242/241/241 -f 241/242/242 273/256/256 275/254/254 -f 244/238/238 272/255/255 270/257/257 -f 273/256/256 245/239/239 271/258/258 -f 244/238/238 268/259/259 246/236/236 -f 245/239/239 269/260/260 271/258/258 -f 248/234/234 268/259/259 266/261/261 -f 269/260/260 249/235/235 267/262/262 -f 248/234/234 264/263/263 250/231/231 -f 249/235/235 265/264/264 267/262/262 -f 250/231/231 262/265/265 252/232/232 -f 251/233/233 263/266/266 265/264/264 -f 234/222/222 262/265/265 280/267/267 -f 263/266/266 235/224/224 281/268/268 -f 256/228/228 260/269/269 258/270/270 -f 261/271/271 257/229/229 259/250/250 -f 254/226/226 282/272/272 260/269/269 -f 283/273/273 255/227/227 261/271/271 -f 232/223/223 280/267/267 282/272/272 -f 281/268/268 233/225/225 283/273/273 -f 67/123/123 284/274/274 73/275/275 -f 285/276/276 67/123/123 73/275/275 -f 108/119/119 286/277/277 284/274/274 -f 287/278/278 109/121/121 285/276/276 -f 104/116/116 286/277/277 106/115/115 -f 105/279/279 287/278/278 289/280/280 -f 102/108/108 288/281/281 104/116/116 -f 103/110/110 289/280/280 291/282/282 -f 100/104/104 290/283/283 102/108/108 -f 101/106/106 291/282/282 293/284/284 -f 100/104/104 294/285/285 292/286/286 -f 295/287/287 101/106/106 293/284/284 -f 96/101/101 294/285/285 98/100/100 -f 97/288/288 295/287/287 297/289/289 -f 96/101/101 298/290/290 296/291/291 -f 299/292/292 97/288/288 297/289/289 -f 94/93/93 300/293/293 298/290/290 -f 301/294/294 95/95/95 299/292/292 -f 309/295/295 338/296/296 308/297/297 -f 309/298/295 339/299/298 329/300/299 -f 308/297/297 336/301/300 307/302/301 -f 308/303/297 337/304/302 339/299/298 -f 307/302/301 340/305/303 306/306/304 -f 307/307/301 341/308/305 337/304/302 -f 89/69/69 306/306/304 340/305/303 -f 306/306/304 90/75/75 341/308/305 -f 87/73/73 340/305/303 334/309/306 -f 341/308/305 88/78/78 335/310/307 -f 85/76/76 334/309/306 330/311/308 -f 335/310/307 86/77/77 331/312/309 -f 83/79/79 330/311/308 332/313/310 -f 331/312/309 84/82/82 333/314/311 -f 330/311/308 338/296/296 332/313/310 -f 339/299/298 331/312/309 333/314/311 -f 334/309/306 336/301/300 330/311/308 -f 335/310/307 337/304/302 341/308/305 -f 332/313/310 328/315/312 326/316/313 -f 333/314/311 329/300/299 339/299/298 -f 81/83/83 332/313/310 326/316/313 -f 333/314/311 82/86/86 327/317/314 -f 342/318/315 215/203/203 209/201/201 -f 343/319/316 216/204/204 345/320/317 -f 326/316/313 209/201/201 81/83/83 -f 327/317/314 210/202/202 343/319/316 -f 215/203/203 346/321/318 79/87/87 -f 216/204/204 347/322/319 345/320/317 -f 346/321/318 92/94/94 79/87/87 -f 347/322/319 93/323/320 301/294/294 -f 324/324/321 304/325/322 77/326/323 -f 325/327/324 304/328/322 353/329/325 -f 352/330/326 78/331/327 304/325/322 -f 353/329/325 78/332/327 351/333/328 -f 78/331/327 348/334/329 305/335/330 -f 349/336/331 78/332/327 305/337/330 -f 305/335/330 328/315/312 309/295/295 -f 329/300/299 305/337/330 309/298/295 -f 328/315/312 342/318/315 326/316/313 -f 329/300/299 343/319/316 349/336/331 -f 296/291/291 318/338/332 310/339/333 -f 319/340/334 297/289/289 311/341/335 -f 316/342/336 77/326/323 76/343/337 -f 317/344/338 77/345/323 325/327/324 -f 358/346/339 303/347/340 302/348/341 -f 359/349/342 303/350/340 357/351/343 -f 303/347/340 354/352/344 75/353/345 -f 355/354/346 303/350/340 75/355/345 -f 75/353/345 316/342/336 76/343/337 -f 317/344/338 75/355/345 76/356/337 -f 292/357/286 362/358/347 364/359/348 -f 363/360/349 293/361/284 365/362/350 -f 364/359/348 368/363/351 366/364/352 -f 369/365/353 365/362/350 367/366/354 -f 366/364/352 370/367/355 372/368/356 -f 371/369/357 367/366/354 373/370/358 -f 372/368/356 376/371/359 374/372/360 -f 377/373/361 373/370/358 375/374/362 -f 378/375/363 376/371/359 314/376/364 -f 379/377/365 377/373/361 375/374/362 -f 316/342/336 374/372/360 378/375/363 -f 375/374/362 317/344/338 379/377/365 -f 354/352/344 372/368/356 374/372/360 -f 373/370/358 355/354/346 375/374/362 -f 356/378/366 366/364/352 372/368/356 -f 367/366/354 357/351/343 373/370/358 -f 358/346/339 364/359/348 366/364/352 -f 365/362/350 359/349/342 367/366/354 -f 292/357/286 360/379/367 290/380/283 -f 293/361/284 361/381/368 365/362/350 -f 360/379/367 302/348/341 74/382/369 -f 361/381/368 302/383/341 359/349/342 -f 284/384/274 288/385/281 290/380/283 -f 289/386/280 285/387/276 291/388/282 -f 284/384/274 360/379/367 74/382/369 -f 361/381/368 285/387/276 74/389/369 -f 73/390/275 284/384/274 74/382/369 -f 74/389/369 285/387/276 73/391/275 -f 296/291/291 362/358/347 294/285/285 -f 297/289/289 363/360/349 311/341/335 -f 310/339/333 368/363/351 362/358/347 -f 369/365/353 311/341/335 363/360/349 -f 312/392/370 370/367/355 368/363/351 -f 371/369/357 313/393/371 369/365/353 -f 376/371/359 382/394/372 314/376/364 -f 377/373/361 383/395/373 371/369/357 -f 350/396/374 384/397/375 348/334/329 -f 351/333/328 385/398/376 387/399/377 -f 384/397/375 320/400/378 318/338/332 -f 385/398/376 321/401/379 387/399/377 -f 298/290/290 384/397/375 318/338/332 -f 385/398/376 299/292/292 319/340/334 -f 300/293/293 342/318/315 384/397/375 -f 343/319/316 301/294/294 385/398/376 -f 342/318/315 348/334/329 384/397/375 -f 385/398/376 349/336/331 343/319/316 -f 300/293/293 346/321/318 344/402/380 -f 345/320/317 347/322/319 301/294/294 -f 322/403/381 378/375/363 314/376/364 -f 323/404/382 379/377/365 381/405/383 -f 378/375/363 324/324/321 316/342/336 -f 379/377/365 325/327/324 381/405/383 -f 386/406/384 322/403/381 320/400/378 -f 387/399/377 323/404/382 381/405/383 -f 352/330/326 386/406/384 350/396/374 -f 353/329/325 387/399/377 381/405/383 -f 324/324/321 380/407/385 352/330/326 -f 353/329/325 381/405/383 325/327/324 -f 388/408/386 402/409/387 400/410/388 -f 389/411/389 403/412/390 415/413/391 -f 400/410/388 404/414/392 398/415/393 -f 405/416/394 401/417/395 399/418/396 -f 404/414/392 396/419/397 398/415/393 -f 405/416/394 397/420/398 407/421/399 -f 406/422/400 394/423/401 396/419/397 -f 407/421/399 395/424/402 409/425/403 -f 408/426/404 392/427/405 394/423/401 -f 409/425/403 393/428/406 411/429/407 -f 392/427/405 412/430/408 390/431/409 -f 413/432/410 393/428/406 391/433/411 -f 410/434/412 418/435/413 412/430/408 -f 419/436/414 411/429/407 413/432/410 -f 408/426/404 420/437/415 410/434/412 -f 421/438/416 409/425/403 411/429/407 -f 424/439/417 408/426/404 406/422/400 -f 425/440/418 409/425/403 423/441/419 -f 426/442/420 406/422/400 404/414/392 -f 427/443/421 407/421/399 425/440/418 -f 428/444/422 404/414/392 402/409/387 -f 429/445/423 405/416/394 427/443/421 -f 402/409/387 416/446/424 428/444/422 -f 417/447/425 403/412/390 429/445/423 -f 320/400/378 442/448/426 318/338/332 -f 321/401/379 443/449/427 445/450/428 -f 390/431/409 444/451/429 320/452/378 -f 391/433/411 445/453/428 413/432/410 -f 310/339/333 442/448/426 312/392/370 -f 443/449/427 311/341/335 313/393/371 -f 382/454/372 414/455/430 388/408/386 -f 415/413/391 383/456/373 389/411/389 -f 412/430/408 440/457/431 444/451/429 -f 441/458/432 413/432/410 445/453/428 -f 446/459/433 440/457/431 438/460/434 -f 447/461/435 441/458/432 445/453/428 -f 434/462/436 438/460/434 436/463/437 -f 439/464/438 435/465/439 437/466/440 -f 448/467/441 434/462/436 432/468/442 -f 449/469/443 435/465/439 447/461/435 -f 448/467/441 450/470/444 430/471/445 -f 449/469/443 451/472/446 433/473/447 -f 430/471/445 416/446/424 414/455/430 -f 431/474/448 417/447/425 451/472/446 -f 312/392/370 430/475/445 382/394/372 -f 431/476/448 313/393/371 383/395/373 -f 442/448/426 448/477/441 312/392/370 -f 443/449/427 449/478/443 447/479/435 -f 442/448/426 444/480/429 446/481/433 -f 447/479/435 445/450/428 443/449/427 -f 416/446/424 452/482/449 476/483/450 -f 453/484/451 417/447/425 477/485/452 -f 432/468/442 452/482/449 450/470/444 -f 433/473/447 453/484/451 463/486/453 -f 432/468/442 460/487/454 462/488/455 -f 461/489/456 433/473/447 463/486/453 -f 436/463/437 460/487/454 434/462/436 -f 437/466/440 461/489/456 459/490/457 -f 438/460/434 458/491/458 436/463/437 -f 439/464/438 459/490/457 457/492/459 -f 438/460/434 454/493/460 456/494/461 -f 455/495/462 439/464/438 457/492/459 -f 440/457/431 474/496/463 454/493/460 -f 475/497/464 441/458/432 455/495/462 -f 428/444/422 476/483/450 464/498/465 -f 477/485/452 429/445/423 465/499/466 -f 426/442/420 464/498/465 466/500/467 -f 465/499/466 427/443/421 467/501/468 -f 424/439/417 466/500/467 468/502/469 -f 467/501/468 425/440/418 469/503/470 -f 424/439/417 470/504/471 422/505/472 -f 425/440/418 471/506/473 469/503/470 -f 422/505/472 472/507/474 420/437/415 -f 423/441/419 473/508/475 471/506/473 -f 420/437/415 474/496/463 418/435/413 -f 421/438/416 475/497/464 473/508/475 -f 456/494/461 478/509/476 458/491/458 -f 457/492/459 479/510/477 481/511/478 -f 480/512/479 484/513/480 478/509/476 -f 481/511/478 485/514/481 483/515/482 -f 484/513/480 488/516/483 486/517/484 -f 489/518/485 485/514/481 487/519/486 -f 488/516/483 492/520/487 486/517/484 -f 489/518/485 493/521/488 491/522/489 -f 464/498/465 486/517/484 492/520/487 -f 487/519/486 465/499/466 493/521/488 -f 484/513/480 476/483/450 452/482/449 -f 485/514/481 477/485/452 487/519/486 -f 462/488/455 484/513/480 452/482/449 -f 463/486/453 485/514/481 479/510/477 -f 458/491/458 462/488/455 460/487/454 -f 463/486/453 459/490/457 461/489/456 -f 474/496/463 456/494/461 454/493/460 -f 475/497/464 457/492/459 481/511/478 -f 472/507/474 480/512/479 474/496/463 -f 481/511/478 473/508/475 475/497/464 -f 488/516/483 472/507/474 470/504/471 -f 489/518/485 473/508/475 483/515/482 -f 490/523/490 470/504/471 468/502/469 -f 491/522/489 471/506/473 489/518/485 -f 466/500/467 490/523/490 468/502/469 -f 491/522/489 467/501/468 469/503/470 -f 464/498/465 492/520/487 466/500/467 -f 467/501/468 493/521/488 465/499/466 -f 392/427/405 504/524/491 502/525/492 -f 505/526/493 393/428/406 503/527/494 -f 394/423/401 502/525/492 500/528/495 -f 503/527/494 395/424/402 501/529/496 -f 394/423/401 498/530/497 396/419/397 -f 395/424/402 499/531/498 501/529/496 -f 396/419/397 496/532/499 398/533/393 -f 397/420/398 497/534/500 499/531/498 -f 398/533/393 494/535/501 400/536/388 -f 399/537/396 495/538/502 497/534/500 -f 400/536/388 506/539/503 388/540/386 -f 401/541/395 507/542/504 495/538/502 -f 502/525/492 506/539/503 494/535/501 -f 503/527/494 507/542/504 505/526/493 -f 494/535/501 500/528/495 502/525/492 -f 501/529/496 495/538/502 503/527/494 -f 496/532/499 498/530/497 500/528/495 -f 501/529/496 499/531/498 497/534/500 -f 382/543/372 506/539/503 314/544/364 -f 383/545/373 507/542/504 389/546/389 -f 314/544/364 504/524/491 322/547/381 -f 505/526/493 315/548/505 323/549/382 -f 320/452/378 504/524/491 390/431/409 -f 505/526/493 321/550/379 391/433/411 -f 47/1/1 1/13/13 3/2/2 -f 4/4/4 2/16/16 48/5/5 -f 45/3/3 3/2/2 5/7/7 -f 6/9/9 4/4/4 46/6/6 -f 3/2/2 9/14/14 7/11/11 -f 8/12/12 10/15/15 4/4/4 -f 1/13/13 11/17/17 9/14/14 -f 10/15/15 12/20/20 2/16/16 -f 11/17/17 13/26/26 15/18/18 -f 16/19/19 14/27/27 12/20/20 -f 9/14/14 15/18/18 17/21/21 -f 18/22/22 16/19/19 10/15/15 -f 21/23/23 19/32/32 17/21/21 -f 22/24/24 16/19/19 18/22/22 -f 13/26/26 23/28/28 21/23/23 -f 22/24/24 24/31/31 14/27/27 -f 23/28/28 25/39/39 27/29/29 -f 28/30/30 26/54/54 24/31/31 -f 27/29/29 29/35/35 19/32/32 -f 28/30/30 22/24/24 20/25/25 -f 33/34/34 31/45/45 29/35/35 -f 34/36/36 28/30/30 30/33/33 -f 35/38/38 33/34/34 27/29/29 -f 36/40/40 26/54/54 28/30/30 -f 37/41/41 39/44/44 33/34/34 -f 38/42/42 36/40/40 34/36/36 -f 39/44/44 41/47/47 31/45/45 -f 40/43/43 34/36/36 32/37/37 -f 45/3/3 43/8/8 41/47/47 -f 46/6/6 40/43/43 42/46/46 -f 47/1/1 45/3/3 39/44/44 -f 48/5/5 38/42/42 40/43/43 -f 37/41/41 51/51/51 49/48/48 -f 38/42/42 48/5/5 50/49/49 -f 35/38/38 53/53/53 51/51/51 -f 36/40/40 38/42/42 52/50/50 -f 25/39/39 55/56/56 53/53/53 -f 26/54/54 36/40/40 54/52/52 -f 23/28/28 57/59/59 55/56/56 -f 24/31/31 26/54/54 56/55/55 -f 23/28/28 13/26/26 59/58/58 -f 60/60/60 14/27/27 24/31/31 -f 13/26/26 11/17/17 63/61/61 -f 64/62/62 12/20/20 14/27/27 -f 11/17/17 1/13/13 65/63/63 -f 66/64/64 2/16/16 12/20/20 -f 1/13/13 47/1/1 49/48/48 -f 50/49/49 48/5/5 2/16/16 -f 174/67/67 176/71/71 91/68/68 -f 175/70/70 90/75/75 91/68/68 -f 172/72/72 174/67/67 89/69/69 -f 173/74/74 88/78/78 90/75/75 -f 85/76/76 170/80/80 172/72/72 -f 173/74/74 171/81/81 86/77/77 -f 83/79/79 168/84/84 170/80/80 -f 171/81/81 169/85/85 84/82/82 -f 81/83/83 166/158/158 168/84/84 -f 169/85/85 167/161/161 82/86/86 -f 79/87/87 92/94/94 146/88/88 -f 147/90/90 93/323/320 80/91/91 -f 94/93/93 148/98/98 146/88/88 -f 95/95/95 93/323/320 147/90/90 -f 94/93/93 96/101/101 150/97/97 -f 151/99/99 97/288/288 95/95/95 -f 98/100/100 152/105/105 150/97/97 -f 99/102/102 97/288/288 151/99/99 -f 100/104/104 154/109/109 152/105/105 -f 101/106/106 99/102/102 153/103/103 -f 102/108/108 156/113/113 154/109/109 -f 103/110/110 101/106/106 155/107/107 -f 102/108/108 104/116/116 158/112/112 -f 159/114/114 105/279/279 103/110/110 -f 106/115/115 160/120/120 158/112/112 -f 107/117/117 105/279/279 159/114/114 -f 108/119/119 162/124/124 160/120/120 -f 109/121/121 107/117/117 161/118/118 -f 67/123/123 68/125/125 162/124/124 -f 67/123/123 109/121/121 163/122/122 -f 128/126/126 160/120/120 162/124/124 -f 129/128/128 111/551/506 163/122/122 -f 128/126/126 179/129/129 158/112/112 -f 159/114/114 180/131/131 129/128/128 -f 156/113/113 158/112/112 179/129/129 -f 157/111/111 127/133/133 180/131/131 -f 154/109/109 156/113/113 126/130/130 -f 155/107/107 125/135/135 127/133/133 -f 152/105/105 154/109/109 124/132/132 -f 153/103/103 123/137/137 125/135/135 -f 150/97/97 152/105/105 122/134/134 -f 151/99/99 121/139/139 123/137/137 -f 148/98/98 150/97/97 120/136/136 -f 149/96/96 119/141/141 121/139/139 -f 146/88/88 148/98/98 118/138/138 -f 147/90/90 117/143/143 119/141/141 -f 164/89/89 146/88/88 116/140/140 -f 165/92/92 115/145/145 117/143/143 -f 114/142/142 181/151/151 177/144/144 -f 177/144/144 182/152/152 115/145/145 -f 162/124/124 68/125/125 112/146/146 -f 163/122/122 111/551/506 113/147/147 -f 112/146/146 68/125/125 178/148/148 -f 178/148/148 68/125/125 113/147/147 -f 181/151/151 183/149/149 178/148/148 -f 182/152/152 177/144/144 178/148/148 -f 135/153/153 137/212/212 176/71/71 -f 176/71/71 137/212/212 136/154/154 -f 133/155/155 135/153/153 174/67/67 -f 175/70/70 136/154/154 134/156/156 -f 133/155/155 172/72/72 170/80/80 -f 134/156/156 132/162/162 171/81/81 -f 166/158/158 187/164/164 185/159/159 -f 186/160/160 188/166/166 167/161/161 -f 131/157/157 170/80/80 168/84/84 -f 169/85/85 171/81/81 132/162/162 -f 190/163/163 189/167/167 187/164/164 -f 190/163/163 145/174/174 188/166/166 -f 187/164/164 189/167/167 69/168/168 -f 188/166/166 186/160/160 69/168/168 -f 131/157/157 185/159/159 69/168/168 -f 132/162/162 130/169/169 69/168/168 -f 142/170/170 193/176/176 191/171/171 -f 192/172/172 194/177/177 143/173/173 -f 140/175/175 195/191/191 193/176/176 -f 194/177/177 196/182/182 141/178/178 -f 197/179/179 195/191/191 140/175/175 -f 198/181/181 139/180/180 141/178/178 -f 71/183/183 197/179/179 139/180/180 -f 144/165/165 191/171/171 70/185/185 -f 145/174/174 190/163/163 70/185/185 -f 191/171/171 206/194/194 208/186/186 -f 192/172/172 70/185/185 208/186/186 -f 71/183/183 199/196/196 200/188/188 -f 201/189/189 199/196/196 71/183/183 -f 197/179/179 200/188/188 202/190/190 -f 203/192/192 201/189/189 198/181/181 -f 202/190/190 204/195/195 193/176/176 -f 203/192/192 196/182/182 194/177/177 -f 193/176/176 204/195/195 206/194/194 -f 207/187/187 205/193/193 194/177/177 -f 204/195/195 202/190/190 200/188/188 -f 205/193/193 199/196/196 201/189/189 -f 199/196/196 208/186/186 206/194/194 -f 207/187/187 208/186/186 199/196/196 -f 139/180/180 140/175/175 164/89/89 -f 165/92/92 141/178/178 139/180/180 -f 140/175/175 142/170/170 211/197/197 -f 212/198/198 143/173/173 141/178/178 -f 144/165/165 213/200/200 211/197/197 -f 145/174/174 143/173/173 212/198/198 -f 187/164/164 166/158/158 213/200/200 -f 188/166/166 145/174/174 214/199/199 -f 209/201/201 213/200/200 166/158/158 -f 210/202/202 82/86/86 167/161/161 -f 215/203/203 211/197/197 213/200/200 -f 216/204/204 210/202/202 214/199/199 -f 79/87/87 164/89/89 211/197/197 -f 212/198/198 165/92/92 80/91/91 -f 130/169/169 72/207/207 222/205/205 -f 130/169/169 132/162/162 223/206/206 -f 133/155/155 131/157/157 222/205/205 -f 223/206/206 132/162/162 134/156/156 -f 135/153/153 133/155/155 220/208/208 -f 221/209/209 134/156/156 136/154/154 -f 137/212/212 135/153/153 218/210/210 -f 219/211/211 136/154/154 137/212/212 -f 218/210/210 229/217/217 231/214/214 -f 219/211/211 217/213/213 231/214/214 -f 218/210/210 220/208/208 227/216/216 -f 228/218/218 221/209/209 219/211/211 -f 220/208/208 222/205/205 225/219/219 -f 226/220/220 223/206/206 221/209/209 -f 72/207/207 224/221/221 225/219/219 -f 72/207/207 223/206/206 226/220/220 -f 224/221/221 231/214/214 229/217/217 -f 230/215/215 231/214/214 224/221/221 -f 183/149/149 181/151/151 234/222/222 -f 235/224/224 182/152/152 184/150/150 -f 112/146/146 183/149/149 232/223/223 -f 233/225/225 184/150/150 113/147/147 -f 112/146/146 254/226/226 256/228/228 -f 113/147/147 111/551/506 257/229/229 -f 114/142/142 252/232/232 234/222/222 -f 115/145/145 182/152/152 235/224/224 -f 114/142/142 116/140/140 250/231/231 -f 251/233/233 117/143/143 115/145/145 -f 116/140/140 118/138/138 248/234/234 -f 249/235/235 119/141/141 117/143/143 -f 118/138/138 120/136/136 246/236/236 -f 247/237/237 121/139/139 119/141/141 -f 120/136/136 122/134/134 244/238/238 -f 245/239/239 123/137/137 121/139/139 -f 124/132/132 242/241/241 244/238/238 -f 125/135/135 123/137/137 245/239/239 -f 126/130/130 240/244/244 242/241/241 -f 127/133/133 125/135/135 243/240/240 -f 126/130/130 179/129/129 236/243/243 -f 237/245/245 180/131/131 127/133/133 -f 179/129/129 128/126/126 238/246/246 -f 239/247/247 129/128/128 180/131/131 -f 128/126/126 110/127/127 256/228/228 -f 257/229/229 111/551/506 129/128/128 -f 256/228/228 258/270/270 276/248/248 -f 257/229/229 239/247/247 277/249/249 -f 236/243/243 238/246/246 276/248/248 -f 277/249/249 239/247/247 237/245/245 -f 236/243/243 278/251/251 274/253/253 -f 237/245/245 241/242/242 275/254/254 -f 240/244/244 274/253/253 272/255/255 -f 241/242/242 243/240/240 273/256/256 -f 244/238/238 242/241/241 272/255/255 -f 273/256/256 243/240/240 245/239/239 -f 244/238/238 270/257/257 268/259/259 -f 245/239/239 247/237/237 269/260/260 -f 248/234/234 246/236/236 268/259/259 -f 269/260/260 247/237/237 249/235/235 -f 248/234/234 266/261/261 264/263/263 -f 249/235/235 251/233/233 265/264/264 -f 250/231/231 264/263/263 262/265/265 -f 251/233/233 253/230/230 263/266/266 -f 234/222/222 252/232/232 262/265/265 -f 263/266/266 253/230/230 235/224/224 -f 256/228/228 254/226/226 260/269/269 -f 261/271/271 255/227/227 257/229/229 -f 254/226/226 232/223/223 282/272/272 -f 283/273/273 233/225/225 255/227/227 -f 232/223/223 234/222/222 280/267/267 -f 281/268/268 235/224/224 233/225/225 -f 67/123/123 108/119/119 284/274/274 -f 285/276/276 109/121/121 67/123/123 -f 108/119/119 106/115/115 286/277/277 -f 287/278/278 107/117/117 109/121/121 -f 104/116/116 288/281/281 286/277/277 -f 105/279/279 107/117/117 287/278/278 -f 102/108/108 290/283/283 288/281/281 -f 103/110/110 105/279/279 289/280/280 -f 100/104/104 292/286/286 290/283/283 -f 101/106/106 103/110/110 291/282/282 -f 100/104/104 98/100/100 294/285/285 -f 295/287/287 99/102/102 101/106/106 -f 96/101/101 296/291/291 294/285/285 -f 97/288/288 99/102/102 295/287/287 -f 96/101/101 94/93/93 298/290/290 -f 299/292/292 95/95/95 97/288/288 -f 94/93/93 92/94/94 300/293/293 -f 301/294/294 93/323/320 95/95/95 -f 309/295/295 328/315/312 338/296/296 -f 309/298/295 308/303/297 339/299/298 -f 308/297/297 338/296/296 336/301/300 -f 308/303/297 307/307/301 337/304/302 -f 307/302/301 336/301/300 340/305/303 -f 307/307/301 306/306/304 341/308/305 -f 89/69/69 91/68/68 306/306/304 -f 306/306/304 91/68/68 90/75/75 -f 87/73/73 89/69/69 340/305/303 -f 341/308/305 90/75/75 88/78/78 -f 85/76/76 87/73/73 334/309/306 -f 335/310/307 88/78/78 86/77/77 -f 83/79/79 85/76/76 330/311/308 -f 331/312/309 86/77/77 84/82/82 -f 330/311/308 336/301/300 338/296/296 -f 339/299/298 337/304/302 331/312/309 -f 334/309/306 340/305/303 336/301/300 -f 335/310/307 331/312/309 337/304/302 -f 332/313/310 338/296/296 328/315/312 -f 333/314/311 327/317/314 329/300/299 -f 81/83/83 83/79/79 332/313/310 -f 333/314/311 84/82/82 82/86/86 -f 342/318/315 344/402/380 215/203/203 -f 343/319/316 210/202/202 216/204/204 -f 326/316/313 342/318/315 209/201/201 -f 327/317/314 82/86/86 210/202/202 -f 215/203/203 344/402/380 346/321/318 -f 216/204/204 80/91/91 347/322/319 -f 346/321/318 300/293/293 92/94/94 -f 347/322/319 80/91/91 93/323/320 -f 324/324/321 352/330/326 304/325/322 -f 325/327/324 77/345/323 304/328/322 -f 352/330/326 350/396/374 78/331/327 -f 353/329/325 304/328/322 78/332/327 -f 78/331/327 350/396/374 348/334/329 -f 349/336/331 351/333/328 78/332/327 -f 305/335/330 348/334/329 328/315/312 -f 329/300/299 349/336/331 305/337/330 -f 328/315/312 348/334/329 342/318/315 -f 329/300/299 327/317/314 343/319/316 -f 296/291/291 298/290/290 318/338/332 -f 319/340/334 299/292/292 297/289/289 -f 316/342/336 324/324/321 77/326/323 -f 317/344/338 76/356/337 77/345/323 -f 358/346/339 356/378/366 303/347/340 -f 359/349/342 302/383/341 303/350/340 -f 303/347/340 356/378/366 354/352/344 -f 355/354/346 357/351/343 303/350/340 -f 75/353/345 354/352/344 316/342/336 -f 317/344/338 355/354/346 75/355/345 -f 292/357/286 294/285/285 362/358/347 -f 363/360/349 295/287/287 293/361/284 -f 364/359/348 362/358/347 368/363/351 -f 369/365/353 363/360/349 365/362/350 -f 366/364/352 368/363/351 370/367/355 -f 371/369/357 369/365/353 367/366/354 -f 372/368/356 370/367/355 376/371/359 -f 377/373/361 371/369/357 373/370/358 -f 378/375/363 374/372/360 376/371/359 -f 379/377/365 315/552/505 377/373/361 -f 316/342/336 354/352/344 374/372/360 -f 375/374/362 355/354/346 317/344/338 -f 354/352/344 356/378/366 372/368/356 -f 373/370/358 357/351/343 355/354/346 -f 356/378/366 358/346/339 366/364/352 -f 367/366/354 359/349/342 357/351/343 -f 358/346/339 360/379/367 364/359/348 -f 365/362/350 361/381/368 359/349/342 -f 292/357/286 364/359/348 360/379/367 -f 293/361/284 291/388/282 361/381/368 -f 360/379/367 358/346/339 302/348/341 -f 361/381/368 74/389/369 302/383/341 -f 284/384/274 286/553/277 288/385/281 -f 289/386/280 287/554/278 285/387/276 -f 284/384/274 290/380/283 360/379/367 -f 361/381/368 291/388/282 285/387/276 -f 296/291/291 310/339/333 362/358/347 -f 297/289/289 295/287/287 363/360/349 -f 310/339/333 312/392/370 368/363/351 -f 369/365/353 313/393/371 311/341/335 -f 312/392/370 382/394/372 370/367/355 -f 371/369/357 383/395/373 313/393/371 -f 376/371/359 370/367/355 382/394/372 -f 377/373/361 315/552/505 383/395/373 -f 350/396/374 386/406/384 384/397/375 -f 351/333/328 349/336/331 385/398/376 -f 384/397/375 386/406/384 320/400/378 -f 385/398/376 319/340/334 321/401/379 -f 298/290/290 300/293/293 384/397/375 -f 385/398/376 301/294/294 299/292/292 -f 300/293/293 344/402/380 342/318/315 -f 343/319/316 345/320/317 301/294/294 -f 322/403/381 380/407/385 378/375/363 -f 323/404/382 315/552/505 379/377/365 -f 378/375/363 380/407/385 324/324/321 -f 379/377/365 317/344/338 325/327/324 -f 386/406/384 380/407/385 322/403/381 -f 387/399/377 321/401/379 323/404/382 -f 352/330/326 380/407/385 386/406/384 -f 353/329/325 351/333/328 387/399/377 -f 388/408/386 414/455/430 402/409/387 -f 389/411/389 401/417/395 403/412/390 -f 400/410/388 402/409/387 404/414/392 -f 405/416/394 403/412/390 401/417/395 -f 404/414/392 406/422/400 396/419/397 -f 405/416/394 399/418/396 397/420/398 -f 406/422/400 408/426/404 394/423/401 -f 407/421/399 397/420/398 395/424/402 -f 408/426/404 410/434/412 392/427/405 -f 409/425/403 395/424/402 393/428/406 -f 392/427/405 410/434/412 412/430/408 -f 413/432/410 411/429/407 393/428/406 -f 410/434/412 420/437/415 418/435/413 -f 419/436/414 421/438/416 411/429/407 -f 408/426/404 422/505/472 420/437/415 -f 421/438/416 423/441/419 409/425/403 -f 424/439/417 422/505/472 408/426/404 -f 425/440/418 407/421/399 409/425/403 -f 426/442/420 424/439/417 406/422/400 -f 427/443/421 405/416/394 407/421/399 -f 428/444/422 426/442/420 404/414/392 -f 429/445/423 403/412/390 405/416/394 -f 402/409/387 414/455/430 416/446/424 -f 417/447/425 415/413/391 403/412/390 -f 320/400/378 444/480/429 442/448/426 -f 321/401/379 319/340/334 443/449/427 -f 390/431/409 412/430/408 444/451/429 -f 391/433/411 321/550/379 445/453/428 -f 310/339/333 318/338/332 442/448/426 -f 443/449/427 319/340/334 311/341/335 -f 382/454/372 430/471/445 414/455/430 -f 415/413/391 431/474/448 383/456/373 -f 412/430/408 418/435/413 440/457/431 -f 441/458/432 419/436/414 413/432/410 -f 446/459/433 444/451/429 440/457/431 -f 447/461/435 439/464/438 441/458/432 -f 434/462/436 446/459/433 438/460/434 -f 439/464/438 447/461/435 435/465/439 -f 448/467/441 446/459/433 434/462/436 -f 449/469/443 433/473/447 435/465/439 -f 448/467/441 432/468/442 450/470/444 -f 449/469/443 431/474/448 451/472/446 -f 430/471/445 450/470/444 416/446/424 -f 431/474/448 415/413/391 417/447/425 -f 312/392/370 448/477/441 430/475/445 -f 431/476/448 449/478/443 313/393/371 -f 442/448/426 446/481/433 448/477/441 -f 443/449/427 313/393/371 449/478/443 -f 416/446/424 450/470/444 452/482/449 -f 453/484/451 451/472/446 417/447/425 -f 432/468/442 462/488/455 452/482/449 -f 433/473/447 451/472/446 453/484/451 -f 432/468/442 434/462/436 460/487/454 -f 461/489/456 435/465/439 433/473/447 -f 436/463/437 458/491/458 460/487/454 -f 437/466/440 435/465/439 461/489/456 -f 438/460/434 456/494/461 458/491/458 -f 439/464/438 437/466/440 459/490/457 -f 438/460/434 440/457/431 454/493/460 -f 455/495/462 441/458/432 439/464/438 -f 440/457/431 418/435/413 474/496/463 -f 475/497/464 419/436/414 441/458/432 -f 428/444/422 416/446/424 476/483/450 -f 477/485/452 417/447/425 429/445/423 -f 426/442/420 428/444/422 464/498/465 -f 465/499/466 429/445/423 427/443/421 -f 424/439/417 426/442/420 466/500/467 -f 467/501/468 427/443/421 425/440/418 -f 424/439/417 468/502/469 470/504/471 -f 425/440/418 423/441/419 471/506/473 -f 422/505/472 470/504/471 472/507/474 -f 423/441/419 421/438/416 473/508/475 -f 420/437/415 472/507/474 474/496/463 -f 421/438/416 419/436/414 475/497/464 -f 456/494/461 480/512/479 478/509/476 -f 457/492/459 459/490/457 479/510/477 -f 480/512/479 482/555/507 484/513/480 -f 481/511/478 479/510/477 485/514/481 -f 484/513/480 482/555/507 488/516/483 -f 489/518/485 483/515/482 485/514/481 -f 488/516/483 490/523/490 492/520/487 -f 489/518/485 487/519/486 493/521/488 -f 464/498/465 476/483/450 486/517/484 -f 487/519/486 477/485/452 465/499/466 -f 484/513/480 486/517/484 476/483/450 -f 485/514/481 453/484/451 477/485/452 -f 462/488/455 478/509/476 484/513/480 -f 463/486/453 453/484/451 485/514/481 -f 458/491/458 478/509/476 462/488/455 -f 463/486/453 479/510/477 459/490/457 -f 474/496/463 480/512/479 456/494/461 -f 475/497/464 455/495/462 457/492/459 -f 472/507/474 482/555/507 480/512/479 -f 481/511/478 483/515/482 473/508/475 -f 488/516/483 482/555/507 472/507/474 -f 489/518/485 471/506/473 473/508/475 -f 490/523/490 488/516/483 470/504/471 -f 491/522/489 469/503/470 471/506/473 -f 466/500/467 492/520/487 490/523/490 -f 491/522/489 493/521/488 467/501/468 -f 392/427/405 390/431/409 504/524/491 -f 505/526/493 391/433/411 393/428/406 -f 394/423/401 392/427/405 502/525/492 -f 503/527/494 393/428/406 395/424/402 -f 394/423/401 500/528/495 498/530/497 -f 395/424/402 397/420/398 499/531/498 -f 396/419/397 498/530/497 496/532/499 -f 397/420/398 399/537/396 497/534/500 -f 398/533/393 496/532/499 494/535/501 -f 399/537/396 401/541/395 495/538/502 -f 400/536/388 494/535/501 506/539/503 -f 401/541/395 389/546/389 507/542/504 -f 502/525/492 504/524/491 506/539/503 -f 503/527/494 495/538/502 507/542/504 -f 494/535/501 496/532/499 500/528/495 -f 501/529/496 497/534/500 495/538/502 -f 382/543/372 388/540/386 506/539/503 -f 383/545/373 315/548/505 507/542/504 -f 314/544/364 506/539/503 504/524/491 -f 505/526/493 507/542/504 315/548/505 -f 320/452/378 322/547/381 504/524/491 -f 505/526/493 323/549/382 321/550/379 -- cgit v1.2.3-54-g00ecf