summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorOmni <omniscient.oce@gmail.com>2024-08-17 17:17:11 +1000
committerOmni <omniscient.oce@gmail.com>2024-08-17 17:17:11 +1000
commiteadfaaa86c29c36dd5ef5d0b6b6fa27af0cdb8b3 (patch)
treeaf18d90ba02d5e26dd6ca746154ce9df6d3b0327 /src
parentcfd7266c21a43cbd37fa712725cea85cdd1f7aab (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.c21
-rw-r--r--src/animation.h46
-rw-r--r--src/maths/primitives.c11
-rw-r--r--src/render/pbr.c22
-rw-r--r--src/render/render_types.h5
-rw-r--r--src/resources/gltf.c93
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;
}