#ifndef CGLTF_H_INCLUDED__ #define CGLTF_H_INCLUDED__ #ifdef __cplusplus extern "C" { #endif typedef unsigned long cgltf_size; typedef float cgltf_float; typedef int cgltf_bool; typedef enum cgltf_file_type { cgltf_file_type_invalid, cgltf_file_type_gltf, cgltf_file_type_glb, } cgltf_file_type; typedef struct cgltf_options { cgltf_file_type type; cgltf_size json_token_count; /* 0 == auto */ void* (*memory_alloc)(void* user, cgltf_size size); void (*memory_free) (void* user, void* ptr); void* memory_user_data; } cgltf_options; typedef enum cgltf_result { cgltf_result_success, cgltf_result_data_too_short, cgltf_result_unknown_format, cgltf_result_invalid_json, cgltf_result_invalid_options, } cgltf_result; 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; 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_0, cgltf_attribute_type_texcoord_1, cgltf_attribute_type_color_0, cgltf_attribute_type_joints_0, cgltf_attribute_type_weights_0, } cgltf_attribute_type; typedef enum cgltf_component_type { cgltf_component_type_invalid, cgltf_component_type_rgb_32f, cgltf_component_type_rgba_32f, cgltf_component_type_rg_32f, cgltf_component_type_rg_8, cgltf_component_type_rg_16, cgltf_component_type_rgba_8, cgltf_component_type_rgba_16, cgltf_component_type_r_8, cgltf_component_type_r_8u, cgltf_component_type_r_16, cgltf_component_type_r_16u, cgltf_component_type_r_32u, cgltf_component_type_r_32f, } 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; typedef enum cgltf_primitive_type { cgltf_type_points, cgltf_type_lines, cgltf_type_line_loop, cgltf_type_line_strip, cgltf_type_triangles, cgltf_type_triangle_strip, cgltf_type_triangle_fan, } cgltf_primitive_type; typedef struct cgltf_buffer { cgltf_size size; char* uri; } cgltf_buffer; typedef struct cgltf_buffer_view { cgltf_buffer* buffer; cgltf_size offset; cgltf_size size; cgltf_size stride; /* 0 == automatically determined by accessor */ cgltf_buffer_view_type type; } cgltf_buffer_view; typedef struct cgltf_accessor { cgltf_component_type component_type; cgltf_type type; cgltf_size offset; cgltf_size count; cgltf_size stride; cgltf_buffer_view* buffer_view; } cgltf_accessor; typedef struct cgltf_attribute { cgltf_attribute_type name; cgltf_accessor* data; } cgltf_attribute; typedef struct cgltf_rgba { cgltf_float r; cgltf_float g; cgltf_float b; cgltf_float a; } cgltf_rgba; typedef struct cgltf_image { char* uri; cgltf_buffer_view* buffer_view; char* mime_type; } cgltf_image; typedef struct cgltf_sampler { cgltf_float mag_filter; cgltf_float min_filter; cgltf_float wrap_s; cgltf_float wrap_t; } cgltf_sampler; typedef struct cgltf_texture { cgltf_image* image; cgltf_sampler* sampler; } cgltf_texture; typedef struct cgltf_texture_view { cgltf_texture* texture; cgltf_size texcoord; cgltf_float scale; } cgltf_texture_view; typedef struct cgltf_pbr { cgltf_texture_view base_color_texture; cgltf_texture_view metallic_roughness_texture; cgltf_rgba base_color; cgltf_float metallic_factor; cgltf_float roughness_factor; } cgltf_pbr; typedef struct cgltf_material { char* name; cgltf_pbr pbr; cgltf_rgba emissive_color; cgltf_texture_view normal_texture; cgltf_texture_view emissive_texture; cgltf_texture_view occlusion_texture; cgltf_bool double_sided; } cgltf_material; typedef struct cgltf_primitive { cgltf_primitive_type type; cgltf_accessor* indices; cgltf_material* material; cgltf_attribute* attributes; cgltf_size attributes_count; } cgltf_primitive; typedef struct cgltf_mesh { char* name; cgltf_primitive* primitives; cgltf_size primitives_count; } cgltf_mesh; typedef struct cgltf_data { unsigned version; cgltf_file_type file_type; 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; const void* bin; cgltf_size bin_size; void (*memory_free) (void* user, void* ptr); void* memory_user_data; } cgltf_data; cgltf_result cgltf_parse( const cgltf_options* options, const void* data, cgltf_size size, cgltf_data* out_data); void cgltf_free(cgltf_data* data); #endif /* #ifndef CGLTF_H_INCLUDED__ */ /* * * Stop now, if you are only interested in the API. * Below, you find the implementation. * */ #ifdef __INTELLISENSE__ /* This makes MSVC intellisense work. */ #define CGLTF_IMPLEMENTATION #endif #ifdef CGLTF_IMPLEMENTATION #include /* For uint8_t, uint32_t */ #include /* For strncpy */ #include /* For malloc, free */ /* * -- 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; int start; int end; int size; #ifdef JSMN_PARENT_LINKS int parent; #endif } jsmntok_t; typedef struct { unsigned int 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; void jsmn_init(jsmn_parser *parser); int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens); /* * -- jsmn.h end -- */ static const cgltf_size GltfHeaderSize = 12; static const cgltf_size GltfChunkHeaderSize = 8; static const uint32_t GltfMagic = 0x46546C67; static const uint32_t GltfMagicJsonChunk = 0x4E4F534A; static const uint32_t GltfMagicBinChunk = 0x004E4942; static void* cgltf_mem_alloc(void* user, cgltf_size size) { return malloc(size); } static void cgltf_mem_free(void* user, void* ptr) { free(ptr); } 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 < GltfHeaderSize) { return cgltf_result_data_too_short; } if (options == NULL) { return cgltf_result_invalid_options; } cgltf_options fixed_options = *options; if (fixed_options.memory_alloc == NULL) { fixed_options.memory_alloc = &cgltf_mem_alloc; } if (fixed_options.memory_free == NULL) { fixed_options.memory_free = &cgltf_mem_free; } uint32_t tmp; // Magic memcpy(&tmp, data, 4); if (tmp != GltfMagic) { if (fixed_options.type == cgltf_file_type_invalid) { fixed_options.type = cgltf_file_type_gltf; } else { return cgltf_result_unknown_format; } } memset(out_data, 0, sizeof(cgltf_data)); out_data->memory_free = fixed_options.memory_free; out_data->memory_user_data = fixed_options.memory_user_data; if (fixed_options.type == cgltf_file_type_gltf) { out_data->file_type = cgltf_file_type_gltf; return cgltf_parse_json(&fixed_options, data, size, out_data); } const uint8_t* ptr = (const uint8_t*)data; // Version memcpy(&tmp, ptr + 4, 4); out_data->version = tmp; // Total length memcpy(&tmp, ptr + 8, 4); if (tmp > size) { return cgltf_result_data_too_short; } const uint8_t* json_chunk = ptr + GltfHeaderSize; // JSON chunk: length uint32_t json_length; memcpy(&json_length, json_chunk, 4); if (GltfHeaderSize + GltfChunkHeaderSize + json_length > size) { return cgltf_result_data_too_short; } // JSON chunk: magic memcpy(&tmp, json_chunk + 4, 4); if (tmp != GltfMagicJsonChunk) { return cgltf_result_unknown_format; } json_chunk += GltfChunkHeaderSize; 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_invalid; if (GltfHeaderSize + GltfChunkHeaderSize + json_length + GltfChunkHeaderSize <= size) { // 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 (GltfHeaderSize + GltfChunkHeaderSize + json_length + GltfChunkHeaderSize + bin_length > size) { return cgltf_result_data_too_short; } // Bin chunk: magic memcpy(&tmp, bin_chunk + 4, 4); if (tmp != GltfMagicBinChunk) { return cgltf_result_unknown_format; } bin_chunk += GltfChunkHeaderSize; out_data->file_type = cgltf_file_type_glb; out_data->bin = bin_chunk; out_data->bin_size = bin_length; } return cgltf_result_success; } void cgltf_free(cgltf_data* data) { data->memory_free(data->memory_user_data, data->accessors); data->memory_free(data->memory_user_data, data->buffer_views); for (cgltf_size i = 0; i < data->buffers_count; ++i) { data->memory_free(data->memory_user_data, data->buffers[i].uri); } data->memory_free(data->memory_user_data, data->buffers); for (cgltf_size i = 0; i < data->meshes_count; ++i) { data->memory_free(data->memory_user_data, data->meshes[i].name); for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) { data->memory_free(data->memory_user_data, data->meshes[i].primitives[j].attributes); } data->memory_free(data->memory_user_data, data->meshes[i].primitives); } data->memory_free(data->memory_user_data, data->meshes); for (cgltf_size i = 0; i < data->materials_count; ++i) { data->memory_free(data->memory_user_data, data->materials[i].name); } data->memory_free(data->memory_user_data, data->materials); for (cgltf_size i = 0; i < data->images_count; ++i) { data->memory_free(data->memory_user_data, data->images[i].uri); data->memory_free(data->memory_user_data, data->images[i].mime_type); } data->memory_free(data->memory_user_data, data->images); data->memory_free(data->memory_user_data, data->textures); data->memory_free(data->memory_user_data, data->samplers); } #define CGLTF_CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return -128; } static char cgltf_to_lower(char c) { if (c >= 'A' && c <= 'Z') { c = 'a' + (c - 'A'); } return c; } static int cgltf_json_strcmp(jsmntok_t const* tok, const uint8_t* json_chunk, const char* str) { CGLTF_CHECK_TOKTYPE(*tok, JSMN_STRING); int const str_len = strlen(str); int const name_length = tok->end - tok->start; if (name_length == str_len) { for (int i = 0; i < str_len; ++i) { char const a = cgltf_to_lower(*((const char*)json_chunk + tok->start + i)); char const b = cgltf_to_lower(*(str + i)); if (a < b) { return -1; } else if (a > b) { return 1; } } return 0; } return 128; } static int cgltf_json_to_int(jsmntok_t const* tok, const uint8_t* json_chunk) { char tmp[128]; CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); int size = tok->end - tok->start; strncpy(tmp, (const char*)json_chunk + tok->start, size); tmp[size] = 0; return atoi(tmp); } static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json_chunk) { char tmp[128]; CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); int size = tok->end - tok->start; strncpy(tmp, (const char*)json_chunk + tok->start, size); tmp[size] = 0; return atof(tmp); } static cgltf_bool cgltf_json_to_bool(jsmntok_t const* tok, const uint8_t* json_chunk) { //TODO: error handling? if (memcmp(json_chunk + tok->start, "true", 4) == 0) return 1; return 0; } static int cgltf_skip_json(jsmntok_t const* tokens, int i) { if (tokens[i].type == JSMN_ARRAY) { int size = tokens[i].size; ++i; for (int j = 0; j < size; ++j) { i = cgltf_skip_json(tokens, i); } } else if (tokens[i].type == JSMN_OBJECT) { int size = tokens[i].size; ++i; for (int j = 0; j < size; ++j) { i = cgltf_skip_json(tokens, i); i = cgltf_skip_json(tokens, i); } } else if (tokens[i].type == JSMN_PRIMITIVE || tokens[i].type == JSMN_STRING) { return i + 1; } return i; } 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); int size = tokens[i].size; ++i; out_prim->indices = (void* )-1; out_prim->material = NULL; for (int j = 0; j < size; ++j) { if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0) { ++i; out_prim->type = (cgltf_primitive_type) cgltf_json_to_int(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0) { ++i; out_prim->indices = (void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "material") == 0) { ++i; out_prim->material = (void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "attributes") == 0) { ++i; if (tokens[i].type != JSMN_OBJECT) { return -1; } out_prim->attributes_count = tokens[i].size; out_prim->attributes = options->memory_alloc(options->memory_user_data, sizeof(cgltf_attribute) * tokens[i].size); ++i; for (cgltf_size iattr = 0; iattr < out_prim->attributes_count; ++iattr) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING); out_prim->attributes[iattr].name = cgltf_attribute_type_invalid; if (cgltf_json_strcmp(tokens+i, json_chunk, "POSITION") == 0) { out_prim->attributes[iattr].name = cgltf_attribute_type_position; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "NORMAL") == 0) { out_prim->attributes[iattr].name = cgltf_attribute_type_normal; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "TANGENT") == 0) { out_prim->attributes[iattr].name = cgltf_attribute_type_tangent; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "TEXCOORD_0") == 0) { out_prim->attributes[iattr].name = cgltf_attribute_type_texcoord_0; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "TEXCOORD_1") == 0) { out_prim->attributes[iattr].name = cgltf_attribute_type_texcoord_1; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "COLOR_0") == 0) { out_prim->attributes[iattr].name = cgltf_attribute_type_color_0; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "JOINTS_0") == 0) { out_prim->attributes[iattr].name = cgltf_attribute_type_joints_0; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "WEIGHTS_0") == 0) { out_prim->attributes[iattr].name = cgltf_attribute_type_weights_0; } ++i; out_prim->attributes[iattr].data = (void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk); ++i; } } else { i = cgltf_skip_json(tokens, i+1); } } return i; } static int cgltf_parse_json_mesh(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_size mesh_index, cgltf_data* out_data) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); out_data->meshes[mesh_index].name = NULL; int size = tokens[i].size; ++i; for (int j = 0; j < size; ++j) { if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) { ++i; int strsize = tokens[i].end - tokens[i].start; out_data->meshes[mesh_index].name = options->memory_alloc(options->memory_user_data, strsize + 1); strncpy(out_data->meshes[mesh_index].name, (const char*)json_chunk + tokens[i].start, strsize); out_data->meshes[mesh_index].name[strsize] = 0; ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "primitives") == 0) { ++i; if (tokens[i].type != JSMN_ARRAY) { return -1; } out_data->meshes[mesh_index].primitives_count = tokens[i].size; out_data->meshes[mesh_index].primitives = options->memory_alloc(options->memory_user_data, sizeof(cgltf_primitive) * tokens[i].size); ++i; for (cgltf_size prim_index = 0; prim_index < out_data->meshes[mesh_index].primitives_count; ++prim_index) { i = cgltf_parse_json_primitive(options, tokens, i, json_chunk, &out_data->meshes[mesh_index].primitives[prim_index]); if (i < 0) { return i; } } } else { i = cgltf_skip_json(tokens, i+1); } } 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) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); out_data->meshes_count = tokens[i].size; out_data->meshes = options->memory_alloc(options->memory_user_data, sizeof(cgltf_mesh) * out_data->meshes_count); ++i; for (cgltf_size j = 0 ; j < out_data->meshes_count; ++j) { i = cgltf_parse_json_mesh(options, tokens, i, json_chunk, j, out_data); if (i < 0) { return i; } } return i; } static int cgltf_parse_json_accessor(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_size accessor_index, cgltf_data* out_data) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); memset(&out_data->accessors[accessor_index], 0, sizeof(cgltf_accessor)); out_data->accessors[accessor_index].buffer_view = (void*)-1; int size = tokens[i].size; ++i; for (int j = 0; j < size; ++j) { if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) { ++i; out_data->accessors[accessor_index].buffer_view = (void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) { ++i; out_data->accessors[accessor_index].offset = cgltf_json_to_int(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0) { ++i; int type = cgltf_json_to_int(tokens+i, json_chunk); switch (type) { case 5120: type = cgltf_component_type_r_8; break; case 5121: type = cgltf_component_type_r_8u; break; case 5122: type = cgltf_component_type_r_16; break; case 5123: type = cgltf_component_type_r_16u; break; case 5125: type = cgltf_component_type_r_32u; break; case 5126: type = cgltf_component_type_r_32f; break; default: type = cgltf_component_type_invalid; break; } out_data->accessors[accessor_index].component_type = type; ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) { ++i; out_data->accessors[accessor_index].count = cgltf_json_to_int(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_data->accessors[accessor_index].type = cgltf_type_scalar; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC2") == 0) { out_data->accessors[accessor_index].type = cgltf_type_vec2; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC3") == 0) { out_data->accessors[accessor_index].type = cgltf_type_vec3; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC4") == 0) { out_data->accessors[accessor_index].type = cgltf_type_vec4; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT2") == 0) { out_data->accessors[accessor_index].type = cgltf_type_mat2; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT3") == 0) { out_data->accessors[accessor_index].type = cgltf_type_mat3; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT4") == 0) { out_data->accessors[accessor_index].type = cgltf_type_mat4; } ++i; } else { i = cgltf_skip_json(tokens, i+1); } } return i; } static int cgltf_parse_json_rgba(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_rgba* out) { int components = tokens[i].size; if (components >= 2) { out->r = cgltf_json_to_float(tokens + ++i, json_chunk); out->g = cgltf_json_to_float(tokens + ++i, json_chunk); if (components > 2) out->b = cgltf_json_to_float(tokens + ++i, json_chunk); if (components > 3) out->a = cgltf_json_to_float(tokens + ++i, json_chunk); } else { out->r = cgltf_json_to_float(tokens + ++i, json_chunk); out->g = out->r; out->b = out->r; out->a = out->r; } return ++i; } static int cgltf_parse_json_texture_view(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_view* out) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); int size = tokens[i].size; ++i; for (int j = 0; j < size; ++j) { if (cgltf_json_strcmp(tokens + i, json_chunk, "index") == 0) { ++i; out->texture = (void*)(size_t)cgltf_json_to_int(tokens + i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0) { ++i; out->texcoord = cgltf_json_to_int(tokens + i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) { ++i; out->scale = cgltf_json_to_float(tokens + i, json_chunk); ++i; } else { i = cgltf_skip_json(tokens, i + 1); } } return i; } static int cgltf_parse_json_pbr(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_size mat_index, cgltf_data* out_data) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); int size = tokens[i].size; ++i; for (int j = 0; j < size; ++j) { if (cgltf_json_strcmp(tokens+i, json_chunk, "metallicFactor") == 0) { ++i; out_data->materials[mat_index].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_data->materials[mat_index].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_rgba(tokens, i + 1, json_chunk, &(out_data->materials[mat_index].pbr.base_color)); } else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorTexture") == 0) { i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk, &(out_data->materials[mat_index].pbr.base_color_texture)); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "metallicRoughnessTexture") == 0) { i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk, &(out_data->materials[mat_index].pbr.metallic_roughness_texture)); } else { i = cgltf_skip_json(tokens, i+1); } } return i; } static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_size img_index, cgltf_data* out_data) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); memset(&out_data->images[img_index], 0, sizeof(cgltf_image)); int size = tokens[i].size; ++i; out_data->images[img_index].buffer_view = (void*)-1; for (int j = 0; j < size; ++j) { if (cgltf_json_strcmp(tokens + i, json_chunk, "uri") == 0) { ++i; int strsize = tokens[i].end - tokens[i].start; out_data->images[img_index].uri = options->memory_alloc(options->memory_user_data, strsize + 1); strncpy(out_data->images[img_index].uri, (const char*)json_chunk + tokens[i].start, strsize); out_data->images[img_index].uri[strsize] = 0; ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) { ++i; out_data->images[img_index].buffer_view = (void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens + i, json_chunk, "mimeType") == 0) { ++i; int strsize = tokens[i].end - tokens[i].start; out_data->images[img_index].mime_type = options->memory_alloc(options->memory_user_data, strsize + 1); strncpy(out_data->images[img_index].mime_type, (const char*)json_chunk + tokens[i].start, strsize); out_data->images[img_index].mime_type[strsize] = 0; ++i; } else { i = cgltf_skip_json(tokens, i + 1); } } return i; } static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_size smp_index, cgltf_data* out_data) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); memset(&out_data->samplers[smp_index], 0, sizeof(cgltf_sampler)); int size = tokens[i].size; ++i; for (int j = 0; j < size; ++j) { if (cgltf_json_strcmp(tokens + i, json_chunk, "magFilter") == 0) { ++i; out_data->samplers[smp_index].mag_filter = cgltf_json_to_int(tokens + i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens + i, json_chunk, "minFilter") == 0) { ++i; out_data->samplers[smp_index].min_filter = cgltf_json_to_int(tokens + i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapS") == 0) { ++i; out_data->samplers[smp_index].wrap_s = cgltf_json_to_int(tokens + i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapT") == 0) { ++i; out_data->samplers[smp_index].wrap_t = cgltf_json_to_int(tokens + i, json_chunk); ++i; } else { i = cgltf_skip_json(tokens, i + 1); } } return i; } static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_size tex_index, cgltf_data* out_data) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); memset(&out_data->textures[tex_index], 0, sizeof(cgltf_texture)); out_data->textures[tex_index].image = (void*)-1; out_data->textures[tex_index].sampler = (void*)-1; int size = tokens[i].size; ++i; for (int j = 0; j < size; ++j) { if (cgltf_json_strcmp(tokens + i, json_chunk, "sampler") == 0) { ++i; out_data->textures[tex_index].sampler = (void*)(size_t)cgltf_json_to_int(tokens + i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) { ++i; out_data->textures[tex_index].image = (void*)(size_t)cgltf_json_to_int(tokens + i, json_chunk); ++i; } else { i = cgltf_skip_json(tokens, i + 1); } } return i; } static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_size mat_index, cgltf_data* out_data) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); cgltf_material* material = &out_data->materials[mat_index]; memset(material, 0, sizeof(cgltf_material)); material->emissive_texture.texture = (void*)-1; material->emissive_texture.scale = 1.0f; material->normal_texture.texture = (void*)-1; material->normal_texture.scale = 1.0f; material->occlusion_texture.texture = (void*)-1; material->occlusion_texture.scale = 1.0f; material->pbr.base_color_texture.texture = (void*)-1; material->pbr.base_color_texture.scale = 1.0f; material->pbr.metallic_roughness_texture.texture = (void*)-1; material->pbr.metallic_roughness_texture.scale = 1.0f; int size = tokens[i].size; ++i; for (int j = 0; j < size; ++j) { if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) { ++i; int strsize = tokens[i].end - tokens[i].start; material->name = options->memory_alloc(options->memory_user_data, strsize + 1); strncpy(material->name, (const char*)json_chunk + tokens[i].start, strsize); material->name[strsize] = 0; ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "pbrMetallicRoughness") == 0) { i = cgltf_parse_json_pbr(tokens, i+1, json_chunk, mat_index, out_data); } else if (cgltf_json_strcmp(tokens+i, json_chunk, "emissiveFactor") == 0) { i = cgltf_parse_json_rgba(tokens, i + 1, json_chunk, &(material->emissive_color)); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "normalTexture") == 0) { i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk, &(material->normal_texture)); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveTexture") == 0) { i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk, &(material->emissive_texture)); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "occlusionTexture") == 0) { i = cgltf_parse_json_texture_view(tokens, i + 1, json_chunk, &(material->occlusion_texture)); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "doubleSided") == 0) { ++i; material->double_sided = cgltf_json_to_bool(tokens + i, json_chunk); ++i; } else { i = cgltf_skip_json(tokens, i+1); } } 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) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); out_data->accessors_count = tokens[i].size; out_data->accessors = options->memory_alloc(options->memory_user_data, sizeof(cgltf_accessor) * out_data->accessors_count); ++i; for (cgltf_size j = 0 ; j < out_data->accessors_count; ++j) { i = cgltf_parse_json_accessor(tokens, i, json_chunk, j, out_data); 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) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); out_data->materials_count = tokens[i].size; out_data->materials = options->memory_alloc(options->memory_user_data, sizeof(cgltf_material) * out_data->materials_count); ++i; for (cgltf_size j = 0; j < out_data->materials_count; ++j) { i = cgltf_parse_json_material(options, tokens, i, json_chunk, j, out_data); 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) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); out_data->images_count = tokens[i].size; out_data->images = options->memory_alloc(options->memory_user_data, sizeof(cgltf_image) * out_data->images_count); ++i; for (cgltf_size j = 0; j < out_data->images_count; ++j) { i = cgltf_parse_json_image(options, tokens, i, json_chunk, j, out_data); 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) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); out_data->textures_count = tokens[i].size; out_data->textures = options->memory_alloc(options->memory_user_data, sizeof(cgltf_texture) * out_data->textures_count); ++i; for (cgltf_size j = 0; j < out_data->textures_count; ++j) { i = cgltf_parse_json_texture(options, tokens, i, json_chunk, j, out_data); 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) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); out_data->samplers_count = tokens[i].size; out_data->samplers = options->memory_alloc(options->memory_user_data, sizeof(cgltf_sampler) * out_data->samplers_count); ++i; for (cgltf_size j = 0; j < out_data->samplers_count; ++j) { i = cgltf_parse_json_sampler(options, tokens, i, json_chunk, j, out_data); if (i < 0) { return i; } } return i; } static int cgltf_parse_json_buffer_view(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_size buffer_view_index, cgltf_data* out_data) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); int size = tokens[i].size; ++i; memset(&out_data->buffer_views[buffer_view_index], 0, sizeof(cgltf_buffer_view)); for (int j = 0; j < size; ++j) { if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0) { ++i; out_data->buffer_views[buffer_view_index].buffer = (void*)(size_t)cgltf_json_to_int(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) { ++i; out_data->buffer_views[buffer_view_index].offset = cgltf_json_to_int(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) { ++i; out_data->buffer_views[buffer_view_index].size = cgltf_json_to_int(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0) { ++i; out_data->buffer_views[buffer_view_index].stride = cgltf_json_to_int(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_data->buffer_views[buffer_view_index].type = type; ++i; } else { i = cgltf_skip_json(tokens, i+1); } } 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) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); out_data->buffer_views_count = tokens[i].size; out_data->buffer_views = options->memory_alloc(options->memory_user_data, sizeof(cgltf_buffer_view) * out_data->buffer_views_count); ++i; for (cgltf_size j = 0 ; j < out_data->buffer_views_count; ++j) { i = cgltf_parse_json_buffer_view(tokens, i, json_chunk, j, out_data); 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_size buffer_index, cgltf_data* out_data) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); out_data->buffers[buffer_index].uri = NULL; int size = tokens[i].size; ++i; for (int j = 0; j < size; ++j) { if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) { ++i; out_data->buffers[buffer_index].size = cgltf_json_to_int(tokens+i, json_chunk); ++i; } else if (cgltf_json_strcmp(tokens+i, json_chunk, "uri") == 0) { ++i; int strsize = tokens[i].end - tokens[i].start; out_data->buffers[buffer_index].uri = options->memory_alloc(options->memory_user_data, strsize + 1); strncpy(out_data->buffers[buffer_index].uri, (const char*)json_chunk + tokens[i].start, strsize); out_data->buffers[buffer_index].uri[strsize] = 0; ++i; } else { i = cgltf_skip_json(tokens, i+1); } } 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) { CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); out_data->buffers_count = tokens[i].size; out_data->buffers = options->memory_alloc(options->memory_user_data, sizeof(cgltf_buffer) * out_data->buffers_count); ++i; for (cgltf_size j = 0 ; j < out_data->buffers_count; ++j) { i = cgltf_parse_json_buffer(options, tokens, i, json_chunk, j, out_data); if (i < 0) { return i; } } return i; } static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type) { cgltf_type size = 0; switch (component_type) { case cgltf_component_type_rgb_32f: size = 12; break; case cgltf_component_type_rgba_32f: size = 16; break; case cgltf_component_type_rg_32f: size = 8; break; case cgltf_component_type_rg_8: size = 2; break; case cgltf_component_type_rg_16: size = 4; break; case cgltf_component_type_rgba_8: size = 4; break; case cgltf_component_type_rgba_16: size = 8; break; case cgltf_component_type_r_8: case cgltf_component_type_r_8u: size = 1; break; case cgltf_component_type_r_16: case cgltf_component_type_r_16u: size = 2; break; case cgltf_component_type_r_32u: case cgltf_component_type_r_32f: size = 4; break; case cgltf_component_type_invalid: default: size = 0; break; } switch (type) { case cgltf_type_vec2: size *= 2; break; case cgltf_type_vec3: size *= 3; break; case cgltf_type_vec4: size *= 4; break; case cgltf_type_mat2: size *= 4; break; case cgltf_type_mat3: size *= 9; break; case cgltf_type_mat4: size *= 16; break; case cgltf_type_invalid: case cgltf_type_scalar: default: size *= 1; break; } return size; } cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data* out_data) { jsmn_parser parser = {0}; if (options->json_token_count == 0) { options->json_token_count = jsmn_parse(&parser, (const char*)json_chunk, size, NULL, 0); } jsmntok_t* tokens = options->memory_alloc(options->memory_user_data, sizeof(jsmntok_t) * options->json_token_count); jsmn_init(&parser); int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, tokens, options->json_token_count); if (token_count < 0 || tokens[0].type != JSMN_OBJECT) { return cgltf_result_invalid_json; } // The root is an object. for (int i = 1; i < token_count; ) { jsmntok_t const* tok = &tokens[i]; if (tok->type == JSMN_STRING && i + 1 < token_count) { int const name_length = tok->end - tok->start; if (name_length == 6 && strncmp((const char*)json_chunk + tok->start, "meshes", 6) == 0) { i = cgltf_parse_json_meshes(options, tokens, i+1, json_chunk, out_data); } else if (name_length == 9 && strncmp((const char*)json_chunk + tok->start, "accessors", 9) == 0) { i = cgltf_parse_json_accessors(options, tokens, i+1, json_chunk, out_data); } else if (name_length == 11 && strncmp((const char*)json_chunk + tok->start, "bufferViews", 11) == 0) { i = cgltf_parse_json_buffer_views(options, tokens, i+1, json_chunk, out_data); } else if (name_length == 7 && strncmp((const char*)json_chunk + tok->start, "buffers", 7) == 0) { i = cgltf_parse_json_buffers(options, tokens, i+1, json_chunk, out_data); } else if (name_length == 9 && strncmp((const char*)json_chunk + tok->start, "materials", 9) == 0) { i = cgltf_parse_json_materials(options, tokens, i+1, json_chunk, out_data); } else if (name_length == 6 && strncmp((const char*)json_chunk + tok->start, "images", 6) == 0) { i = cgltf_parse_json_images(options, tokens, i + 1, json_chunk, out_data); } else if (name_length == 8 && strncmp((const char*)json_chunk + tok->start, "textures", 8) == 0) { i = cgltf_parse_json_textures(options, tokens, i + 1, json_chunk, out_data); } else if (name_length == 8 && strncmp((const char*)json_chunk + tok->start, "samplers", 8) == 0) { i = cgltf_parse_json_samplers(options, tokens, i + 1, json_chunk, out_data); } else { i = cgltf_skip_json(tokens, i+1); } if (i < 0) { return cgltf_result_invalid_json; } } } options->memory_free(options->memory_user_data, tokens); /* Fix up pointers */ for (cgltf_size i = 0; i < out_data->meshes_count; ++i) { for (cgltf_size j = 0; j < out_data->meshes[i].primitives_count; ++j) { if (out_data->meshes[i].primitives[j].indices ==(void*)-1) { out_data->meshes[i].primitives[j].indices = NULL; } else { out_data->meshes[i].primitives[j].indices = &out_data->accessors[(cgltf_size)out_data->meshes[i].primitives[j].indices]; } for (cgltf_size k = 0; k < out_data->meshes[i].primitives[j].attributes_count; ++k) { out_data->meshes[i].primitives[j].attributes[k].data = &out_data->accessors[(cgltf_size)out_data->meshes[i].primitives[j].attributes[k].data]; } } } for (cgltf_size i = 0; i < out_data->accessors_count; ++i) { if (out_data->accessors[i].buffer_view == (void*)-1) { out_data->accessors[i].buffer_view = NULL; } else { out_data->accessors[i].buffer_view = &out_data->buffer_views[(cgltf_size)out_data->accessors[i].buffer_view]; out_data->accessors[i].stride = 0; if (out_data->accessors[i].buffer_view) { out_data->accessors[i].stride = out_data->accessors[i].buffer_view->stride; } } if (out_data->accessors[i].stride == 0) { out_data->accessors[i].stride = cgltf_calc_size(out_data->accessors[i].type, out_data->accessors[i].component_type); } } for (cgltf_size i = 0; i < out_data->textures_count; ++i) { if (out_data->textures[i].image == (void*)-1) { out_data->textures[i].image = NULL; } else { out_data->textures[i].image = &out_data->images[(cgltf_size)out_data->textures[i].image]; } if (out_data->textures[i].sampler == (void*)-1) { out_data->textures[i].sampler = NULL; } else { out_data->textures[i].sampler = &out_data->samplers[(cgltf_size)out_data->textures[i].sampler]; } } for (cgltf_size i = 0; i < out_data->images_count; ++i) { if (out_data->images[i].buffer_view == (void*)-1) { out_data->images[i].buffer_view = NULL; } else { out_data->images[i].buffer_view = &out_data->buffer_views[(cgltf_size)out_data->images[i].buffer_view]; } } for (cgltf_size i = 0; i < out_data->materials_count; ++i) { if (out_data->materials[i].emissive_texture.texture == (void*)-1) { out_data->materials[i].emissive_texture.texture = NULL; } else { out_data->materials[i].emissive_texture.texture = &out_data->textures[(cgltf_size)out_data->materials[i].emissive_texture.texture]; } if (out_data->materials[i].normal_texture.texture == (void*)-1) { out_data->materials[i].normal_texture.texture = NULL; } else { out_data->materials[i].normal_texture.texture = &out_data->textures[(cgltf_size)out_data->materials[i].normal_texture.texture]; } if (out_data->materials[i].occlusion_texture.texture == (void*)-1) { out_data->materials[i].occlusion_texture.texture = NULL; } else { out_data->materials[i].occlusion_texture.texture = &out_data->textures[(cgltf_size)out_data->materials[i].occlusion_texture.texture]; } if (out_data->materials[i].pbr.base_color_texture.texture == (void*)-1) { out_data->materials[i].pbr.base_color_texture.texture = NULL; } else { out_data->materials[i].pbr.base_color_texture.texture = &out_data->textures[(cgltf_size)out_data->materials[i].pbr.base_color_texture.texture]; } if (out_data->materials[i].pbr.metallic_roughness_texture.texture == (void*)-1) { out_data->materials[i].pbr.metallic_roughness_texture.texture = NULL; } else { out_data->materials[i].pbr.metallic_roughness_texture.texture = &out_data->textures[(cgltf_size)out_data->materials[i].pbr.metallic_roughness_texture.texture]; } } for (cgltf_size i = 0; i < out_data->buffer_views_count; ++i) { out_data->buffer_views[i].buffer = &out_data->buffers[(cgltf_size)out_data->buffer_views[i].buffer]; } return cgltf_result_success; } /* * -- jsmn.c start -- * Source: https://github.com/zserge/jsmn * License: MIT */ /** * 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, int start, int 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; int 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; int 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. */ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, unsigned int 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. */ void jsmn_init(jsmn_parser *parser) { parser->pos = 0; parser->toknext = 0; parser->toksuper = -1; } /* * -- jsmn.c end -- */ #endif /* #ifdef CGLTF_IMPLEMENTATION */ #ifdef __cplusplus } #endif