diff --git a/src/l2/core/glb_file.h b/src/l2/core/glb_file.h index 7e46920..9c8a34e 100644 --- a/src/l2/core/glb_file.h +++ b/src/l2/core/glb_file.h @@ -151,6 +151,7 @@ typedef struct { U64 offset; U64 stride; // Set to 0 when not specified. May replace with Option later /* name is overlooked */ + U64 usage_in_accessors; } GltfBufferView; #include "../../../gen/l1/eve/VecGltfBufferView.h" @@ -183,6 +184,43 @@ typedef struct { /* name is overlooked */ } GltfAccessor; +U64 GltfAccessor_get_type_size(const GltfAccessor* self) { + U64 cs; + if (self->component_type == GltfAccessorComponentType_byte) + cs = 1; + else if (self->component_type == GltfAccessorComponentType_unsigned_byte) + cs = 1; + else if (self->component_type == GltfAccessorComponentType_short) + cs = 2; + else if (self->component_type == GltfAccessorComponentType_unsigned_short) + cs = 2; + else if (self->component_type == GltfAccessorComponentType_unsigned_int) + cs = 4; + else if (self->component_type == GltfAccessorComponentType_float) + cs = 4; + else + assert(false); + U64 cc; + if (self->matrix_type == GltfAccessorMatrixType_scalar) + cc = 1; + else if (self->matrix_type == GltfAccessorMatrixType_vec2) + cc = 2; + else if (self->matrix_type == GltfAccessorMatrixType_vec3) + cc = 3; + else if (self->matrix_type == GltfAccessorMatrixType_vec4) + cc = 4; + else if (self->matrix_type == GltfAccessorMatrixType_mat2) + cc = 4; + else if (self->matrix_type == GltfAccessorMatrixType_mat3) + cc = 9; + else if (self->matrix_type == GltfAccessorMatrixType_mat4) + cc = 16; + else + assert(false); + assert(cc * cs <= 64); + return cc * cs; +} + #include "../../../gen/l1/eve/VecGltfAccessor.h" typedef enum { @@ -322,15 +360,6 @@ OptionGltfTextureInfo glb_file_json_dict_field_try_as_tex_info(const Json* dict, ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) { const Json* json = &segments->gltf; - /* default_scene "scene" : Option - * - * scenes "scenes" : Vec { - * if "scenes" field is not present, it counts as empty vector } - * - * nodes "nodes" : Vec { - * 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(); @@ -539,8 +568,10 @@ ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) 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}); + VecGltfBufferView_append(&buffer_views, (GltfBufferView){ + .buffer = opt_buffer_id.some, + .offset = byte_offset, .length = opt_byte_length.some, .stride = byte_stride, + .usage_in_accessors = 0}); } } @@ -693,10 +724,12 @@ ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) 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; + metallic_factor = MIN_float(MAX_float(0, metallic_factor), 1); 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; + roughness_factor = MIN_float(MAX_float(0, roughness_factor), 1); } VecGltfMaterial_append(&materials, (GltfMaterial){.name = VecU8_from_span(name), @@ -786,6 +819,142 @@ ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) } /* Now we are doing some checks and filling fields that were not directly specified */ + for (U64 i = 0; i < buffers.len; i++) { + const GltfBuffer* buffer = &buffers.buf[i]; + if (!buffer->is_external) { + if (buffer->length > segments->bin_segment.len) { + ret_error = VecU8_fmt("Buffer corresponding to bin segment is larger than the actual segment"); + goto destroy_everything_return_error; + } + } + } + + for (U64 i = 0; i < accessors.len; i++) { + const GltfAccessor* accessor = &accessors.buf[i]; + U64 bv = accessor->buffer_view; + if (bv >= buffer_views.len) { + ret_error = VecU8_fmt("Accessor %u has bufferView %u, it is out-of-bounds", i, bv); + goto destroy_everything_return_error; + } + buffer_views.buf[bv].usage_in_accessors++; + } + + for (U64 bv = 0; bv < buffer_views.len; bv++) { + const GltfBufferView* buffer_view = &buffer_views.buf[bv]; + if (buffer_view->usage_in_accessors >= 2 && buffer_view->stride == 0) { + ret_error = VecU8_fmt("BufferView %u is used by several accessors, but stride isn't specified", bv); + goto destroy_everything_return_error; + } + U64 buf_id = buffer_view->buffer; + if (buf_id >= buffers.len) { + ret_error = VecU8_fmt("BufferView %u has buffer %u, that is out-of-bounds of buffers array", bv, buf_id); + goto destroy_everything_return_error; + } + const GltfBuffer* buffer = &buffers.buf[buf_id]; + + if (buffer_view->stride > 900 || + buffer_view->offset > 10000000000000000 || buffer_view->length > 10000000000000000) { + ret_error = VecU8_fmt("Insane buffer view"); + goto destroy_everything_return_error; + } + + U64 view_end = buffer_view->offset + buffer_view->length; + if (view_end > buffer->length) { + ret_error = VecU8_fmt("BufferView %u is out-of-bound of it's Buffer %u", bv, buf_id); + goto destroy_everything_return_error; + } + } + + for (U64 i = 0; i < accessors.len; i++) { + const GltfAccessor* accessor = &accessors.buf[i]; + if (accessor->count < 1) { + ret_error = VecU8_fmt("Accessor %u has count 0", i); + goto destroy_everything_return_error; + } + + U64 bv = accessor->buffer_view; + const GltfBufferView* buffer_view = VecGltfBufferView_at(&buffer_views, bv); + assert(buffer_view->stride <= 900); + U64 my_type_size = GltfAccessor_get_type_size(accessor); + U64 bv_stride = buffer_view->stride; + assert(bv_stride <= 900); + if (bv_stride == 0) { + bv_stride = my_type_size; + } else if (my_type_size > bv_stride) { + ret_error = VecU8_fmt("O_o"); + goto destroy_everything_return_error; + } + if (accessor->count > 10000000000000 || accessor->offset > 10000000000000000) { + ret_error = VecU8_fmt("Insane accessor"); + goto destroy_everything_return_error; + } + U64 accsr_end = accessor->offset + my_type_size + (accessor->count - 1) * bv_stride; + if (accsr_end > buffer_view->length) { + ret_error = VecU8_fmt("Accessor %u is out-of-bound of it's BufferView %u", i, bv); + goto destroy_everything_return_error; + } + } + + for (U64 i = 0; i < textures.len; i++) { + const GltfTexture* texture = &textures.buf[i]; + if (texture->is_in_buffer_view) { + U64 bv = texture->source_buffer_view; + if (bv >= buffer_views.len) { + ret_error = VecU8_fmt("$images[$textures[%u].source].bufferView %u is out-of-bounds", i, bv); + goto destroy_everything_return_error; + } + if (buffer_views.buf[bv].usage_in_accessors > 0) { + ret_error = VecU8_fmt("Please, don't use bufferView both by accessor and image, it's scary", i, bv); + goto destroy_everything_return_error; + } + } + } + + for (U64 i = 0; i < materials.len; i++) { + const GltfMaterial* material = &materials.buf[i]; + if (material->base_color_tex.variant == Option_Some) { + if (material->base_color_tex.some.index >= textures.len) { + ret_error = VecU8_fmt("Material's %u base color texture is out-of-bounds of textures array", i); + goto destroy_everything_return_error; + } + if (material->base_color_tex.some.tex_coord_set_index > 0) { + ret_error = VecU8_fmt("Sorry, texCoord bigger than 0 isn't supported"); + goto destroy_everything_return_error; + } + } + if (material->normal_map_tex.variant == Option_Some) { + if (material->normal_map_tex.some.index >= textures.len) { + ret_error = VecU8_fmt("Material's %u normal map texture is out-of-bounds of textures array", i); + goto destroy_everything_return_error; + } + if (material->normal_map_tex.some.tex_coord_set_index > 0) { + ret_error = VecU8_fmt("Mmmmm NOPE"); + goto destroy_everything_return_error; + } + } + } + + for (U64 i = 0; i < meshes.len; i++) { + const GltfMesh* mesh = &meshes.buf[i]; + for (U64 p = 0; p < mesh->primitives.len; p++) { + const GltfMeshPrimitivePart* primitive = &mesh->primitives.buf[p]; + if ( + (primitive->position.variant == Option_Some && primitive->position.some >= accessors.len) || + (primitive->color_0.variant == Option_Some && primitive->color_0.some >= accessors.len) || + (primitive->texcoord_0.variant == Option_Some && primitive->texcoord_0.some >= accessors.len) || + (primitive->indices.variant == Option_Some && primitive->indices.some >= accessors.len) + /* And whenever I fill like adding another attribute I have to come here and add a check */ + ) { + ret_error = VecU8_fmt("Primitive %u of Mesh %u has an attribute with out-of-bounds accessor", i, p); + goto destroy_everything_return_error; + } + if (primitive->material.variant == Option_Some && primitive->material.some >= materials.len) { + ret_error = VecU8_fmt("Mesh %u Primitiv %u material is out-of-bounds of materials array", i, p); + goto destroy_everything_return_error; + } + } + } + for (U64 i = 0; i < nodes.len; i++) { const GltfNode* node = &nodes.buf[i]; for (size_t jjj = 0; jjj < node->children.len; jjj++) { @@ -802,6 +971,10 @@ ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) lower_node->has_parent = true; lower_node->parent = i; } + if (node->mesh.variant == Option_Some && node->mesh.some >= meshes.len) { + ret_error = VecU8_fmt("Node %u has msh %u that is out-of-bounds of meshes array", i, node->mesh.some); + goto destroy_everything_return_error; + } } for (U64 s = 0; s < scenes.len; s++) {