Export animations from blender, render correctly with small # of bones!

main
parent 36b910c34b
commit 53c04d573b

@ -89,7 +89,7 @@ for o in D.objects:
o = o.parent o = o.parent
object_transform_info = (mesh_name, mapping @ o.location, o.rotation_euler, o.scale) object_transform_info = (mesh_name, mapping @ o.location, o.rotation_euler, o.scale)
if o.users_collection[0].name == 'Level': if o.users_collection[0].name == 'Level':
assert(False, "Cannot put armatures in the level. The level is for static placed meshes. For dynamic entities, you put them outside of the level collection, their entity kind is encoded, and the game code decides how to draw them") assert False, "Cannot put armatures in the level. The level is for static placed meshes. For dynamic entities, you put them outside of the level collection, their entity kind is encoded, and the game code decides how to draw them"
else: else:
placed_entities.append((mesh_object.name,) + object_transform_info) placed_entities.append((mesh_object.name,) + object_transform_info)
armature_name = o.data.name armature_name = o.data.name
@ -114,7 +114,7 @@ for o in D.objects:
parent_index = i parent_index = i
break break
if parent_index == -1: if parent_index == -1:
assert(false, f"Couldn't find parent of bone {b}") assert False, f"Couldn't find parent of bone {b}"
print(f"Parent of bone {b.name} is index {parent_index} in list {bones_in_armature}") print(f"Parent of bone {b.name} is index {parent_index} in list {bones_in_armature}")
write_i32(f, parent_index) write_i32(f, parent_index)
@ -123,8 +123,33 @@ for o in D.objects:
write_f32(f, b.length) write_f32(f, b.length)
# write the pose information # write the pose information
write_u64(f, len(o.pose.bones))
for pose_bone in o.pose.bones: # it's very important that the array of pose bones contains the same amount of bones
# as there are in the edit bones. Because the edit bones are exported, etc etc. Cowabunga!
assert(len(o.pose.bones) == len(bones_in_armature))
armature = o
for animation in bpy.data.actions:
armature.animation_data.action = animation
startFrame = int(animation.frame_range.x)
endFrame = int(animation.frame_range.y)
total_frames = (endFrame - startFrame) + 1 # the end frame is inclusive
print(f"Exporting animation {animation.name} with {total_frames} frames")
write_u64(f, total_frames)
time_per_anim_frame = 1.0 / float(bpy.context.scene.render.fps)
for frame in range(startFrame, endFrame+1):
time_through_this_frame_occurs_at = (frame - startFrame) * time_per_anim_frame
bpy.context.scene.frame_set(frame)
write_f32(f, time_through_this_frame_occurs_at)
for pose_bone_i in range(len(o.pose.bones)):
pose_bone = o.pose.bones[pose_bone_i]
# in the engine, it's assumed that the poses are in the same order as the bones
# they're referring to. This checks that that is the case.
assert(pose_bone.bone == bones_in_armature[pose_bone_i])
parent_space_pose = None parent_space_pose = None
@ -133,13 +158,14 @@ for o in D.objects:
else: else:
parent_space_pose = mapping @ pose_bone.matrix parent_space_pose = mapping @ pose_bone.matrix
#parent_space_pose = pose_bone.matrix #parent_space_pose = pose_bone.matrix
print("parent_space_pose of the bone with no parent:") #print("parent_space_pose of the bone with no parent:")
print(parent_space_pose) #print(parent_space_pose)
#parent_space_pose = mapping @ pose_bone.matrix #parent_space_pose = mapping @ pose_bone.matrix
translation = parent_space_pose.to_translation() translation = parent_space_pose.to_translation()
rotation = parent_space_pose.to_quaternion() rotation = parent_space_pose.to_quaternion()
scale = parent_space_pose.to_scale() scale = parent_space_pose.to_scale()
write_v3(f, translation) write_v3(f, translation)
write_quat(f, rotation) write_quat(f, rotation)
write_v3(f, scale) write_v3(f, scale)

BIN
art/art.blend (Stored with Git LFS)

Binary file not shown.

@ -837,13 +837,15 @@ typedef struct Mesh
typedef struct PoseBone typedef struct PoseBone
{ {
float time; // time through animation this pose occurs at
Mat4 parent_space_pose; Mat4 parent_space_pose;
} PoseBone; } PoseBone;
typedef struct Bone typedef struct Bone
{ {
struct Bone *parent; struct Bone *parent;
PoseBone pose_bone; PoseBone *anim_poses;
MD_u64 anim_poses_length;
Mat4 matrix_local; Mat4 matrix_local;
Mat4 inverse_model_space_pos; Mat4 inverse_model_space_pos;
float length; float length;
@ -1014,21 +1016,32 @@ Armature load_armature(MD_Arena *arena, MD_String8 binary_file, MD_String8 armat
} }
} }
MD_u64 poses_length; MD_u64 frames_in_anim;
ser_MD_u64(&ser, &poses_length); ser_MD_u64(&ser, &frames_in_anim);
Log("There are %llu animation frames\n", frames_in_anim);
assert(poses_length == to_return.bones_length); for(MD_u64 i = 0; i < to_return.bones_length; i++)
for(MD_u64 i = 0; i < poses_length; i++)
{ {
PoseBone *next_pose_bone = &to_return.bones[i].pose_bone; to_return.bones[i].anim_poses = MD_PushArray(arena, PoseBone, frames_in_anim);
to_return.bones[i].anim_poses_length = frames_in_anim;
}
for(MD_u64 anim_i = 0; anim_i < frames_in_anim; anim_i++)
{
float time_through;
ser_float(&ser, &time_through);
for(MD_u64 pose_bone_i = 0; pose_bone_i < to_return.bones_length; pose_bone_i++)
{
PoseBone *next_pose_bone = &to_return.bones[pose_bone_i].anim_poses[anim_i];
Transform t; Transform t;
ser_Vec3(&ser, &t.offset); ser_Vec3(&ser, &t.offset);
ser_Quat(&ser, &t.rotation); ser_Quat(&ser, &t.rotation);
ser_Vec3(&ser, &t.scale); ser_Vec3(&ser, &t.scale);
next_pose_bone->time = time_through;
next_pose_bone->parent_space_pose = transform_to_mat(t); next_pose_bone->parent_space_pose = transform_to_mat(t);
} }
}
ser_MD_u64(&ser, &to_return.vertices_length); ser_MD_u64(&ser, &to_return.vertices_length);
to_return.vertices = MD_PushArray(arena, ArmatureVertex, to_return.vertices_length); to_return.vertices = MD_PushArray(arena, ArmatureVertex, to_return.vertices_length);
@ -2641,7 +2654,27 @@ void draw_placed(Mat4 view, Mat4 projection, PlacedMesh *cur)
sg_draw(0, (int)drawing->num_vertices, 1); sg_draw(0, (int)drawing->num_vertices, 1);
} }
void draw_armature(Mat4 view, Mat4 projection, Transform t, Armature *armature) Mat4 get_transform_along_time(Bone *bone, float time)
{
float total_anim_time = bone->anim_poses[bone->anim_poses_length - 1].time;
assert(total_anim_time > 0.0f);
time = fmodf(time, total_anim_time);
for(MD_u64 i = 0; i < bone->anim_poses_length - 1; i++)
{
if(bone->anim_poses[i].time <= time && time <= bone->anim_poses[i + 1].time)
{
float gap_btwn_keyframes = bone->anim_poses[i + 1].time - bone->anim_poses[i].time;
float t = (time - bone->anim_poses[i].time)/gap_btwn_keyframes;
assert(t >= 0.0f);
assert(t <= 1.0f);
return bone->anim_poses[i].parent_space_pose;
}
}
assert(false);
return M4D(1.0f);
}
void draw_armature(Mat4 view, Mat4 projection, Transform t, Armature *armature, float elapsed_time)
{ {
state.threedee_bind.vertex_buffers[0] = armature->loaded_buffer; state.threedee_bind.vertex_buffers[0] = armature->loaded_buffer;
sg_apply_pipeline(state.armature_pip); sg_apply_pipeline(state.armature_pip);
@ -2662,7 +2695,8 @@ void draw_armature(Mat4 view, Mat4 projection, Transform t, Armature *armature)
final = MulM4(cur->inverse_model_space_pos, final); final = MulM4(cur->inverse_model_space_pos, final);
for(Bone *cur_in_hierarchy = cur; cur_in_hierarchy; cur_in_hierarchy = cur_in_hierarchy->parent) for(Bone *cur_in_hierarchy = cur; cur_in_hierarchy; cur_in_hierarchy = cur_in_hierarchy->parent)
{ {
final = MulM4(cur_in_hierarchy->pose_bone.parent_space_pose, final); //final = MulM4(cur_in_hierarchy->anim_poses[0].parent_space_pose, final);
final = MulM4(get_transform_along_time(cur_in_hierarchy, elapsed_time), final);
} }
memcpy(params.bones[i], (float*)&final, sizeof(final)); memcpy(params.bones[i], (float*)&final, sizeof(final));
@ -4708,7 +4742,7 @@ void frame(void)
// debug draw armature // debug draw armature
for(MD_u64 i = 0; i < armature.bones_length; i++) for(MD_u64 i = 0; i < armature.bones_length; i++)
{ {
PoseBone *cur_pose_bone = &armature.bones[i].pose_bone; PoseBone *cur_pose_bone = &armature.bones[i].anim_poses[0];
Bone *cur = &armature.bones[i]; Bone *cur = &armature.bones[i];
Vec3 offset = V3(1.5, 0, 5); Vec3 offset = V3(1.5, 0, 5);
@ -4749,7 +4783,8 @@ void frame(void)
final_mat = MulM4(cur->inverse_model_space_pos, final_mat); final_mat = MulM4(cur->inverse_model_space_pos, final_mat);
for(Bone *cur_in_hierarchy = cur; cur_in_hierarchy; cur_in_hierarchy = cur_in_hierarchy->parent) for(Bone *cur_in_hierarchy = cur; cur_in_hierarchy; cur_in_hierarchy = cur_in_hierarchy->parent)
{ {
final_mat = MulM4(cur_in_hierarchy->pose_bone.parent_space_pose, final_mat); final_mat = MulM4(get_transform_along_time(cur_in_hierarchy, (float)elapsed_time), final_mat);
//final_mat = MulM4(cur_in_hierarchy->anim_poses[0].parent_space_pose, final_mat);
} }
// uncommenting this skips the pose transform, showing the debug skeleton // uncommenting this skips the pose transform, showing the debug skeleton
@ -4798,7 +4833,7 @@ void frame(void)
Transform draw_with = entity_transform(it); Transform draw_with = entity_transform(it);
if(it->npc_kind == NPC_SimpleWorm) if(it->npc_kind == NPC_SimpleWorm)
{ {
draw_armature(view, projection, draw_with, &armature); draw_armature(view, projection, draw_with, &armature, (float)elapsed_time);
} }
else else
{ {

Loading…
Cancel
Save