summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoromniscient <17525998+omnisci3nce@users.noreply.github.com>2024-08-13 22:13:29 +1000
committeromniscient <17525998+omnisci3nce@users.noreply.github.com>2024-08-13 22:13:29 +1000
commitd4df846c97a7fd216748806abdb729a11a0ce2ec (patch)
treec466b49a356904842f7fb5b7df7c43adfa1b91d7
parent567d384b698151e287e31140709c93f1b92e6db4 (diff)
go
-rw-r--r--assets/shaders/pbr_textured.frag2
-rw-r--r--assets/shaders/skinned_geometry.vert37
-rw-r--r--examples/game_demo/game_demo.c8
-rw-r--r--examples/skinned_animation/ex_skinned_animation.c23
-rw-r--r--src/animation.c101
-rw-r--r--src/animation.h21
-rw-r--r--src/core/camera.c2
-rw-r--r--src/render/pbr.c34
-rw-r--r--src/render/render_types.h7
-rw-r--r--src/resources/gltf.c26
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);