diff options
author | Omni <omniscient.oce@gmail.com> | 2024-08-17 17:17:11 +1000 |
---|---|---|
committer | Omni <omniscient.oce@gmail.com> | 2024-08-17 17:17:11 +1000 |
commit | eadfaaa86c29c36dd5ef5d0b6b6fa27af0cdb8b3 (patch) | |
tree | af18d90ba02d5e26dd6ca746154ce9df6d3b0327 /src | |
parent | cfd7266c21a43cbd37fa712725cea85cdd1f7aab (diff) |
improving the way I load animation data and store it. Load -> Tick(Armature*) -> RenderEnt.Armature -> matrix shenanigans
Diffstat (limited to 'src')
-rw-r--r-- | src/animation.c | 21 | ||||
-rw-r--r-- | src/animation.h | 46 | ||||
-rw-r--r-- | src/maths/primitives.c | 11 | ||||
-rw-r--r-- | src/render/pbr.c | 22 | ||||
-rw-r--r-- | src/render/render_types.h | 5 | ||||
-rw-r--r-- | src/resources/gltf.c | 93 |
6 files changed, 145 insertions, 53 deletions
diff --git a/src/animation.c b/src/animation.c index 48a5ff1..274602c 100644 --- a/src/animation.c +++ b/src/animation.c @@ -4,7 +4,6 @@ #include "maths.h" #include "maths_types.h" #include "ral_types.h" -#include "transform_hierarchy.h" Keyframe Animation_Sample(AnimationSampler* sampler, f32 t) { size_t previous_index = 0; @@ -47,6 +46,26 @@ Keyframe Animation_Sample(AnimationSampler* sampler, f32 t) { } } +void Animation_Tick(AnimationClip* clip, Armature* armature, f32 time) { + TRACE("Ticking animation %s", clip->clip_name); + + for (u32 c_i = 0; c_i < clip->channels->len; c_i++) { + AnimationSampler* sampler = clip->channels->data; + + // Interpolated keyframe based on time + Keyframe k = Animation_Sample(sampler, time); + + // Get the joint in the armature + Joint* joint = &armature->joints->data[sampler->target_joint_idx]; + if (sampler->animation.values.kind == KEYFRAME_ROTATION) { + // Update the joints rotation + joint->transform_components.rotation = k.rotation; + } else { + WARN("not yet implemented animation kind"); + } + } +} + void Animation_VisualiseJoints(Armature* armature) { for (int j = 0; j < armature->joints->len; j++) { Joint joint = armature->joints->data[j]; diff --git a/src/animation.h b/src/animation.h index e0ad543..2a489b8 100644 --- a/src/animation.h +++ b/src/animation.h @@ -1,5 +1,6 @@ #pragma once +#include "cgltf.h" #include "darray.h" #include "defines.h" #include "maths_types.h" @@ -36,9 +37,16 @@ typedef struct Keyframes { } Keyframes; typedef struct Joint { + // used instead of pointers later to update correct joints + size_t node_idx; + ssize_t parent; // parent bone. -1 means its the root + size_t children[8]; // children bones, upto 8 + u8 children_count; char* debug_label; // optional Mat4 inverse_bind_matrix; Mat4 local_transform; + /** @brief holds position, rotation, and scale that will be written to by animation + samplers every tick of the animation system. */ Transform transform_components; } Joint; #ifndef TYPED_JOINT_ARRAY @@ -50,7 +58,6 @@ typedef u32 JointIdx; typedef struct Armature { char* label; - arena arena; Joint_darray* joints; } Armature; @@ -65,41 +72,52 @@ typedef struct AnimationSpline { Interpolation interpolation; } AnimationSpline; +// combines a sampler and a channel in gltf typedef struct AnimationSampler { int current_index; f32 min; f32 max; AnimationSpline animation; + u32 target_joint_idx; // index into the array of joints in an armature } AnimationSampler; +#ifndef TYPED_ANIM_SAMPLER_ARRAY +KITC_DECL_TYPED_ARRAY(AnimationSampler); +#define TYPED_ANIM_SAMPLER_ARRAY +#endif /** @brief Sample an animation at a given time `t` returning an interpolated keyframe */ PUB Keyframe Animation_Sample(AnimationSampler* sampler, f32 t); +/** @brief A clip contains one or more animation curves. */ typedef struct AnimationClip { - // A clip contains one or more animation curves. - // For now I think we can just enumerate all of the properties (assuming *only* one per type is in - // a clip) and NULL = the property is not animated in this clip - // NOTE: when I call 'property' is typically referred to as a 'channel' by GLTF - AnimationSampler* rotation; - AnimationSampler* translation; - AnimationSampler* scale; - AnimationSampler* weights; + const char* clip_name; + bool is_playing; + f32 time; + AnimationSampler_darray* channels; } AnimationClip; #ifndef TYPED_ANIM_CLIP_ARRAY KITC_DECL_TYPED_ARRAY(AnimationClip); #define TYPED_ANIM_CLIP_ARRAY #endif -typedef struct SkinnedAnimation { - Mat4* joint_matrices; - size_t n_joints; -} SkinnedAnimation; +// typedef struct SkinnedAnimation { +// Mat4* joint_matrices; +// size_t n_joints; +// } SkinnedAnimation; PUB void Animation_Play(AnimationClip* clip); +void Animation_Tick(AnimationClip* clip, Armature* armature, f32 delta_time); + void Animation_VisualiseJoints(Armature* armature); +#define MAX_BONES 100 + typedef struct AnimDataUniform { - Mat4 bone_matrices[4]; + Mat4 bone_matrices[MAX_BONES]; } AnimDataUniform; ShaderDataLayout AnimData_GetLayout(void* data); + +// Animation Targets: +// - Mesh +// - Joint diff --git a/src/maths/primitives.c b/src/maths/primitives.c index 551a5df..c24d1e2 100644 --- a/src/maths/primitives.c +++ b/src/maths/primitives.c @@ -296,7 +296,8 @@ Geometry Geo_CreateCylinder(f32 radius, f32 height, u32 resolution) { // bot cap VERT_3D(vertices, VEC3_ZERO, VEC3_NEG_Y, vec2(0, 0)); for (u32 i = 0; i < resolution; i++) { - VERT_3D(vertices, vec3(cos(step * i) * radius, 0.0, sin(step * i) * radius), VEC3_NEG_Y, vec2(0, 0)); + VERT_3D(vertices, vec3(cos(step * i) * radius, 0.0, sin(step * i) * radius), VEC3_NEG_Y, + vec2(0, 0)); } for (u32 i = 1; i < resolution; i++) { push_triangle(indices, 0, i, i + 1); @@ -325,10 +326,10 @@ Geometry Geo_CreateCylinder(f32 radius, f32 height, u32 resolution) { VERT_3D(vertices, vec3(x, 0.0, z), vec3_normalise(vec3(x, 0.0, z)), vec2(0, 0)); } for (u32 i = 0; i < resolution; i++) { - u32 current = sides_start + i * 2; - u32 next = sides_start + ((i + 1) % resolution) * 2; - push_triangle(indices, current, next, current + 1); - push_triangle(indices, current + 1, next, next + 1); + u32 current = sides_start + i * 2; + u32 next = sides_start + ((i + 1) % resolution) * 2; + push_triangle(indices, current, next, current + 1); + push_triangle(indices, current + 1, next, next + 1); } Geometry geo = { diff --git a/src/render/pbr.c b/src/render/pbr.c index 044e6eb..6ea5746 100644 --- a/src/render/pbr.c +++ b/src/render/pbr.c @@ -13,6 +13,7 @@ #include "render_types.h" #include "shader_layouts.h" + void PBR_Init(PBR_Storage* storage) { INFO("PBR shaders init"); storage->pbr_pass = PBR_RPassCreate(); @@ -144,12 +145,27 @@ void PBR_Execute(PBR_Storage* storage, Camera camera, TextureHandle shadowmap_te Binding_Model model_data = { .model = renderable.affine }; GPU_EncodeBindShaderData(enc, 1, Binding_Model_GetLayout(&model_data)); + // Skinning matrices + + // 1. calculate matrices AnimDataUniform anim_data = { 0 }; - for (int i = 0; i < 4; i++) { - anim_data.bone_matrices[i] = mat4_ident(); + CASSERT(renderable.armature); + Armature* skeleton = renderable.armature; + // Skip the first one as we assume its root for this test + for (int j_i = 1; j_i < skeleton->joints->len; j_i++) { + Joint* j = &skeleton->joints->data[j_i]; + j->local_transform = transform_to_mat(&j->transform_components); + Mat4 m = mat4_mult(j->local_transform, j->inverse_bind_matrix); + Joint* p = &skeleton->joints->data[j->parent]; + j->local_transform = mat4_mult(j->local_transform, p->local_transform); + printf("Quat %f \n", j->transform_components.rotation.z); + } + + // 2. bind and upload + for (int j_i = 1; j_i < skeleton->joints->len; j_i++) { + anim_data.bone_matrices[j_i] = skeleton->joints->data[j_i].local_transform; } GPU_EncodeBindShaderData(enc, 3, AnimData_GetLayout(&anim_data)); - // Calculate matrices here // set buffers GPU_EncodeSetVertexBuffer(enc, mesh->vertex_buffer); diff --git a/src/render/render_types.h b/src/render/render_types.h index dd09896..16dee1d 100644 --- a/src/render/render_types.h +++ b/src/render/render_types.h @@ -135,9 +135,8 @@ typedef u32 RenderEntityFlags; typedef struct RenderEnt { MeshHandle mesh; MaterialHandle material; - AnimationClip animation_clip; - bool is_playing; - f32 t; + /** If NULL, no armature and the mesh is static geometry, else it is to be skinned */ + Armature* armature; Mat4 affine; // In the future this should be updated by the transform graph Bbox_3D bounding_box; RenderEntityFlags flags; diff --git a/src/resources/gltf.c b/src/resources/gltf.c index 76ba0a5..8d9217e 100644 --- a/src/resources/gltf.c +++ b/src/resources/gltf.c @@ -37,6 +37,7 @@ KITC_DECL_TYPED_ARRAY(Vec4u) KITC_DECL_TYPED_ARRAY(Vec4i) KITC_DECL_TYPED_ARRAY(Vec4) KITC_DECL_TYPED_ARRAY(face) +KITC_DECL_TYPED_ARRAY(i32) size_t GLTF_LoadMaterials(cgltf_data* data, Str8 relative_path, Material_darray* out_materials); @@ -149,7 +150,7 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel Vec4_darray* tmp_weights = Vec4_darray_new(1000); Material_darray* tmp_materials = Material_darray_new(1); Mesh_darray* tmp_meshes = Mesh_darray_new(1); - u32_darray* tmp_material_indexes = u32_darray_new(1); + i32_darray* tmp_material_indexes = i32_darray_new(1); Joint_darray* joints = Joint_darray_new(256); @@ -183,24 +184,35 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel DEBUG("# Joints %d", num_joints); // Create our data that will be placed onto the model - Armature armature = { .arena = arena_create(malloc(MB(1)), MB(1)) }; + Armature armature = { + .label = "test_skin"}; printf("Skin %s\n", gltf_skin->name); - armature.label = "test_skin"; // armature.label = Clone_cstr(&armature.arena, gltf_skin->name); armature.joints = joints; // ! Make sure not to free this cgltf_accessor* gltf_inverse_bind_matrices = gltf_skin->inverse_bind_matrices; + // --- Joints // for each one we'll spit out a joint for (size_t i = 0; i < num_joints; i++) { - TRACE("Joint %d", i); + // Get the joint and assign its node index for later referencing cgltf_node* joint_node = gltf_skin->joints[i]; - Joint joint_i = { .debug_label = "test_joint" }; + TRACE("Joint %d (node index %d)", i, cgltf_node_index(data, joint_node)); + Joint joint_i = { + .debug_label = "test_joint", + .node_idx = cgltf_node_index(data, joint_node), + .inverse_bind_matrix = mat4_ident() + }; if (joint_node->children_count > 0 && !joint_node->has_translation && !joint_node->has_rotation) { - WARN("joint Node with index %d is the root node", i); + WARN("Joint node with index %d is the root node", i); joint_i.transform_components = TRANSFORM_DEFAULT; + joint_i.parent = -1; + for (u32 c_i = 0; c_i < joint_node->children_count; c_i++) { + joint_i.children[c_i] = cgltf_node_index(data, joint_node->children[c_i]); + joint_i.children_count++; + } } else { TRACE("Storing joint transform"); joint_i.transform_components = TRANSFORM_DEFAULT; @@ -213,8 +225,11 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel if (joint_node->has_scale) { memcpy(&joint_i.transform_components.scale, &joint_node->scale, 3 * sizeof(f32)); } + joint_i.parent = cgltf_node_index(data, joint_node->parent); } + // Calculate and store the starting transform of the joint joint_i.local_transform = transform_to_mat(&joint_i.transform_components); + // Read in the inverse bind matrix cgltf_accessor_read_float(gltf_inverse_bind_matrices, i, &joint_i.inverse_bind_matrix.data[0], 16); Joint_darray_push(armature.joints, joint_i); @@ -280,10 +295,12 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel if (strcmp(primitive.material->name, tmp_materials->data[i].name) == 0) { INFO("Found material"); mat_idx = i; - u32_darray_push(tmp_material_indexes, mat_idx); + i32_darray_push(tmp_material_indexes, mat_idx); break; } } + } else { + i32_darray_push(tmp_material_indexes, -1); } TRACE("Vertex data has been loaded"); @@ -361,34 +378,32 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel out_model->anim_arena = arena_create(malloc(MB(1)), MB(1)); arena* arena = &out_model->anim_arena; + // Iterate over each animation in the GLTF for (int anim_idx = 0; anim_idx < data->animations_count; anim_idx++) { cgltf_animation animation = data->animations[anim_idx]; AnimationClip clip = { 0 }; + clip.clip_name = "test anim clip"; + clip.channels = AnimationSampler_darray_new(1); // for each animation, loop through all the channels for (size_t c = 0; c < animation.channels_count; c++) { cgltf_animation_channel channel = animation.channels[c]; - AnimationSampler* sampler = arena_alloc(arena, sizeof(AnimationSampler)); + AnimationSampler sampler = {0}; - AnimationSampler** target_property; KeyframeKind data_type; switch (channel.target_path) { case cgltf_animation_path_type_rotation: - target_property = &clip.rotation; data_type = KEYFRAME_ROTATION; break; case cgltf_animation_path_type_translation: - target_property = &clip.translation; data_type = KEYFRAME_TRANSLATION; break; case cgltf_animation_path_type_scale: - target_property = &clip.scale; data_type = KEYFRAME_SCALE; break; case cgltf_animation_path_type_weights: - target_property = &clip.weights; data_type = KEYFRAME_WEIGHTS; WARN("Morph target weights arent supported yet"); return false; @@ -397,16 +412,16 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel return false; } - sampler->current_index = 0; - sampler->animation.interpolation = INTERPOLATION_LINEAR; // NOTE: hardcoded for now + sampler.current_index = 0; + sampler.animation.interpolation = INTERPOLATION_LINEAR; // NOTE: hardcoded for now // Keyframe times size_t n_frames = channel.sampler->input->count; CASSERT_MSG(channel.sampler->input->component_type == cgltf_component_type_r_32f, "Expected animation sampler input component to be type f32"); f32* times = arena_alloc(arena, n_frames * sizeof(f32)); - sampler->animation.n_timestamps = n_frames; - sampler->animation.timestamps = times; + sampler.animation.n_timestamps = n_frames; + sampler.animation.timestamps = times; cgltf_accessor_unpack_floats(channel.sampler->input, times, n_frames); // Keyframe values @@ -422,7 +437,7 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel case KEYFRAME_ROTATION: { Quat rot; cgltf_accessor_read_float(channel.sampler->output, v, &rot.x, 4); - printf("Quat %f %f %f %f\n", rot.x, rot.y, rot.z, rot.w); + // printf("Quat %f %f %f %f\n", rot.x, rot.y, rot.z, rot.w); keyframes.values[v].rotation = rot; break; } @@ -444,18 +459,35 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel } } } - sampler->animation.values = keyframes; - sampler->min = channel.sampler->input->min[0]; - sampler->max = channel.sampler->input->max[0]; - - *target_property = sampler; - printf("%d timestamps between %f and %f\n", sampler->animation.n_timestamps, sampler->min, - sampler->max); + sampler.animation.values = keyframes; + sampler.min = channel.sampler->input->min[0]; + sampler.max = channel.sampler->input->max[0]; + + // *target_property = sampler; + printf("%d timestamps between %f and %f\n", sampler.animation.n_timestamps, sampler.min, + sampler.max); + + // TODO: get target + size_t target_index = cgltf_node_index(data, channel.target_node); + size_t joint_index = 0; + bool found = false; + for (u32 ji = 0; ji < main_skeleton.joints->len; ji++) { + if (main_skeleton.joints->data[ji].node_idx == target_index) { + joint_index = ji; + found = true; + break; + } + } + if (!found) { WARN("Coulndnt find joint index");} + sampler.target_joint_idx = joint_index; // NOTE: this assuming the target is a joint at the moment + AnimationSampler_darray_push(clip.channels, sampler); } AnimationClip_darray_push(out_model->animations, clip); } } + + // exit(0); } num_meshes = tmp_meshes->len; @@ -474,8 +506,15 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel memcpy(out_model->materials, mat_handles, num_materials * sizeof(MaterialHandle)); for (u32 mesh_i = 0; mesh_i < num_meshes; mesh_i++) { - u32 mat_idx = tmp_material_indexes->data[mesh_i]; - tmp_meshes->data[mesh_i].material = mat_handles[mat_idx]; + i32 mat_idx = tmp_material_indexes->data[mesh_i]; + if (mat_idx > 0) { + tmp_meshes->data[mesh_i].material = mat_handles[mat_idx]; + + } else { + Material default_mat = PBRMaterialDefault(); + tmp_meshes->data[mesh_i].material = + Material_pool_insert(Render_GetMaterialPool(), &default_mat); + } MeshHandle mesh = Mesh_pool_insert(Render_GetMeshPool(), &tmp_meshes->data[mesh_i]); out_model->meshes[mesh_i] = mesh; } |