Saving progres on glb parsing. All checks of structures are done

This commit is contained in:
Андреев Григорий 2026-02-08 17:00:48 +03:00
parent 24a8be3a75
commit 795f675e6a

View File

@ -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<index into scenes>
*
* scenes "scenes" : Vec<Scene> {
* if "scenes" field is not present, it counts as empty vector }
*
* nodes "nodes" : Vec<Node> {
* 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++) {