diff --git a/model.c b/model.c index 0709c12..6a71890 100644 --- a/model.c +++ b/model.c @@ -34,18 +34,13 @@ void model_cleanup(model_t *m) free(m->inverse_bind_matrices); m->inverse_bind_matrices = nullptr; } - if (m->animation.times != nullptr) { - free(m->animation.times); - m->animation.times = nullptr; + if (m->animation.times_us != nullptr) { + free(m->animation.times_us); + m->animation.times_us = nullptr; } - if (m->animation.bones != nullptr) { - for (unsigned i = 0; i < m->bone_count; ++i) { - free(m->animation.bones[i].translations); - free(m->animation.bones[i].rotations); - free(m->animation.bones[i].scales); - } - free(m->animation.bones); - m->animation.bones = nullptr; + if (m->animation.keyframes != nullptr) { + free(m->animation.keyframes); + m->animation.keyframes = nullptr; } m->vertex_count = m->index_count = m->bone_count = m->animation.keyframe_count = 0; diff --git a/model.h b/model.h index 0f88965..7e01ca2 100644 --- a/model.h +++ b/model.h @@ -3,8 +3,8 @@ #include "linear_alg.h" -#include #include +#include typedef uint16_t index_t; @@ -16,28 +16,21 @@ typedef struct { } vertex_t; typedef struct { - uint32_t parent; vec3_t translation; vec4_t rotation; - vec3_t scale; } bone_t; typedef struct { - vec3_t *translations; - vec4_t *rotations; - vec3_t *scales; -} animation_bone_t; - -typedef struct { - float *times; - size_t keyframe_count; - animation_bone_t *bones; + uint32_t *times_us; + bone_t *keyframes; + size_t length_us, keyframe_count; } animation_t; typedef struct { vertex_t *vertices; index_t *indices; bone_t *bones; + uint32_t *bone_parents; mat4_t *inverse_bind_matrices; animation_t animation; size_t vertex_count; diff --git a/model_format_glb.c b/model_format_glb.c index 52c14b7..1fae449 100644 --- a/model_format_glb.c +++ b/model_format_glb.c @@ -159,6 +159,7 @@ typedef struct { typedef struct { bone_t *bones; + uint32_t *bone_parents; gltf_info_member_t normals; gltf_info_member_t positions; gltf_info_member_t bone_indices; @@ -490,7 +491,7 @@ static skin_t read_skins(const uint8_t *p) } static void read_joint_children( - const uint8_t **p, skin_t skin, bone_t bones[MAX_BONE_COUNT], + const uint8_t **p, skin_t skin, uint32_t bone_parents[MAX_BONE_COUNT], uint32_t parent) { assert(**p == '['); @@ -505,9 +506,9 @@ static void read_joint_children( } assert(j < skin.joint_count); - assert(bones[j].parent == UINT32_MAX); + assert(bone_parents[j] == UINT32_MAX); assert(parent < j); - bones[j].parent = parent; + bone_parents[j] = parent; if (**p == ',') ++(*p); @@ -515,13 +516,13 @@ static void read_joint_children( ++(*p); } -static void -read_joints(const uint8_t *p, skin_t skin, bone_t bones[MAX_BONE_COUNT]) +static void read_joints( + const uint8_t *p, skin_t skin, bone_t *bones, uint32_t *bone_parents) { char buffer[MAX_STRING_SIZE]; for (uint32_t i = 0; i < skin.joint_count; ++i) - bones[i].parent = UINT32_MAX; + bone_parents[i] = UINT32_MAX; assert(*p == '['); ++p; @@ -547,16 +548,20 @@ read_joints(const uint8_t *p, skin_t skin, bone_t bones[MAX_BONE_COUNT]) assert(*p == ':'); ++p; - if (strcmp("children", buffer) == 0) - read_joint_children(&p, skin, bones, j); - else if (strcmp("translation", buffer) == 0) + if (strcmp("children", buffer) == 0) { + read_joint_children(&p, skin, bone_parents, j); + } else if (strcmp("translation", buffer) == 0) { bones[j].translation = read_vec3(&p); - else if (strcmp("rotation", buffer) == 0) + } else if (strcmp("rotation", buffer) == 0) { bones[j].rotation = read_vec4(&p); - else if (strcmp("scale", buffer) == 0) - bones[j].scale = read_vec3(&p); - else + } else if (strcmp("scale", buffer) == 0) { + const vec3_t scale = read_vec3(&p); + assert(fabsf(scale.x - 1.0f) < 1e-4); + assert(fabsf(scale.y - 1.0f) < 1e-4); + assert(fabsf(scale.z - 1.0f) < 1e-4); + } else { skip_value(&p); + } if (*p == ',') ++p; @@ -1010,7 +1015,9 @@ static gltf_info_t read_json(const uint8_t *json) bone_t *bones = calloc(skin.joint_count, sizeof(bone_t)); assert(bones != nullptr); - read_joints(nodes_json, skin, bones); + uint32_t *bone_parents = calloc(skin.joint_count, sizeof(uint32_t)); + assert(bone_parents != nullptr); + read_joints(nodes_json, skin, bones, bone_parents); animation_component_t animation[MAX_BONE_COUNT]; read_animations(animations_json, skin, animation); @@ -1070,6 +1077,7 @@ static gltf_info_t read_json(const uint8_t *json) gltf_info_t info = { .bones = bones, + .bone_parents = bone_parents, .positions = info_member(accessors.positions, views.positions), .normals = info_member(accessors.normals, views.normals), .bone_indices @@ -1110,6 +1118,27 @@ static void load_vertices( } } +static void load_keyframes( + bone_t *restrict keyframes, const uint8_t *restrict bin, + const gltf_info_member_t *translations_info, + const gltf_info_member_t *rotations_info, size_t keyframe_count, + size_t bone_count) +{ + for (unsigned b = 0; b < bone_count; ++b) { + const uint8_t *translations = bin + translations_info[b].offset; + const uint8_t *rotations = bin + rotations_info[b].offset; + for (unsigned f = 0; f < keyframe_count; ++f) { + bone_t *dest = keyframes + bone_count * f + b; + memcpy( + &dest->translation, translations + f * sizeof(vec3_t), + sizeof(vec3_t)); + memcpy( + &dest->rotation, rotations + f * sizeof(vec4_t), + sizeof(vec4_t)); + } + } +} + model_t model_format_glb_read(const char *path) { uint8_t *file = map_file(path); @@ -1140,6 +1169,7 @@ model_t model_format_glb_read(const char *path) model_t model = model_init(info.vertex_count, info.index_count); model.bone_count = info.bone_count; model.bones = info.bones; + model.bone_parents = info.bone_parents; load_vertices( model.vertices, (const vec3_t *)(bin_data + info.positions.offset), (const vec3_t *)(bin_data + info.normals.offset), @@ -1155,57 +1185,34 @@ model_t model_format_glb_read(const char *path) // Load animation data model.animation.keyframe_count = info.animation.keyframe_count; - const size_t times_size = model.animation.keyframe_count * sizeof(float); - model.animation.times = malloc(times_size); - assert(model.animation.times != nullptr); - memcpy( - model.animation.times, bin_data + info.animation.times[0].offset, - times_size); - model.animation.length_us = (size_t)roundf( - 1'000'000 - * model.animation.times[model.animation.keyframe_count - 1]); + model.animation.times_us + = malloc(model.animation.keyframe_count * sizeof(uint32_t)); + assert(model.animation.times_us != nullptr); + const float *times + = (const float *)(bin_data + info.animation.times[0].offset); + for (unsigned i = 0; i < model.animation.keyframe_count; ++i) + model.animation.times_us[i] = (size_t)roundf(1'000'000 * times[i]); + model.animation.length_us + = model.animation.times_us[model.animation.keyframe_count - 1]; for (unsigned i = 1; i < info.bone_count; ++i) { // Verify that all time series data matches if (info.animation.times[i].offset != info.animation.times[0].offset) { assert( memcmp( - model.animation.times, - bin_data + info.animation.times[i].offset, times_size) + bin_data + info.animation.times[0].offset, + bin_data + info.animation.times[i].offset, + model.animation.keyframe_count * sizeof(float)) == 0); } } - model.animation.bones - = malloc(model.bone_count * sizeof(animation_bone_t)); - assert(model.animation.bones != nullptr); - for (unsigned i = 0; i < info.bone_count; ++i) { - // Load translations - model.animation.bones[i].translations - = malloc(model.animation.keyframe_count * sizeof(vec3_t)); - assert(model.animation.bones[i].translations != nullptr); - memcpy( - model.animation.bones[i].translations, - bin_data + info.animation.translations[i].offset, - model.animation.keyframe_count * sizeof(vec3_t)); - - // Load rotations - model.animation.bones[i].rotations - = malloc(model.animation.keyframe_count * sizeof(vec4_t)); - assert(model.animation.bones[i].rotations != nullptr); - memcpy( - model.animation.bones[i].rotations, - bin_data + info.animation.rotations[i].offset, - model.animation.keyframe_count * sizeof(vec4_t)); - - // Load scales - model.animation.bones[i].scales - = malloc(model.animation.keyframe_count * sizeof(vec3_t)); - assert(model.animation.bones[i].scales != nullptr); - memcpy( - model.animation.bones[i].scales, - bin_data + info.animation.scales[i].offset, - model.animation.keyframe_count * sizeof(vec3_t)); - } + model.animation.keyframes = malloc( + model.animation.keyframe_count * model.bone_count * sizeof(bone_t)); + assert(model.animation.keyframes != nullptr); + load_keyframes( + model.animation.keyframes, bin_data, info.animation.translations, + info.animation.rotations, model.animation.keyframe_count, + model.bone_count); munmap(file, len); return model;