diff --git a/src/l1/anne/gltf_structures.h b/src/l1/anne/gltf_structures.h index 62a47c1..2e6b458 100644 --- a/src/l1/anne/gltf_structures.h +++ b/src/l1/anne/gltf_structures.h @@ -7,5 +7,13 @@ void generate_l1_gltf_headers() { SpanU8 l = cstr("l1"), ns = cstr(""); generate_eve_span_company_for_non_primitive_non_clonable(l, ns, cstr("GltfScene"), true, false); generate_eve_span_company_for_non_primitive_non_clonable(l, ns, cstr("GltfNode"), true, false); - generate_eve_span_company_for_non_primitive_non_clonable(l, ns, cstr("Gltf"), true, false); + generate_eve_span_company_for_primitive(l, ns, cstr("GltfBuffer"), true, false); + generate_eve_span_company_for_primitive(l, ns, cstr("GltfBufferView"), true, false); + generate_eve_span_company_for_primitive(l, ns, cstr("GltfAccessor"), true, false); + generate_eve_span_company_for_primitive(l, ns, cstr("GltfMeshPrimitivePart"), true, false); + generate_eve_span_company_for_non_primitive_non_clonable(l, ns, cstr("GltfMesh"), true, false); + generate_eve_span_company_for_non_primitive_non_clonable(l, ns, cstr("GltfMaterial"), true, false); + generate_eve_span_company_for_primitive(l, ns, cstr("GltfTexture"), true, false); + generate_Option_templ_inst_eve_header(l, ns, (option_template_instantiation_op){ + .T = cstr("GltfTextureInfo"), .t_primitive = true, }); } diff --git a/src/l1/core/util.h b/src/l1/core/util.h index 5575f96..1323eeb 100644 --- a/src/l1/core/util.h +++ b/src/l1/core/util.h @@ -16,11 +16,11 @@ typedef const char* CSTR; typedef enum { - Option_Some, Option_None + Option_None = 0, Option_Some = 1 } Option_variant; typedef enum { - Result_Ok, Result_Err + Result_Err = 0, Result_Ok = 1 } Result_variant; NORETURN diff --git a/src/l2/alice/model_file.h b/src/l2/alice/model_file.h index b4b8d44..fd95d7c 100644 --- a/src/l2/alice/model_file.h +++ b/src/l2/alice/model_file.h @@ -238,7 +238,3 @@ GenericMeshTopology alice_expect_read_generic_mesh_from_obj_file(VecU8 file_path OptionGenericMeshTopology option = alice_read_generic_mesh_from_obj_file(file_path); return OptionGenericMeshTopology_expect(option); } -/* -ret_error = VecU8_fmt(""); -goto destroy_everything_return_error; - */ \ No newline at end of file diff --git a/src/l2/core/glb_file.h b/src/l2/core/glb_file.h index 3920ac7..7e46920 100644 --- a/src/l2/core/glb_file.h +++ b/src/l2/core/glb_file.h @@ -4,6 +4,7 @@ #include "../../../gen/l1/VecAndSpan_U64.h" #include "../../../gen/l1/OptionU64.h" #include "../../l1_5/core/quaternion.h" +#include "../../l1/core/VecU8_as_str.h" /* todo: add big endian support */ @@ -135,6 +136,125 @@ void GltfNode_drop(GltfNode self) { #include "../../../gen/l1/eve/VecGltfNode.h" +typedef struct { + U64 length; + bool is_external; + U64 external_file_id; + /* name is overlooked */ +} GltfBuffer; + +#include "../../../gen/l1/eve/VecGltfBuffer.h" + +typedef struct { + U64 buffer; + U64 length; + U64 offset; + U64 stride; // Set to 0 when not specified. May replace with Option later + /* name is overlooked */ +} GltfBufferView; + +#include "../../../gen/l1/eve/VecGltfBufferView.h" + +typedef enum { + GltfAccessorComponentType_byte, /* 5120 */ + GltfAccessorComponentType_unsigned_byte, /* 5121 */ + GltfAccessorComponentType_short, /* 5122 */ + GltfAccessorComponentType_unsigned_short, /* 5123 */ + GltfAccessorComponentType_unsigned_int, /* 5125 */ + GltfAccessorComponentType_float, /* 5126 */ +} GltfAccessorComponentType; + +typedef enum { + GltfAccessorMatrixType_scalar, + GltfAccessorMatrixType_vec2, + GltfAccessorMatrixType_vec3, + GltfAccessorMatrixType_vec4, + GltfAccessorMatrixType_mat2, + GltfAccessorMatrixType_mat3, + GltfAccessorMatrixType_mat4, +} GltfAccessorMatrixType; + +typedef struct { + U64 buffer_view; + U64 offset; + U64 count; + GltfAccessorComponentType component_type; + GltfAccessorMatrixType matrix_type; + /* name is overlooked */ +} GltfAccessor; + +#include "../../../gen/l1/eve/VecGltfAccessor.h" + +typedef enum { + GltfPrimitiveMode_triangles, /* 4, this is default btw */ + GltfPrimitiveMode_something_else, /* I honestly could not care less */ +} GltfPrimitiveMode; + +typedef struct { + OptionU64 position; /* index in accessors array */ + OptionU64 texcoord_0; /* index in accessors array */ + OptionU64 color_0; /* index in accessors array */ + OptionU64 indices; /* index in accessors array */ + OptionU64 material; + GltfPrimitiveMode mode; + /* morph targets are overlooked */ +} GltfMeshPrimitivePart; + +#include "../../../gen/l1/eve/VecGltfMeshPrimitivePart.h" + +typedef struct { + VecGltfMeshPrimitivePart primitives; + VecU8 name; +} GltfMesh; + +void GltfMesh_drop(GltfMesh self) { + VecGltfMeshPrimitivePart_drop(self.primitives); + VecU8_drop(self.name); +} + +#include "../../../gen/l1/eve/VecGltfMesh.h" + +typedef struct { + U64 index; + U64 tex_coord_set_index; // If you are a white person, this should not exceed 0 +} GltfTextureInfo; + +#include "../../../gen/l1/eve/OptionGltfTextureInfo.h" + +typedef struct { + VecU8 name; + OptionGltfTextureInfo base_color_tex; + OptionGltfTextureInfo normal_map_tex; + float metallic_factor; + float roughness_factor; + /* base_color_factor is overlooked */ +} GltfMaterial; + +void GltfMaterial_drop(GltfMaterial self) { + VecU8_drop(self.name); +} + +#include "../../../gen/l1/eve/VecGltfMaterial.h" + +typedef enum { + GltfImageMimeType_png, /* image/png */ + /* The only other mime-type that is allowed is image/jpeg, too bad I don't support it either */ + GltfImageMimeType_jpeg, /* image/jpeg */ + GltfImageMimeType_something_else, +} GltfImageMimeType; + +typedef struct { + /* For simplicity, GltfTexture embeds both GltfImage and GltfSampler */ + /* embeds sampler info. Will add later */ + GltfImageMimeType source_mime_type; + bool is_in_buffer_view; + U64 source_buffer_view; // Relevant only if is_in_buffer_view is true + U64 external_file_id; // Relevant only if is_in_buffer_view is false + /* name is overlooked */ +} GltfTexture; + +#include "../../../gen/l1/eve/VecGltfTexture.h" + #include "../../../gen/l1/VecAndSpan_VecU8.h" typedef struct { @@ -142,6 +262,12 @@ typedef struct { VecGltfNode nodes; VecGltfScene scenes; OptionU64 default_scene; + VecGltfBuffer buffers; + VecGltfBufferView buffer_views; + VecGltfAccessor accessors; + VecGltfMesh meshes; + VecGltfMaterial materials; + VecGltfTexture textures; } GltfFileStructure; void GltfFileStructure_drop(GltfFileStructure self) { @@ -158,10 +284,43 @@ typedef struct { }; } ResultGltfFileStructureOrVecU8; +/* Helper function for glb_file_get_structure. Returns NULL on error. + * json is the toplevel object. Tries to get $samplers[id] $*/ +const Json* glb_file_structure_parsing__get_sampler(const Json* json, U64 id) { + const Json* js = Json_dict_at(json, cstr("samplers")); + if (!js || js->variant != Json_arr) + return NULL; + const VecJson* arr = &js->arr; + if (id >= arr->len) + return NULL; + return &arr->buf[id]; +} + +const Json* glb_file_structure_parsing__get_image(const Json* json, U64 id) { + const Json* js = Json_dict_at(json, cstr("images")); + if (!js || js->variant != Json_arr) + return NULL; + const VecJson* arr = &js->arr; + if (id >= arr->len) + return NULL; + return &arr->buf[id]; +} + +OptionGltfTextureInfo glb_file_json_dict_field_try_as_tex_info(const Json* dict, SpanU8 key) { + const Json* el = Json_dict_at(dict, key); + if (!el) + return None_GltfTextureInfo(); + OptionU64 opt_index = Json_dict_field_try_as_u64(el, cstr("index")); + if (opt_index.variant != Option_Some) + return None_GltfTextureInfo(); + + OptionU64 opt_tex_coord = Json_dict_field_try_as_u64(el, cstr("texCoord")); + return Some_GltfTextureInfo((GltfTextureInfo){ .index = opt_index.some, + opt_tex_coord.variant == Option_Some ? opt_tex_coord.some : 0}); +} + ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) { const Json* json = &segments->gltf; - // VecU8_print(json_encode(json)); - // printf("\n"); /* default_scene "scene" : Option * @@ -172,12 +331,17 @@ ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) * if "nodes" field is not present, it counts as empty vector } */ + VecVecU8 external_files = VecVecU8_new(); VecU8 ret_error = VecU8_new(); OptionU64 default_scene = None_U64(); VecGltfScene scenes = VecGltfScene_new(); VecGltfNode nodes = VecGltfNode_new(); - VecVecU8 external_files = VecVecU8_new(); - + VecGltfBuffer buffers = VecGltfBuffer_new(); + VecGltfBufferView buffer_views = VecGltfBufferView_new(); + VecGltfAccessor accessors = VecGltfAccessor_new(); + VecGltfMesh meshes = VecGltfMesh_new(); + VecGltfMaterial materials = VecGltfMaterial_new(); + VecGltfTexture textures = VecGltfTexture_new(); const Json* json_default_scene = Json_dict_at(json, cstr("scene")); if (json_default_scene) { @@ -199,15 +363,8 @@ ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) for (size_t i = 0; i < arr_scenes->len; i++) { const Json* json_scene = &arr_scenes->buf[i]; - VecU8 name = VecU8_new(); // free - const Json* json_name = Json_dict_at(json_scene, cstr("name")); - if (json_name) { - if (json_name->variant != Json_str) { - ret_error = VecU8_fmt("$scenes[%u].name is not a string", i); - goto destroy_everything_return_error; - } - name = VecU8_clone(&json_name->str); - } + OptionSpanU8 opt_name = Json_dict_field_try_as_str(json_scene, cstr("name")); + SpanU8 name = opt_name.variant == Option_Some ? opt_name.some : cstr(""); VecU64 cur_scene_nodes = VecU64_new(); const Json* json_cur_scene_nodes = Json_dict_at(json_scene, cstr("nodes")); @@ -219,7 +376,7 @@ ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) } cur_scene_nodes = opt_cs_nodes.some; } - VecGltfScene_append(&scenes, (GltfScene){.name = name, .nodes = cur_scene_nodes}); + VecGltfScene_append(&scenes, (GltfScene){.name = VecU8_from_span(name), .nodes = cur_scene_nodes}); } } @@ -233,37 +390,22 @@ ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) nodes = VecGltfNode_new_reserved(arr_nodes->len); for (size_t i = 0; i < arr_nodes->len; i++) { const Json* json_node = &arr_nodes->buf[i]; + VecU64 children = VecU64_new(); // free - VecU8 name = VecU8_new(); // free - const Json* json_name = Json_dict_at(json_node, cstr("name")); - if (json_name) { - if (json_name->variant != Json_str) { - ret_error = VecU8_fmt("nodes[%u].name is not a string", i); - goto destroy_everything_return_error; - } - name = VecU8_clone(&json_name->str); - } + OptionSpanU8 opt_name = Json_dict_field_try_as_str(json_node, cstr("name")); + SpanU8 name = opt_name.variant == Option_Some ? opt_name.some : cstr(""); - VecU64 children = VecU64_new(); const Json* json_children = Json_dict_at(json_node, cstr("children")); if (json_children) { OptionVecU64 opt_children = Json_try_as_arr_of_u64(json_children); if (opt_children.variant != Option_Some) { ret_error = VecU8_fmt("$nodes[%u].children is not an array of positive integers", i); - goto destroy_everything_return_error; + goto destroy_node_info_return_error; } children = opt_children.some; } - OptionU64 mesh = None_U64(); - const Json* json_mesh = Json_dict_at(json_node, cstr("mesh")); - if (json_mesh) { - mesh = Json_try_as_u64(json_mesh); - if (mesh.variant != Option_Some) { - ret_error = VecU8_fmt("$nodes[%u].mesh is not an positive integer", i); - goto destroy_everything_return_error; - } - } + OptionU64 mesh = Json_dict_field_try_as_u64(json_node, cstr("mesh")); const Json* json_matrix = Json_dict_at(json_node, cstr("matrix")); const Json* json_translation = Json_dict_at(json_node, cstr("translation")); @@ -273,7 +415,7 @@ ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) bool has_trs = json_translation != NULL || json_rotation != NULL || json_scale != NULL; if (json_matrix && has_trs) { ret_error = VecU8_fmt("$nodes[%u] has both matrix and TRS present", i); - goto destroy_everything_return_error; + goto destroy_node_info_return_error; } GltfNodeTransformation trans; @@ -281,7 +423,7 @@ ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) float md[16]; if (!Json_try_as_MutSpanFlt32(json_matrix, (MutSpanFlt32){md, 16})) { ret_error = VecU8_fmt("nodes[%u].matrix is not an array of 16 floats", i); - goto destroy_everything_return_error; + goto destroy_node_info_return_error; } trans = (GltfNodeTransformation){.variant = GltfNodeTransformation_mat, .mat = { .x = {md[0], md[1], md[2], md[3]}, .y = {md[4], md[5], md[6], md[7]}, @@ -292,7 +434,7 @@ ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) float data[3]; if (!Json_try_as_MutSpanFlt32(json_translation, (MutSpanFlt32){data, 3})) { ret_error = VecU8_fmt("nodes[%u].translation is not an array of 3 floats", i); - goto destroy_everything_return_error; + goto destroy_node_info_return_error; } trans.trs.translation = (vec3){data[0], data[1], data[2]}; } else { @@ -302,7 +444,7 @@ ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) float data[4]; if (!Json_try_as_MutSpanFlt32(json_rotation, (MutSpanFlt32){data, 4})) { ret_error = VecU8_fmt("nodes[%u].rotation is not an array of 4 floats", i); - goto destroy_everything_return_error; + goto destroy_node_info_return_error; } trans.trs.rotation = (quaternion_t){data[3], data[0], data[1], data[2]}; } else { @@ -312,7 +454,7 @@ ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) float data[3]; if (!Json_try_as_MutSpanFlt32(json_scale, (MutSpanFlt32){data, 3})) { ret_error = VecU8_fmt("nodes[%u].scale is not an array of 3 floats", i); - goto destroy_everything_return_error; + goto destroy_node_info_return_error; } trans.trs.scale = (vec3){data[0], data[1], data[2]}; } @@ -320,13 +462,331 @@ ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) trans.variant = GltfNodeTransformation_none; } - VecGltfNode_append(&nodes, (GltfNode){.name = name, .children = children, .mesh = mesh, + VecGltfNode_append(&nodes, (GltfNode){.name = VecU8_from_span(name), .children = children, .mesh = mesh, .trans = trans, .has_parent = false}); + continue; /* When OK */ + destroy_node_info_return_error: + VecU64_drop(children); + goto destroy_everything_return_error; + } + } + + const Json* json_buffers = Json_dict_at(json, cstr("buffers")); + if (json_buffers) { + if (json_buffers->variant != Json_arr) { + ret_error = VecU8_fmt("$buffers is not an array"); + goto destroy_everything_return_error; + } + const VecJson* arr_buffers = &json_buffers->arr; + buffers = VecGltfBuffer_new_reserved(arr_buffers->len); + for (U64 i = 0; i < arr_buffers->len; i++) { + const Json* json_buffer = &arr_buffers->buf[i]; + GltfBuffer buffer; + + OptionU64 opt_byte_length = Json_dict_field_try_as_u64(json_buffer, cstr("byteLength")); + if (opt_byte_length.variant != Option_Some) { + ret_error = VecU8_fmt("$buffers[%u].byteLength is not a positive integer", i); + goto destroy_everything_return_error; + } + buffer.length = opt_byte_length.some; + + const Json* json_uri = Json_dict_at(json_buffer, cstr("uri")); + if (json_uri) { + OptionSpanU8 uri = Json_try_as_str(json_uri); + if (uri.variant != Option_Some) { + ret_error = VecU8_fmt("$buffers[%u].uri is not a string", i); + goto destroy_everything_return_error; + } + buffer.is_external = true; + buffer.external_file_id = external_files.len; + VecVecU8_append(&external_files, VecU8_from_span(uri.some)); + } else { + buffer.is_external = false; + } + + VecGltfBuffer_append(&buffers, buffer); + } + } + + const Json* json_buffer_views = Json_dict_at(json, cstr("bufferViews")); + if (json_buffer_views) { + if (json_buffer_views->variant != Json_arr) { + ret_error = VecU8_fmt("$bufferViews is not an array"); + goto destroy_everything_return_error; + } + const VecJson* arr_buffer_views = &json_buffer_views->arr; + buffer_views = VecGltfBufferView_new_reserved(arr_buffer_views->len); + for (U64 i = 0; i < arr_buffer_views->len; i++) { + const Json* json_buffer_view = &arr_buffer_views->buf[i]; + + OptionU64 opt_buffer_id = Json_dict_field_try_as_u64(json_buffer_view, cstr("buffer")); + if (opt_buffer_id.variant != Option_Some) { + ret_error = VecU8_fmt("$bufferViews[%u].buffer is not a positive integer", i); + goto destroy_everything_return_error; + } + /* At this point I gave up spotting all file errors */ + + OptionU64 opt_byte_offset = Json_dict_field_try_as_u64(json_buffer_view, cstr("byteOffset")); + U64 byte_offset = opt_byte_offset.variant == Option_Some ? opt_byte_offset.some : 0; + + OptionU64 opt_byte_length = Json_dict_field_try_as_u64(json_buffer_view, cstr("byteLength")); + if (opt_byte_length.variant != Option_Some) { + ret_error = VecU8_fmt("$bufferViews[%u].byteLength is not a positive integer", i); + goto destroy_everything_return_error; + } + + OptionU64 opt_byte_stride = Json_dict_field_try_as_u64(json_buffer_view, cstr("byteStride")); + U64 byte_stride = opt_byte_stride.variant == Option_Some ? opt_byte_stride.some : 0; + /* Ignoring an error */ + + VecGltfBufferView_append(&buffer_views, (GltfBufferView){.buffer = opt_buffer_id.some, + .offset = byte_offset, .length = opt_byte_length.some, .stride = byte_stride}); + } + } + + const Json* json_accessors = Json_dict_at(json, cstr("accessors")); + if (json_accessors && json_accessors->variant == Json_arr) { + const VecJson* arr_accessors = &json_accessors->arr; + accessors = VecGltfAccessor_new_reserved(arr_accessors->len); + for (size_t i = 0; i < arr_accessors->len; i++) { + const Json* json_accessor = &arr_accessors->buf[i]; + + OptionU64 opt_buffer_view_id = Json_dict_field_try_as_u64(json_accessor, cstr("bufferView")); + if (opt_buffer_view_id.variant != Option_Some) { + ret_error = VecU8_fmt("Oh, I am really sorry, but accessors without bufferView are not supported", i); + goto destroy_everything_return_error; + } + + OptionU64 opt_byte_offset = Json_dict_field_try_as_u64(json_accessor, cstr("byteOffset")); + U64 byte_offset = opt_byte_offset.variant == Option_Some ? opt_byte_offset.some : 0; + + OptionU64 opt_count = Json_dict_field_try_as_u64(json_accessor, cstr("count")); + if (opt_count.variant != Option_Some) { + ret_error = VecU8_fmt("$accessors[%u].count is not a positive integer", i); + goto destroy_everything_return_error; + } + + GltfAccessorComponentType component_type; + OptionU64 opt_component_type = Json_dict_field_try_as_u64(json_accessor, cstr("componentType")); + if (opt_component_type.variant != Option_Some) { + ret_error = VecU8_fmt("$accessors[%u].componentType is not a positive integer", i); + goto destroy_everything_return_error; + } + U64 int_component_type = opt_component_type.some; + if (int_component_type == 5120) { + component_type = GltfAccessorComponentType_byte; + } else if (int_component_type == 5121) { + component_type = GltfAccessorComponentType_unsigned_byte; + } else if (int_component_type == 5122) { + component_type = GltfAccessorComponentType_short; + } else if (int_component_type == 5123) { + component_type = GltfAccessorComponentType_unsigned_short; + } else if (int_component_type == 5125) { + component_type = GltfAccessorComponentType_unsigned_int; + } else if (int_component_type == 5126) { + component_type = GltfAccessorComponentType_float; + } else { + ret_error = VecU8_fmt("$accessors[%u].componentType %s is incorrect", i, int_component_type); + goto destroy_everything_return_error; + } + + GltfAccessorMatrixType matrix_type; + OptionSpanU8 opt_matrix_type = Json_dict_field_try_as_str(json_accessor, cstr("type")); + if (opt_matrix_type.variant != Option_Some) { + ret_error = VecU8_fmt("$accessors[%u].type is not a string", i); + goto destroy_everything_return_error; + } + if (SpanU8_cont_equal(opt_matrix_type.some, cstr("SCALAR"))) { + matrix_type = GltfAccessorMatrixType_scalar; + } else if (SpanU8_cont_equal(opt_matrix_type.some, cstr("VEC2"))) { + matrix_type = GltfAccessorMatrixType_vec2; + } else if (SpanU8_cont_equal(opt_matrix_type.some, cstr("VEC3"))) { + matrix_type = GltfAccessorMatrixType_vec3; + } else if (SpanU8_cont_equal(opt_matrix_type.some, cstr("VEC4"))) { + matrix_type = GltfAccessorMatrixType_vec4; + } else if (SpanU8_cont_equal(opt_matrix_type.some, cstr("MAT2"))) { + matrix_type = GltfAccessorMatrixType_mat2; + } else if (SpanU8_cont_equal(opt_matrix_type.some, cstr("MAT3"))) { + matrix_type = GltfAccessorMatrixType_mat3; + } else if (SpanU8_cont_equal(opt_matrix_type.some, cstr("MAT4"))) { + matrix_type = GltfAccessorMatrixType_mat4; + } else { + ret_error = VecU8_fmt("$accessors[%u].type is incorrect", i); + goto destroy_everything_return_error; + } + + VecGltfAccessor_append(&accessors, (GltfAccessor){ + .buffer_view = opt_buffer_view_id.some, .offset = byte_offset, .count = opt_count.some, + .component_type = component_type, .matrix_type = matrix_type}); + } + } + + const Json* json_meshes = Json_dict_at(json, cstr("meshes")); + if (json_meshes && json_meshes->variant == Json_arr) { + const VecJson* arr_meshes = &json_meshes->arr; + meshes = VecGltfMesh_new_reserved(arr_meshes->len); + for (size_t i = 0; i < arr_meshes->len; i++) { + const Json* json_mesh = &arr_meshes->buf[i]; + + OptionSpanU8 opt_name = Json_dict_field_try_as_str(json_mesh, cstr("name")); + SpanU8 name = opt_name.variant == Option_Some ? opt_name.some : cstr(""); + + const Json* json_primitives = Json_dict_at(json_mesh, cstr("primitives")); + if (!json_primitives || json_primitives->variant != Json_arr) { + ret_error = VecU8_fmt("meshes[%u].primitives is not a non-empty array", i); + goto destroy_everything_return_error; + } + const VecJson* arr_primitives = &json_primitives->arr; + VecGltfMeshPrimitivePart primitives = VecGltfMeshPrimitivePart_new_reserved(arr_primitives->len); + for (U64 p = 0; p < arr_primitives->len; p++) { + const Json* json_primitive = &arr_primitives->buf[p]; + + OptionU64 indices_accsr = Json_dict_field_try_as_u64(json_primitive, cstr("indices")); + + const Json* json_attributes = Json_dict_at(json_primitive, cstr("attributes")); + if (!json_attributes) { + VecGltfMeshPrimitivePart_drop(primitives); + ret_error = VecU8_fmt("meshes[%u].primitives[%u] does not exist", i, p); + } + + OptionU64 position_accsr = Json_dict_field_try_as_u64(json_attributes, cstr("POSITION")); + OptionU64 texcoord_0_accsr = Json_dict_field_try_as_u64(json_attributes, cstr("TEXCOORD_0")); + OptionU64 color_0_accsr = Json_dict_field_try_as_u64(json_attributes, cstr("COLOR_0")); + + OptionU64 material = Json_dict_field_try_as_u64(json_primitive, cstr("material")); + + GltfPrimitiveMode mode = GltfPrimitiveMode_triangles; + OptionU64 opt_mode = Json_dict_field_try_as_u64(json_primitive, cstr("mode")); + if (opt_mode.variant == Option_Some) { + mode = opt_mode.some == 4 ? GltfPrimitiveMode_triangles : GltfPrimitiveMode_something_else; + } + + VecGltfMeshPrimitivePart_append(&primitives, (GltfMeshPrimitivePart){.position = position_accsr, + .texcoord_0 = texcoord_0_accsr, .color_0 = color_0_accsr, .indices = indices_accsr, + .material = material, .mode = mode}); + } + + VecGltfMesh_append(&meshes, (GltfMesh){.primitives = primitives, .name = VecU8_from_span(name)}); + } + } + + const Json* json_materials = Json_dict_at(json, cstr("materials")); + if (json_materials && json_materials->variant == Json_arr) { + const VecJson* arr_materials = &json_materials->arr; + materials = VecGltfMaterial_new_reserved(arr_materials->len); + for (U64 i = 0; i < arr_materials->len; i++) { + const Json* json_material = &arr_materials->buf[i]; + + OptionSpanU8 opt_name = Json_dict_field_try_as_str(json_material, cstr("name")); + SpanU8 name = opt_name.variant == Option_Some ? opt_name.some : cstr(""); + + OptionGltfTextureInfo normal_map_tex; + normal_map_tex = glb_file_json_dict_field_try_as_tex_info(json_material, cstr("normalTexture")); + + OptionGltfTextureInfo base_color_tex = None_GltfTextureInfo(); + float metallic_factor = 1.f; + float roughness_factor = 1.f; + const Json* pbr_mr = Json_dict_at(json_material, cstr("pbrMetallicRoughness")); + if (pbr_mr) { + base_color_tex = glb_file_json_dict_field_try_as_tex_info(pbr_mr, cstr("baseColorTexture")); + + OptionFlt32 opt_metallic_factor = Json_dict_field_try_as_float(pbr_mr, cstr("metallicFactor")); + if (opt_metallic_factor.some != Option_Some) + metallic_factor = opt_metallic_factor.some; + + OptionFlt32 opt_roughness_factor = Json_dict_field_try_as_float(pbr_mr, cstr("roughnessFactor")); + if (opt_roughness_factor.some != Option_Some) + roughness_factor = opt_roughness_factor.some; + } + + VecGltfMaterial_append(&materials, (GltfMaterial){.name = VecU8_from_span(name), + .base_color_tex = base_color_tex, .normal_map_tex = normal_map_tex, + .metallic_factor = metallic_factor, .roughness_factor = roughness_factor }); + } + } + + const Json* json_textures = Json_dict_at(json, cstr("textures")); + if (json_textures && json_textures->variant == Json_arr) { + const VecJson* arr_textures = &json_textures->arr; + textures = VecGltfTexture_new_reserved(arr_textures->len); + for (U64 i = 0; i < arr_textures->len; i++) { + const Json* json_texture = &arr_textures->buf[i]; + GltfTexture texture; + + OptionU64 opt_sampler_id = Json_dict_field_try_as_u64(json_texture, cstr("sampler")); + if (opt_sampler_id.variant == Option_Some) { + const Json* json_sampler = glb_file_structure_parsing__get_sampler(json, opt_sampler_id.some); + if (!json_sampler) { + ret_error = VecU8_fmt("$textures[%u].sampler points to out-of-bounds sampler", i); + goto destroy_everything_return_error; + } + } + + OptionU64 opt_source_image_id = Json_dict_field_try_as_u64(json_texture, cstr("source")); + if (opt_source_image_id.variant != Option_Some) { + ret_error = VecU8_fmt("Ahh I am so sorry, but textures without images are not supported", i); + goto destroy_everything_return_error; + } + U64 image_id = opt_source_image_id.some; + const Json* json_image = glb_file_structure_parsing__get_image(json, image_id); + if (!json_image) { + ret_error = VecU8_fmt("$textures[%u].source points to out-of-bounds image", i); + goto destroy_everything_return_error; + } + + OptionSpanU8 opt_uri = Json_dict_field_try_as_str(json_image, cstr("uri")); + OptionSpanU8 opt_mime_type = Json_dict_field_try_as_str(json_image, cstr("mimeType")); + OptionU64 opt_buffer_view = Json_dict_field_try_as_u64(json_image, cstr("bufferView")); + + if (opt_buffer_view.variant == Option_Some && opt_uri.variant == Option_Some) { + ret_error = VecU8_fmt("$images[%u] has both uri and bufferView defined", image_id); + goto destroy_everything_return_error; + } + if (opt_buffer_view.variant == Option_Some && opt_mime_type.variant != Option_Some) { + ret_error = VecU8_fmt("$images[%u].bufferView is defined, but mimeType isn't specified", image_id); + goto destroy_everything_return_error; + } + if (opt_buffer_view.variant != Option_Some && opt_uri.variant != Option_Some) { + ret_error = VecU8_fmt("images[%u] has neither uri nor bufferView defined", image_id); + goto destroy_everything_return_error; + } + + texture.source_mime_type = GltfImageMimeType_something_else; + if (opt_mime_type.variant != Option_Some) { + if (SpanU8_cont_equal(opt_mime_type.some, cstr("image/png"))) { + texture.source_mime_type = GltfImageMimeType_png; + } else if (SpanU8_cont_equal(opt_mime_type.some, cstr("image/jpeg"))) { + texture.source_mime_type = GltfImageMimeType_jpeg; + } + } + + if (opt_uri.variant == Option_Some) { + SpanU8 uri = opt_uri.some; + if (opt_mime_type.variant != Option_Some) { + assert(texture.source_mime_type == GltfImageMimeType_something_else); + if (SpanU8_is_postfix(cstr(".png"), uri)) { + texture.source_mime_type = GltfImageMimeType_png; + } else if (SpanU8_is_postfix(cstr(".png"), uri)) { + texture.source_mime_type = GltfImageMimeType_jpeg; + } + } + U64 new_external_file_id = external_files.len; + VecVecU8_append(&external_files, VecU8_from_span(uri)); + + texture.is_in_buffer_view = false; + texture.external_file_id = new_external_file_id; + } else { + assert(opt_buffer_view.variant == Option_Some); + texture.is_in_buffer_view = true; + texture.source_buffer_view = opt_buffer_view.some; + } + + VecGltfTexture_append(&textures, texture); } } /* Now we are doing some checks and filling fields that were not directly specified */ - for (size_t i = 0; i < nodes.len; i++) { + for (U64 i = 0; i < nodes.len; i++) { const GltfNode* node = &nodes.buf[i]; for (size_t jjj = 0; jjj < node->children.len; jjj++) { U64 n2 = node->children.buf[jjj]; @@ -344,7 +804,7 @@ ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) } } - for (size_t s = 0; s < scenes.len; s++) { + for (U64 s = 0; s < scenes.len; s++) { const GltfScene* scene = &scenes.buf[s]; for (size_t jjj = 0; jjj < scene->nodes.len; jjj++) { U64 n = scene->nodes.buf[jjj]; @@ -365,9 +825,37 @@ ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) goto destroy_everything_return_error; } - /* Printing stuff (for test) */ - for (U64 i = 0; i < nodes.len; i++) { - const GltfNode* node = &nodes.buf[i]; + + /* ret_error stores nothing, nothing to free */ + return (ResultGltfFileStructureOrVecU8){.variant = Result_Ok, .ok = (GltfFileStructure){ + .nodes = nodes, .scenes = scenes, .default_scene = default_scene, .external_files = external_files}}; + destroy_everything_return_error: + VecVecU8_drop(external_files); + VecGltfNode_drop(nodes); + VecGltfScene_drop(scenes); + VecGltfBuffer_drop(buffers); + VecGltfBufferView_drop(buffer_views); + VecGltfAccessor_drop(accessors); + VecGltfMesh_drop(meshes); + VecGltfMaterial_drop(materials); + VecGltfTexture_drop(textures); + return (ResultGltfFileStructureOrVecU8){.variant = Result_Err, .err = ret_error}; +} + +void GltfFileStructure_debug_print(const GltfFileStructure* self) { + const VecVecU8* external_files = &self->external_files; + const VecGltfNode* nodes = &self->nodes; + const VecGltfScene* scenes = &self->scenes; + OptionU64 default_scene = self->default_scene; + const VecGltfBuffer* buffers = &self->buffers; + const VecGltfBufferView* buffer_views = &self->buffer_views; + const VecGltfAccessor* accessors = &self->accessors; + const VecGltfMesh* meshes = &self->meshes; + const VecGltfMaterial* materials = &self->materials; + const VecGltfTexture* textures = &self->textures; + + for (U64 i = 0; i < nodes->len; i++) { + const GltfNode* node = &nodes->buf[i]; VecU8 children_txt = VecU8_new(); for (size_t j = 0; j < node->children.len; j++) { if (j) @@ -380,8 +868,8 @@ ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) node->mesh.variant == Option_Some ? VecU8_fmt("mesh= %u", node->mesh.some) : vcstr("no mesh"))); } - for (U64 i = 0; i < scenes.len; i++) { - const GltfScene* scene = &scenes.buf[i]; + for (U64 i = 0; i < scenes->len; i++) { + const GltfScene* scene = &scenes->buf[i]; VecU8 nodes_txt = VecU8_new(); for (size_t j = 0; j < scene->nodes.len; j++) { if (j) @@ -393,15 +881,4 @@ ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) VecU8_print(VecU8_fmt("default_scene: %v\n", default_scene.variant == Option_None ? vcstr("None") : U64_stringification(default_scene.some))); - /* End of debug print */ - - - /* ret_error stores nothing, nothing to free */ - return (ResultGltfFileStructureOrVecU8){.variant = Result_Ok, .ok = (GltfFileStructure){ - .nodes = nodes, .scenes = scenes, .default_scene = default_scene, .external_files = external_files}}; - destroy_everything_return_error: - VecGltfNode_drop(nodes); - VecGltfScene_drop(scenes); - VecVecU8_drop(external_files); - return (ResultGltfFileStructureOrVecU8){.variant = Result_Err, .err = ret_error}; } diff --git a/src/l2/core/json.h b/src/l2/core/json.h index 2e85206..352a9bd 100644 --- a/src/l2/core/json.h +++ b/src/l2/core/json.h @@ -179,4 +179,29 @@ bool Json_try_as_MutSpanFlt32(const Json* self, MutSpanFlt32 ret) { ret.data[i] = opt.some; } return true; -} \ No newline at end of file +} + +OptionU64 Json_dict_field_try_as_u64(const Json* self, SpanU8 key) { + const Json* el = Json_dict_at(self, key); + if (!el) + return None_U64(); + return Json_try_as_u64(el); +} + +OptionSpanU8 Json_dict_field_try_as_str(const Json* self, SpanU8 key) { + const Json* el = Json_dict_at(self, key); + if (!el || el->variant != Json_str) + return None_SpanU8(); + return Some_SpanU8(VecU8_to_span(&el->str)); +} + +OptionFlt32 Json_dict_field_try_as_float(const Json* self, SpanU8 key) { + const Json* el = Json_dict_at(self, key); + if (!el) + return None_Flt32(); + if (el->variant == Json_integer) + return Some_Flt32((float)el->integer); + if (el->variant == Json_float) + return Some_Flt32((float)el->float_num); + return None_Flt32(); +} diff --git a/src/l3/r4/r4.c b/src/l3/r4/r4.c index 3b34905..5abddd4 100644 --- a/src/l3/r4/r4.c +++ b/src/l3/r4/r4.c @@ -412,6 +412,8 @@ int main(){ VecU8_drop(file); abort(); } + VecU8_print(json_encode(&segments_r.ok.gltf)); + printf("\n"); ResultGltfFileStructureOrVecU8 structure_r = glb_file_get_structure(&segments_r.ok); if (structure_r.variant == Result_Err) { printf("Something when parsing gltf\n"); @@ -421,6 +423,8 @@ int main(){ VecU8_drop(file); abort(); } + GltfFileStructure_debug_print(&structure_r.ok); + GltfFileStructure_drop(structure_r.ok); GLBFileSegments_drop(segments_r.ok); VecU8_drop(file); }