diff options
author | omniscient <17525998+omnisci3nce@users.noreply.github.com> | 2024-08-13 22:13:29 +1000 |
---|---|---|
committer | omniscient <17525998+omnisci3nce@users.noreply.github.com> | 2024-08-13 22:13:29 +1000 |
commit | d4df846c97a7fd216748806abdb729a11a0ce2ec (patch) | |
tree | c466b49a356904842f7fb5b7df7c43adfa1b91d7 | |
parent | 567d384b698151e287e31140709c93f1b92e6db4 (diff) |
go
-rw-r--r-- | assets/shaders/pbr_textured.frag | 2 | ||||
-rw-r--r-- | assets/shaders/skinned_geometry.vert | 37 | ||||
-rw-r--r-- | examples/game_demo/game_demo.c | 8 | ||||
-rw-r--r-- | examples/skinned_animation/ex_skinned_animation.c | 23 | ||||
-rw-r--r-- | src/animation.c | 101 | ||||
-rw-r--r-- | src/animation.h | 21 | ||||
-rw-r--r-- | src/core/camera.c | 2 | ||||
-rw-r--r-- | src/render/pbr.c | 34 | ||||
-rw-r--r-- | src/render/render_types.h | 7 | ||||
-rw-r--r-- | src/resources/gltf.c | 26 |
10 files changed, 173 insertions, 88 deletions
diff --git a/assets/shaders/pbr_textured.frag b/assets/shaders/pbr_textured.frag index d1c725c..0c801f8 100644 --- a/assets/shaders/pbr_textured.frag +++ b/assets/shaders/pbr_textured.frag @@ -112,7 +112,7 @@ void main() { color = color / (color + vec3(1.0)); color = pow(color, vec3(1.0 / 2.2)); - // FragColor = vec4(color, 1.0); + FragColor = vec4(color, 1.0); FragColor = vec4(1.0); // FragColor = vec4(scene.pointLights[0].position); // FragColor = vec4(albedo, 1.0); diff --git a/assets/shaders/skinned_geometry.vert b/assets/shaders/skinned_geometry.vert index 2021691..a610442 100644 --- a/assets/shaders/skinned_geometry.vert +++ b/assets/shaders/skinned_geometry.vert @@ -4,15 +4,24 @@ layout(location = 0) in vec3 inPosition; layout(location = 1) in vec3 inNormal; layout(location = 2) in vec2 inTexCoords; +layout(location = 3) in ivec4 inBoneIndices; +layout(location = 4) in vec4 inWeights; + +const int MAX_BONES = 100; +const int MAX_BONE_INFLUENCES = 4; + +uniform AnimData { + mat4 boneMatrices[MAX_BONES]; +} anim; uniform Camera { - mat4 view; - mat4 proj; - vec4 viewPos; + mat4 view; + mat4 proj; + vec4 viewPos; } cam; uniform Model { - mat4 inner; + mat4 inner; } model; // Outputs @@ -21,13 +30,23 @@ layout(location = 1) out vec3 fragNormal; layout(location = 2) out vec2 fragTexCoords; out vec4 viewPos; +// out vec4 dbgcolor; void main() { - fragWorldPos = vec3(model.inner * vec4(inPosition, 1.0)); - fragNormal = mat3(transpose(inverse(model.inner))) * inNormal; // world-space normal - fragTexCoords = inTexCoords; + mat4 skinMatrix = + inWeights.x * anim.boneMatrices[int(inBoneIndices.x)] + + inWeights.y * anim.boneMatrices[int(inBoneIndices.y)] + + inWeights.z * anim.boneMatrices[int(inBoneIndices.z)] + + inWeights.w * anim.boneMatrices[int(inBoneIndices.w)]; + + vec4 totalPosition = skinMatrix * vec4(inPosition, 1.0); + + fragWorldPos = vec3(model.inner * totalPosition); + fragNormal = mat3(transpose(inverse(model.inner))) * inNormal; // world-space normal + fragTexCoords = inTexCoords; - viewPos = cam.viewPos; + viewPos = cam.viewPos; - gl_Position = cam.proj * cam.view * model.inner * vec4(inPosition, 1.0); + gl_Position = cam.proj * cam.view * model.inner * vec4(inPosition, 1.0); + // gl_Position = cam.proj * cam.view * model.inner * totalPosition; } diff --git a/examples/game_demo/game_demo.c b/examples/game_demo/game_demo.c index 65b31ba..919e306 100644 --- a/examples/game_demo/game_demo.c +++ b/examples/game_demo/game_demo.c @@ -107,10 +107,10 @@ int main() { // Shadow_Run(entities, entity_count); // Quat rot = quat_from_axis_angle(VEC3_X, HALF_PI, true); - // Immdraw_Sphere(transform_create(VEC3_ZERO, quat_ident(), vec3(1.0, 3.0, 1.0)), - // vec4(1.0, 0.0, 0.0, 1.0), true); - // Immdraw_Cuboid(transform_create(vec3(4.0, 0.0, 0.0), quat_ident(), vec3(3.0, 0.5, 3.0)), - // vec4(1.0, 0.0, 0.0, 1.0), true); + Immdraw_Sphere(transform_create(VEC3_ZERO, quat_ident(), vec3(1.0, 3.0, 1.0)), + vec4(1.0, 0.0, 0.0, 1.0), true); + Immdraw_Cuboid(transform_create(vec3(4.0, 0.0, 0.0), quat_ident(), vec3(3.0, 0.5, 3.0)), + vec4(1.0, 0.0, 0.0, 1.0), true); Immdraw_Bbox(transform_create(vec3(0.0, 0.0, 0.0), quat_ident(), vec3(2.0, 2.0, 2.0)), vec4(0.0, 1.0, 0.0, 1.0), true); diff --git a/examples/skinned_animation/ex_skinned_animation.c b/examples/skinned_animation/ex_skinned_animation.c index 7a44160..f7db62e 100644 --- a/examples/skinned_animation/ex_skinned_animation.c +++ b/examples/skinned_animation/ex_skinned_animation.c @@ -1,6 +1,7 @@ #include <assert.h> #include <glfw3.h> +#include "animation.h" #include "camera.h" #include "core.h" #include "loaders.h" @@ -39,9 +40,9 @@ int main() { // scene our_scene = make_default_scene(); - Vec3 cam_pos = vec3_create(0, 5, -8); - Camera camera = - Camera_Create(cam_pos, vec3_normalise(vec3_negate(cam_pos)), VEC3_Y, deg_to_rad(45.0)); + Vec3 cam_pos = vec3_create(0, 1.2, 4); + Camera cam = Camera_Create(cam_pos, VEC3_NEG_Z, VEC3_Y, deg_to_rad(45.0)); + SetCamera(cam); // Main loop const f32 camera_lateral_speed = 0.2; @@ -71,10 +72,22 @@ int main() { // Transform tf = transform_create(VEC3_ZERO, quat_ident(), 1.0); // TODO: Drawing should still just use the PBR pipeline - ModelExtractRenderEnts(rend_ents, handle, mat4_translation(vec3(0, -1, 0)), 0); + Mesh* m = Mesh_Get(simple_skin->meshes[0]); + RenderEnt render_ents[1] = { + (RenderEnt ){ .mesh = simple_skin->meshes[0], + .material = m->material, + .animation_clip = simple_skin->animations->data[0], + .is_playing = true, + .t = 0.0, + .affine = mat4_ident(), + .flags = 0 } + }; + // ModelExtractRenderEnts(rend_ents, handle, mat4_translation(vec3(0, 0, 0)), 0); // draw_skinned_model(&core->renderer, &game.camera, simple_skin, tf, &our_scene); - Render_RenderEntities(rend_ents->data, rend_ents->len); + Render_RenderEntities(render_ents, 1); + + // Animation_VisualiseJoints(&m->armature); RenderEnt_darray_clear(rend_ents); diff --git a/src/animation.c b/src/animation.c index 1c5d893..48a5ff1 100644 --- a/src/animation.c +++ b/src/animation.c @@ -1,38 +1,71 @@ #include "animation.h" +#include "immdraw.h" #include "log.h" #include "maths.h" +#include "maths_types.h" +#include "ral_types.h" +#include "transform_hierarchy.h" -// keyframe animation_sample(animation_sampler *sampler, f32 t) { -// size_t previous_index = 0; -// f32 previous_time = 0.0; -// // look forwards -// // DEBUG("%d\n", sampler->animation.values.kind); -// TRACE("Total timestamps %d", sampler->animation.n_timestamps); -// for (u32 i = 0; i < sampler->animation.n_timestamps; i++) { -// f32 current_time = sampler->animation.timestamps[i]; -// if (current_time > t) { -// break; -// } -// previous_time = sampler->animation.timestamps[i]; -// previous_index = i; -// } - -// size_t next_index = (previous_index + 1) % sampler->animation.n_timestamps; -// f32 next_time = sampler->animation.timestamps[next_index]; -// printf("%d %f %d %f\n", previous_index, previous_time, next_index, next_time); - -// keyframe prev_value = sampler->animation.values.values[previous_index]; -// keyframe next_value = sampler->animation.values.values[next_index]; - -// printf("%d %d\n", previous_index, next_index); - -// f32 time_diff = -// sampler->animation.timestamps[next_index] - sampler->animation.timestamps[previous_index]; -// f32 percent = (t - previous_time) / time_diff; - -// quat interpolated_rot = -// quat_slerp(sampler->animation.values.values[previous_index].rotation, -// sampler->animation.values.values[next_index].rotation, percent); - -// return (keyframe){ .rotation = interpolated_rot }; -// } +Keyframe Animation_Sample(AnimationSampler* sampler, f32 t) { + size_t previous_index = 0; + f32 previous_time = 0.0; + // look forwards + DEBUG("%d\n", keyframe_kind_strings[sampler->animation.values.kind]); + TRACE("Total timestamps %d", sampler->animation.n_timestamps); + for (u32 i = 0; i < sampler->animation.n_timestamps; i++) { + f32 current_time = sampler->animation.timestamps[i]; + if (current_time > t) { + break; + } + previous_time = sampler->animation.timestamps[i]; + previous_index = i; + } + + size_t next_index = (previous_index + 1) % sampler->animation.n_timestamps; + f32 next_time = sampler->animation.timestamps[next_index]; + printf("%d %f %d %f\n", previous_index, previous_time, next_index, next_time); + + Keyframe prev_value = sampler->animation.values.values[previous_index]; + Keyframe next_value = sampler->animation.values.values[next_index]; + + printf("%d %d\n", previous_index, next_index); + + f32 time_diff = + sampler->animation.timestamps[next_index] - sampler->animation.timestamps[previous_index]; + f32 percent = (t - previous_time) / time_diff; + + switch (sampler->animation.values.kind) { + case KEYFRAME_ROTATION: + return (Keyframe){ .rotation = quat_slerp( + sampler->animation.values.values[previous_index].rotation, + sampler->animation.values.values[next_index].rotation, percent) }; + case KEYFRAME_TRANSLATION: + case KEYFRAME_SCALE: + case KEYFRAME_WEIGHTS: + WARN("TODO: other keyframe kind interpolation"); + return prev_value; + } +} + +void Animation_VisualiseJoints(Armature* armature) { + for (int j = 0; j < armature->joints->len; j++) { + Joint joint = armature->joints->data[j]; + Transform tf = joint.transform_components; + tf.scale = vec3(0.05, 0.05, 0.05); + Immdraw_Sphere(tf, vec4(0, 1, 1, 1), true); + } +} + +ShaderDataLayout AnimData_GetLayout(void* data) { + AnimDataUniform* d = data; + bool has_data = data != NULL; + + ShaderBinding b1 = { .label = "AnimData", + .kind = BINDING_BYTES, + .data.bytes.size = sizeof(AnimDataUniform) }; + + if (has_data) { + b1.data.bytes.data = d; + } + return (ShaderDataLayout){ .bindings = { b1 }, .binding_count = 1 }; +} diff --git a/src/animation.h b/src/animation.h index 08ee92a..e0ad543 100644 --- a/src/animation.h +++ b/src/animation.h @@ -4,6 +4,7 @@ #include "defines.h" #include "maths_types.h" #include "mem.h" +#include "ral_types.h" typedef enum Interpolation { INTERPOLATION_STEP, @@ -19,6 +20,8 @@ typedef enum KeyframeKind { KEYFRAME_WEIGHTS, } KeyframeKind; +static const char* keyframe_kind_strings[4] = { "ROTATION", "TRANSLATION", "SCALE", "WEIGHTS"}; + typedef union Keyframe { Quat rotation; Vec3 translation; @@ -70,12 +73,13 @@ typedef struct AnimationSampler { } AnimationSampler; /** @brief Sample an animation at a given time `t` returning an interpolated keyframe */ -Keyframe Animation_Sample(AnimationSampler* sampler, f32 t); +PUB Keyframe Animation_Sample(AnimationSampler* sampler, f32 t); 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) NULL = this property is not animated in this clip + // 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; @@ -91,4 +95,11 @@ typedef struct SkinnedAnimation { size_t n_joints; } SkinnedAnimation; -void animation_play(AnimationClip* clip); +PUB void Animation_Play(AnimationClip* clip); + +void Animation_VisualiseJoints(Armature* armature); + +typedef struct AnimDataUniform { + Mat4 bone_matrices[4]; +} AnimDataUniform; +ShaderDataLayout AnimData_GetLayout(void* data); diff --git a/src/core/camera.c b/src/core/camera.c index c996d7d..77ddad6 100644 --- a/src/core/camera.c +++ b/src/core/camera.c @@ -87,4 +87,4 @@ void FlyCamera_Update(Camera* camera) { } // TODO: Right mouse => pan in screen space -}
\ No newline at end of file +} diff --git a/src/render/pbr.c b/src/render/pbr.c index 57ca1d8..cfa02fc 100644 --- a/src/render/pbr.c +++ b/src/render/pbr.c @@ -1,4 +1,5 @@ #include "pbr.h" +#include "animation.h" #include "camera.h" #include "core.h" #include "file.h" @@ -24,6 +25,12 @@ GPU_Renderpass* PBR_RPassCreate() { } void PBR_PipelinesCreate(PBR_Storage* storage, GPU_Renderpass* rpass) { + // Common shader bindings + ShaderDataLayout camera_data = Binding_Camera_GetLayout(NULL); + ShaderDataLayout model_data = Binding_Model_GetLayout(NULL); + ShaderDataLayout material_data = PBRMaterial_GetLayout(NULL); + ShaderDataLayout lights_data = Binding_Lights_GetLayout(NULL); + // Static { const char* vert_path = "assets/shaders/static_geometry.vert"; @@ -31,11 +38,6 @@ void PBR_PipelinesCreate(PBR_Storage* storage, GPU_Renderpass* rpass) { char* vert_shader = string_from_file(vert_path); char* frag_shader = string_from_file(frag_path); - ShaderDataLayout camera_data = Binding_Camera_GetLayout(NULL); - ShaderDataLayout model_data = Binding_Model_GetLayout(NULL); - ShaderDataLayout material_data = PBRMaterial_GetLayout(NULL); - ShaderDataLayout lights_data = Binding_Lights_GetLayout(NULL); - GraphicsPipelineDesc desc = { .debug_name = "PBR (Static) Pipeline", .vertex_desc = static_3d_vertex_description(), @@ -60,14 +62,11 @@ void PBR_PipelinesCreate(PBR_Storage* storage, GPU_Renderpass* rpass) { char* vert_shader = string_from_file(vert_path); char* frag_shader = string_from_file(frag_path); - ShaderDataLayout camera_data = Binding_Camera_GetLayout(NULL); - ShaderDataLayout model_data = Binding_Model_GetLayout(NULL); - ShaderDataLayout material_data = PBRMaterial_GetLayout(NULL); - ShaderDataLayout lights_data = Binding_Lights_GetLayout(NULL); + ShaderDataLayout anim_uniform = AnimData_GetLayout(NULL); VertexDescription vertex_desc = { .debug_label = "Skinned vertices", .use_full_vertex_size = true }; - VertexDesc_AddAttr(&vertex_desc, "inPos", ATTR_F32x3); + VertexDesc_AddAttr(&vertex_desc, "inPosition", ATTR_F32x3); VertexDesc_AddAttr(&vertex_desc, "inNormal", ATTR_F32x3); VertexDesc_AddAttr(&vertex_desc, "inTexCoords", ATTR_F32x2); VertexDesc_AddAttr(&vertex_desc, "inBoneIndices", ATTR_I32x4); @@ -76,8 +75,8 @@ void PBR_PipelinesCreate(PBR_Storage* storage, GPU_Renderpass* rpass) { GraphicsPipelineDesc desc = { .debug_name = "PBR (Skinned) Pipeline", .vertex_desc = vertex_desc, - .data_layouts = { camera_data, model_data, material_data, lights_data }, - .data_layouts_count = 4, + .data_layouts = { camera_data, model_data, material_data, lights_data, anim_uniform }, + .data_layouts_count = 5, .vs = { .debug_name = "PBR (textured) Vertex Shader", .filepath = str8(vert_path), .code = vert_shader }, @@ -102,7 +101,9 @@ void PBR_Execute(PBR_Storage* storage, Camera camera, TextureHandle shadowmap_te GPU_CmdEncoder* enc = GPU_GetDefaultEncoder(); GPU_CmdEncoder_BeginRender(enc, storage->pbr_pass); - GPU_EncodeBindPipeline(enc, storage->pbr_static_pipeline); + + // TEMP: only do skinned + GPU_EncodeBindPipeline(enc, storage->pbr_skinned_pipeline); // Feed shader data Mat4 view, proj; @@ -143,6 +144,13 @@ 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)); + AnimDataUniform anim_data = {0}; + for (int i =0; i < 4; i++) { + anim_data.bone_matrices[i] = mat4_ident(); + } + GPU_EncodeBindShaderData(enc, 3, AnimData_GetLayout(&anim_data)); + // Calculate matrices here + // set buffers GPU_EncodeSetVertexBuffer(enc, mesh->vertex_buffer); GPU_EncodeSetIndexBuffer(enc, mesh->index_buffer); diff --git a/src/render/render_types.h b/src/render/render_types.h index 5e2b282..dd09896 100644 --- a/src/render/render_types.h +++ b/src/render/render_types.h @@ -43,6 +43,7 @@ typedef struct Mesh { Geometry geometry; // NULL means it has been freed CPU-side MaterialHandle material; bool is_skinned; // false = its static + Armature armature; bool is_uploaded; // has the data been uploaded to the GPU } Mesh; #ifndef TYPED_MESH_CONTAINERS @@ -96,8 +97,7 @@ typedef struct Model { size_t mesh_count; MaterialHandle* materials; size_t material_count; - Armature armature; - bool has_joints; + arena anim_arena; AnimationClip_darray* animations; } Model; #ifndef TYPED_MODEL_ARRAY @@ -135,6 +135,9 @@ typedef u32 RenderEntityFlags; typedef struct RenderEnt { MeshHandle mesh; MaterialHandle material; + AnimationClip animation_clip; + bool is_playing; + f32 t; 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 f788043..c5f1fd1 100644 --- a/src/resources/gltf.c +++ b/src/resources/gltf.c @@ -119,11 +119,11 @@ void load_texcoord_components(Vec2_darray* texcoords, cgltf_accessor* accessor) } } -void load_joint_index_components(Vec4u_darray* joint_indices, cgltf_accessor* accessor) { +void load_joint_index_components(Vec4i_darray* joint_indices, cgltf_accessor* accessor) { TRACE("Load joint indices from accessor"); CASSERT(accessor->component_type == cgltf_component_type_r_16u); CASSERT_MSG(accessor->type == cgltf_type_vec4, "Joint indices should be a vec4"); - Vec4u tmp_joint_index; + Vec4i tmp_joint_index; Vec4 joints_as_floats; for (cgltf_size v = 0; v < accessor->count; ++v) { cgltf_accessor_read_float(accessor, v, &joints_as_floats.x, 4); @@ -133,7 +133,7 @@ void load_joint_index_components(Vec4u_darray* joint_indices, cgltf_accessor* ac tmp_joint_index.w = (u32)joints_as_floats.w; printf("Joints affecting vertex %d : %d %d %d %d\n", v, tmp_joint_index.x, tmp_joint_index.y, tmp_joint_index.z, tmp_joint_index.w); - Vec4u_darray_push(joint_indices, tmp_joint_index); + Vec4i_darray_push(joint_indices, tmp_joint_index); } } @@ -168,6 +168,7 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel // --- Skin size_t num_skins = data->skins_count; bool is_skinned = false; + Armature main_skeleton = {0}; if (num_skins == 1) { is_skinned = true; } else if (num_skins > 1) { @@ -218,9 +219,9 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel 16); Joint_darray_push(armature.joints, joint_i); } - - out_model->armature = armature; - out_model->has_joints = true; + main_skeleton = armature; + // out_model->armature = armature; + // out_model->has_joints = true; } // --- Materials @@ -335,6 +336,7 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel Mesh m = Mesh_Create(geometry, false); if (is_skinned) { m.is_skinned = true; + m.armature = main_skeleton; } Mesh_darray_push(tmp_meshes, m); @@ -356,7 +358,8 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel if (!out_model->animations) { out_model->animations = AnimationClip_darray_new(num_animations); } - arena* arena = &out_model->armature.arena; + out_model->anim_arena = arena_create(malloc(MB(1)), MB(1)); + arena* arena = &out_model->anim_arena; for (int anim_idx = 0; anim_idx < data->animations_count; anim_idx++) { cgltf_animation animation = data->animations[anim_idx]; @@ -395,12 +398,10 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel } sampler->current_index = 0; - // printf("1 %d index\n", sampler->current_index); sampler->animation.interpolation = INTERPOLATION_LINEAR; // NOTE: hardcoded for now // Keyframe times size_t n_frames = channel.sampler->input->count; - assert(channel.sampler->input->component_type == cgltf_component_type_r_32f); 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)); @@ -408,9 +409,6 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel sampler->animation.timestamps = times; cgltf_accessor_unpack_floats(channel.sampler->input, times, n_frames); - // assert_path_type_matches_component_type(channel.target_path, - // channel.sampler->output); - // Keyframe values size_t n_values = channel.sampler->output->count; CASSERT_MSG(n_frames == n_values, "keyframe times = keyframe values"); @@ -451,8 +449,8 @@ bool model_load_gltf_str(const char* file_string, const char* filepath, Str8 rel sampler->max = channel.sampler->input->max[0]; *target_property = sampler; - printf("%d timestamps\n", sampler->animation.n_timestamps); - printf("%d index\n", sampler->current_index); + printf("%d timestamps between %f and %f\n", sampler->animation.n_timestamps, sampler->min, + sampler->max); } AnimationClip_darray_push(out_model->animations, clip); |