Saving progress. Parsing of nodes and scenes arrays of glb object

This commit is contained in:
Андреев Григорий 2026-02-06 17:44:36 +03:00
parent 09df9f4e2d
commit 2dd4ad0d7b
7 changed files with 433 additions and 51 deletions

View File

@ -7,4 +7,5 @@ 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);
}

View File

@ -5,8 +5,12 @@
/* These headers are guarded */
void generate_util_temp_very_base_headers() {
SpanU8 l = cstr("l1"), ns = cstr("");
SpanU8 T_codegen_VecAndSpan[] = {cstr("U8"), cstr("U16"), cstr("U32"), cstr("S32"), cstr("U64"), cstr("S64")};
SpanU8 T_codegen_Option[] = {cstr("U8"), cstr("U16"), cstr("U32"), cstr("U64"), cstr("S64")};
SpanU8 T_codegen_VecAndSpan[] = {
cstr("U8"), cstr("U16"), cstr("U32"), cstr("S32"), cstr("U64"), cstr("S64"), cstr("Flt32"), cstr("Flt64")
};
SpanU8 T_codegen_Option[] = {
cstr("U8"), cstr("U16"), cstr("U32"), cstr("U64"), cstr("S64"), cstr("Flt32"), cstr("Flt64")
};
SpanU8 T_codegen_VecAndSpan_of_Vec[] = {cstr("U8"), cstr("U16"), cstr("U32"), cstr("U64")};
SpanU8 T_codegen_VecAndSpan_of_Span[] = {cstr("U8"), cstr("U16"), cstr("U32"), cstr("U64")};
for (size_t i = 0; i < ARRAY_SIZE(T_codegen_VecAndSpan); i++) {
@ -47,6 +51,13 @@ void generate_util_temp_very_base_headers() {
VecU8_drop(SpanT);
VecU8_drop(dependency);
}
// todo: maybe make these for all other int types too
generate_Option_templ_inst_guarded_header(l, ns, cstr("#include \"VecAndSpan_U8.h\""),
(option_template_instantiation_op){.T = cstr("SpanU8"), .t_primitive = true});
generate_Option_templ_inst_guarded_header(l, ns, cstr("#include \"VecAndSpan_U64.h\""),
(option_template_instantiation_op){.T = cstr("VecU64"), .t_primitive = false});
generate_guarded_span_company_for_primitive(l, ns, cstr("CSTR"), cstr(""), true, false);
generate_ResultType_templ_inst_guarded_header(l, ns,

View File

@ -12,6 +12,8 @@ typedef int8_t S8;
typedef int16_t S16;
typedef int32_t S32;
typedef int64_t S64;
typedef float Flt32;
typedef double Flt64;
// todo: move these to util (l1) template instantiation too
#define int_minmax_function_Definition(T) \

View File

@ -238,32 +238,7 @@ 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);
}
/* Temporary function for some experiments. Will rewrite/delete later */
GenericMeshTopology alice_expect_read_generic_mesh_from_glb_file(VecU8 file_path) {
GLBFileSegments segments;
int code = glb_file_get_segments(VecU8_to_span(&file_path), &segments);
if (code) {
abortf("Something went wrong when reading glb container\n");
}
/* 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 }
*/
OptionU32 default_scene = None_U32();
VecU8_print(VecU8_fmt("default_scene: %v\n",
default_scene.variant == Option_None ? vcstr("None") : U64_stringification(default_scene.some)));
VecGenericMeshVertexInc vertices = VecGenericMeshVertexInc_new();
VecU32 indexes = VecU32_new();
return (GenericMeshTopology){.vertices = vertices, .indexes = indexes};
}
/*
ret_error = VecU8_fmt("");
goto destroy_everything_return_error;
*/

View File

@ -2,7 +2,7 @@
#include "json_encoded.h"
#include "../../../gen/l1/VecAndSpan_U64.h"
#include "../../../gen/l1/OptionU32.h"
#include "../../../gen/l1/OptionU64.h"
#include "../../l1_5/core/quaternion.h"
/* todo: add big endian support */
@ -11,7 +11,7 @@
typedef struct {
U32 version;
Json gltf;
/* If length is 0, BIN segment is absent */
/* If length is 0, BIN segment is absent. Keeps pointing at original string */
SpanU8 bin_segment;
} GLBFileSegments;
@ -19,42 +19,54 @@ void GLBFileSegments_drop(GLBFileSegments self){
Json_drop(self.gltf);
}
/* Can point to some string (.ok can contain span). Be careful with lifetimes */
typedef struct {
Result_variant variant;
union {
GLBFileSegments ok;
// this one has infinite lifetime
SpanU8 err;
};
} ResultGLBFileSegmentsOrSpanU8;
/* Returns positive on error, 0 on ok */
int glb_file_get_segments(SpanU8 file, GLBFileSegments* ret){
ResultGLBFileSegmentsOrSpanU8 glb_file_get_segments(SpanU8 file){
if (file.len < 12) {
return 1;
return (ResultGLBFileSegmentsOrSpanU8){.variant = Result_Err, .err=cstr("too smol")};
}
SpanU8 json_segment = {0, 0}; // length of 0 means segment is absent
SpanU8 bin_segment = {0, 0}; // length of 0 means segment is absent
if (*(const U32*)file.data != 0x46546C67) {
return 2;
return (ResultGLBFileSegmentsOrSpanU8){.variant = Result_Err, .err=cstr("magic number is incorrect")};
}
U32 version = *(const U32*)(file.data + 4);
/* Nobody cares about version */
if (*(const U32*)(file.data + 8) != file.len) {
return 3;
return (ResultGLBFileSegmentsOrSpanU8){.variant = Result_Err, .err=cstr("incorrect glb file length")};;
}
U64 cur = 12;
while (cur < file.len) {
if (cur + 8 > file.len) {
return 4;
return (ResultGLBFileSegmentsOrSpanU8){.variant = Result_Err,
.err=cstr("out-of-bounds when reading segment header")};;
}
U32 chunk_length = *(const U32*)(file.data + cur);
U32 chunk_type = *(const U32*)(file.data + cur + 4);
if (cur + 8 + chunk_length > file.len) {
return 5;
return (ResultGLBFileSegmentsOrSpanU8){.variant = Result_Err,
.err=cstr("out-of-bounds when reading segment data")};;
}
SpanU8 cur_segment = SpanU8_span(file, cur + 8, chunk_length);
if (chunk_type == 0x4E4F534A) {
if (json_segment.len > 0) {
/* Illegal! Two json segments */
return 6;
return (ResultGLBFileSegmentsOrSpanU8){.variant = Result_Err, .err=cstr("two json segments")};;
}
json_segment = cur_segment;
} else if (chunk_type == 0x004E4942) {
if (bin_segment.len > 0) {
/* Illegal! Two bin segments */
return 7;
return (ResultGLBFileSegmentsOrSpanU8){.variant = Result_Err, .err=cstr("two bin segments")};;
}
bin_segment = cur_segment;
}
@ -62,17 +74,15 @@ int glb_file_get_segments(SpanU8 file, GLBFileSegments* ret){
}
if (json_segment.len == 0) {
/* Illegal, no json segment */
return 8;
return (ResultGLBFileSegmentsOrSpanU8){.variant = Result_Err, .err=cstr("no json segment in glb file")};;
}
SpanU8_print(json_segment);
printf("\n");
OptionJson parsed_json = json_decode(json_segment, 15);
if (parsed_json.variant == Option_None) {
return 9;
return (ResultGLBFileSegmentsOrSpanU8){.variant = Result_Err, .err=cstr("syntax error in json segment")};;
}
/* Everything is correct */
*ret = (GLBFileSegments){.version = version, .gltf = parsed_json.some, .bin_segment = bin_segment};
return 0;
return (ResultGLBFileSegmentsOrSpanU8){.variant = Result_Ok,
.ok = (GLBFileSegments){.version = version, .gltf = parsed_json.some, .bin_segment = bin_segment}};
}
typedef struct {
@ -87,10 +97,35 @@ void GltfScene_drop(GltfScene self) {
#include "../../../gen/l1/eve/VecGltfScene.h"
typedef enum {
GltfNodeTransformation_none,
GltfNodeTransformation_trs,
GltfNodeTransformation_mat,
} GltfNodeTransformation_variant;
typedef struct {
vec3 translation;
quaternion_t rotation;
vec3 scale;
} GltfNodeTransformationTRS;
typedef struct {
GltfNodeTransformation_variant variant;
union {
GltfNodeTransformationTRS trs;
mat4 mat;
};
} GltfNodeTransformation;
typedef struct {
VecU8 name;
VecU64 children;
OptionU32 mesh;
OptionU64 mesh;
GltfNodeTransformation trans;
/* These are not specified in gltf file */
bool has_parent;
U64 parent; /* Relevant only when self.has_parent = true */
} GltfNode;
void GltfNode_drop(GltfNode self) {
@ -99,3 +134,274 @@ void GltfNode_drop(GltfNode self) {
}
#include "../../../gen/l1/eve/VecGltfNode.h"
#include "../../../gen/l1/VecAndSpan_VecU8.h"
typedef struct {
VecVecU8 external_files;
VecGltfNode nodes;
VecGltfScene scenes;
OptionU64 default_scene;
} GltfFileStructure;
void GltfFileStructure_drop(GltfFileStructure self) {
VecVecU8_drop(self.external_files);
VecGltfNode_drop(self.nodes);
VecGltfScene_drop(self.scenes);
}
typedef struct {
Result_variant variant;
union {
GltfFileStructure ok;
VecU8 err;
};
} ResultGltfFileStructureOrVecU8;
ResultGltfFileStructureOrVecU8 glb_file_get_structure(GLBFileSegments* segments) {
const Json* json = &segments->gltf;
// VecU8_print(json_encode(json));
// printf("\n");
/* 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 }
*/
VecU8 ret_error = VecU8_new();
OptionU64 default_scene = None_U64();
VecGltfScene scenes = VecGltfScene_new();
VecGltfNode nodes = VecGltfNode_new();
VecVecU8 external_files = VecVecU8_new();
const Json* json_default_scene = Json_dict_at(json, cstr("scene"));
if (json_default_scene) {
default_scene = Json_try_as_u64(json_default_scene);
if (default_scene.variant != Option_Some) {
ret_error = VecU8_fmt("$scene is not an positive integer");
goto destroy_everything_return_error;
}
}
const Json* json_scenes = Json_dict_at(json, cstr("scenes"));
if (json_scenes) {
if (json_scenes->variant != Json_arr){
ret_error = VecU8_fmt("$scenes is not an array");
goto destroy_everything_return_error;
}
const VecJson* arr_scenes = &json_scenes->arr;
scenes = VecGltfScene_new_reserved(arr_scenes->len);
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);
}
VecU64 cur_scene_nodes = VecU64_new();
const Json* json_cur_scene_nodes = Json_dict_at(json_scene, cstr("nodes"));
if (json_cur_scene_nodes) {
OptionVecU64 opt_cs_nodes = Json_try_as_arr_of_u64(json_cur_scene_nodes);
if (opt_cs_nodes.variant != Option_Some) {
ret_error = VecU8_fmt("$scenes[%u].nodes is not an array of positive integers", i);
goto destroy_everything_return_error;
}
cur_scene_nodes = opt_cs_nodes.some;
}
VecGltfScene_append(&scenes, (GltfScene){.name = name, .nodes = cur_scene_nodes});
}
}
const Json* json_nodes = Json_dict_at(json, cstr("nodes"));
if (json_nodes) {
if (json_nodes->variant != Json_arr) {
ret_error = VecU8_fmt("$nodes is not an array");
goto destroy_everything_return_error;
}
const VecJson* arr_nodes = &json_nodes->arr;
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];
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);
}
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;
}
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;
}
}
const Json* json_matrix = Json_dict_at(json_node, cstr("matrix"));
const Json* json_translation = Json_dict_at(json_node, cstr("translation"));
const Json* json_rotation = Json_dict_at(json_node, cstr("rotation"));
const Json* json_scale = Json_dict_at(json_node, cstr("scale"));
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;
}
GltfNodeTransformation trans;
if (json_matrix) {
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;
}
trans = (GltfNodeTransformation){.variant = GltfNodeTransformation_mat, .mat = {
.x = {md[0], md[1], md[2], md[3]}, .y = {md[4], md[5], md[6], md[7]},
.z = {md[8], md[9], md[10], md[11]}, .w = {md[12], md[13], md[14], md[15]}}};
} else if (has_trs) {
trans.variant = GltfNodeTransformation_trs;
if (json_translation) {
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;
}
trans.trs.translation = (vec3){data[0], data[1], data[2]};
} else {
trans.trs.translation = (vec3){0};
}
if (json_rotation) {
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;
}
trans.trs.rotation = (quaternion_t){data[3], data[0], data[1], data[2]};
} else {
trans.trs.rotation = (quaternion_t){1, 0, 0, 0};
}
if (json_scale) {
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;
}
trans.trs.scale = (vec3){data[0], data[1], data[2]};
}
} else {
trans.variant = GltfNodeTransformation_none;
}
VecGltfNode_append(&nodes, (GltfNode){.name = name, .children = children, .mesh = mesh,
.trans = trans, .has_parent = false});
}
}
/* Now we are doing some checks and filling fields that were not directly specified */
for (size_t 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];
if (n2 >= nodes.len) {
ret_error = VecU8_fmt("Node %u has child %u that is out-of-bounds of nodes array", i, n2);
goto destroy_everything_return_error;
}
GltfNode* lower_node = &nodes.buf[n2];
if (lower_node->has_parent) {
ret_error = VecU8_fmt("Node %u has two parents (%u and %u)", n2, lower_node->parent, i);
goto destroy_everything_return_error;
}
lower_node->has_parent = true;
lower_node->parent = i;
}
}
for (size_t 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];
if (n >= nodes.len) {
ret_error = VecU8_fmt("Scene $u has child %u that is out-of-bounds of nodes array", s, n);
goto destroy_everything_return_error;
}
const GltfNode* node = &nodes.buf[n];
if (node->has_parent) {
ret_error = VecU8_fmt("Scene %u specifies a non-root node %u", s, n);
goto destroy_everything_return_error;
}
}
}
if (default_scene.variant == Option_Some && default_scene.some >= scenes.len) {
ret_error = VecU8_fmt("Default scene %u is out-of-bounds of scenes array", default_scene.some);
goto destroy_everything_return_error;
}
/* Printing stuff (for test) */
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)
VecU8_append_cstr(&children_txt, ", ");
U64_stringification_into_buf(node->children.buf[j], &children_txt);
}
VecU8_print(VecU8_fmt("Node #%u \"%r\": %v, children= [ %v ], %v\n", i, node->name,
node->has_parent ? VecU8_fmt("parent= %u", node->parent) : vcstr("root node"),
children_txt,
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];
VecU8 nodes_txt = VecU8_new();
for (size_t j = 0; j < scene->nodes.len; j++) {
if (j)
VecU8_append_cstr(&nodes_txt, ", ");
U64_stringification_into_buf(scene->nodes.buf[j], &nodes_txt);
}
VecU8_print(VecU8_fmt("Scene #%u \"%r\": root nodes= [ %v ]\n", i, scene->name, nodes_txt));
}
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};
}

View File

@ -103,12 +103,80 @@ const Json* Json_dict_at(const Json* self, SpanU8 key) {
while (cur != d->NIL) {
RBTreeNode_KVPVecU8ToJson* n = (RBTreeNode_KVPVecU8ToJson*)cur;
if (SpanU8_less_SpanU8(VecU8_to_span(&n->key), key)) {
cur = cur->left;
} else if (SpanU8_less_SpanU8(key, VecU8_to_span(&n->key))) {
cur = cur->right;
} else if (SpanU8_less_SpanU8(key, VecU8_to_span(&n->key))) {
cur = cur->left;
} else {
return &n->value;
}
}
return NULL;
}
/* Random quality of life json functions */
#include "../../../gen/l1/OptionS64.h"
/* self must not be NULL */
OptionS64 Json_try_as_int(const Json* self) {
return self->variant == Json_integer ? Some_S64(self->integer) : None_S64();
}
#include "../../../gen/l1/OptionU64.h"
OptionU64 Json_try_as_u64(const Json* self) {
return (self->variant == Json_integer && self->integer >= 0) ? Some_U64((U64)self->integer) : None_U64();
}
#include "../../../gen/l1/OptionSpanU8.h"
/* self must not be NULL, returns immutable string reference */
OptionSpanU8 Json_try_as_str(const Json* self) {
return self->variant == Json_str ? Some_SpanU8(VecU8_to_span(&self->str)) : None_SpanU8();
}
#include "../../../gen/l1/OptionFlt32.h"
OptionFlt32 Json_try_as_float(const Json* self) {
if (self->variant == Json_integer)
return Some_Flt32((float)self->integer);
if (self->variant == Json_float)
return Some_Flt32(self->float_num);
return None_Flt32();
}
#include "../../../gen/l1/OptionVecU64.h"
OptionVecU64 Json_try_as_arr_of_u64(const Json* self) {
if (self->variant != Json_arr)
return None_VecU64();
VecU64 res = VecU64_new_zeroinit(self->arr.len);
for (size_t i = 0; i < self->arr.len; i++) {
const Json* el = &self->arr.buf[i];
if (el->variant != Json_integer || el->integer < 0) {
VecU64_drop(res);
return None_VecU64();
}
res.buf[i] = (U64)el->integer;
}
return Some_VecU64(res);
}
#include "../../../gen/l1/VecAndSpan_Flt32.h"
/* true=ok, false=err */
bool Json_try_as_MutSpanFlt32(const Json* self, MutSpanFlt32 ret) {
if (self->variant != Json_arr)
return false;
size_t n = ret.len;
if (self->arr.len != n)
return false;
for (size_t i = 0; i < n; i++) {
const Json* el = &self->arr.buf[i];
OptionFlt32 opt = Json_try_as_float(el);
if (opt.variant != Option_Some)
return false;
ret.data[i] = opt.some;
}
return true;
}

View File

@ -403,5 +403,24 @@ void run_app(){
}
int main(){
alice_expect_read_generic_mesh_from_glb_file(vcstr("./src/l3/models/skeleton.glb"));
VecU8 file = read_file_by_path(vcstr("./src/l3/models/skeleton.glb"));
ResultGLBFileSegmentsOrSpanU8 segments_r = glb_file_get_segments(VecU8_to_span(&file));
if (segments_r.variant == Result_Err) {
printf("Something went wrong when reading glb container\n");
SpanU8_print(segments_r.err);
printf("\n");
VecU8_drop(file);
abort();
}
ResultGltfFileStructureOrVecU8 structure_r = glb_file_get_structure(&segments_r.ok);
if (structure_r.variant == Result_Err) {
printf("Something when parsing gltf\n");
VecU8_print(structure_r.err);
printf("\n");
GLBFileSegments_drop(segments_r.ok);
VecU8_drop(file);
abort();
}
GLBFileSegments_drop(segments_r.ok);
VecU8_drop(file);
}