Correctly load, visualize, and transform simple armature pose

main
Cameron Murphy Reikes 2 years ago
parent 5da994257b
commit 90cd549acc

@ -25,6 +25,9 @@ def write_f32(f, number: float):
def write_u64(f, number: int): def write_u64(f, number: int):
f.write(bytes(struct.pack("Q", number))) f.write(bytes(struct.pack("Q", number)))
def write_i32(f, number: int):
f.write(bytes(struct.pack("i", number)))
def write_string(f, s: str): def write_string(f, s: str):
encoded = s.encode("utf8") encoded = s.encode("utf8")
write_u64(f, len(encoded)) write_u64(f, len(encoded))
@ -56,12 +59,47 @@ for o in D.objects:
output_filepath = bpy.path.abspath(f"//{EXPORT_DIRECTORY}/{armature_name}.bin") output_filepath = bpy.path.abspath(f"//{EXPORT_DIRECTORY}/{armature_name}.bin")
print(f"Exporting armature to {output_filepath}") print(f"Exporting armature to {output_filepath}")
with open(output_filepath, "wb") as f: with open(output_filepath, "wb") as f:
write_u64(f, len(o.data.bones)) bones_in_armature = []
for b in o.data.bones: for b in o.data.bones:
print(mapping) bones_in_armature.append(b)
# the inverse model space pos of the bones
write_u64(f, len(bones_in_armature))
for b in bones_in_armature:
model_space_pose = mapping @ b.matrix_local
print(b.name)
print(b.matrix_local) print(b.matrix_local)
in_game_coordinate_system = mapping @ b.matrix_local inverse_model_space_pose = (mapping @ b.matrix_local).inverted()
write_4x4matrix(f, in_game_coordinate_system)
write_4x4matrix(f, model_space_pose)
write_4x4matrix(f, inverse_model_space_pose)
write_f32(f, b.length)
# write the pose information
write_u64(f, len(o.pose.bones))
for pose_bone in o.pose.bones:
parent_index = -1
if pose_bone.parent:
for i in range(len(bones_in_armature)):
if bones_in_armature[i] == pose_bone.parent.bone:
parent_index = i
break
if parent_index == -1:
assert(false, f"Couldn't find parent of bone {pose_bone}")
print(f"Parent of bone {pose_bone.name} is index {parent_index} in list {bones_in_armature}")
parent_space_pose = None
if pose_bone.parent:
parent_space_pose = pose_bone.parent.matrix.inverted() @ pose_bone.matrix
else:
parent_space_pose = mapping @ pose_bone.matrix
print("parent_space_pose of the bone with no parent:")
print(parent_space_pose)
write_string(f, pose_bone.bone.name)
write_i32(f, parent_index)
#parent_space_pose = mapping @ pose_bone.matrix
write_4x4matrix(f, parent_space_pose)
elif o.type == "MESH": elif o.type == "MESH":

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

Binary file not shown.

@ -180,6 +180,11 @@ CharacterGen characters[] = {
.enum_name = "Invalid", .enum_name = "Invalid",
.prompt = "There has been an internal error.", .prompt = "There has been an internal error.",
}, },
{
.name = "SimpleWorm",
.enum_name = "SimpleWorm",
.prompt = "There has been an internal error.",
},
{ {
.name = "Player", .name = "Player",
.enum_name = "Player", .enum_name = "Player",

195
main.c

@ -660,6 +660,10 @@ Vec2 entity_aabb_size(Entity *e)
{ {
return V2(1.0f*2.0f, 1.0f*2.0f); return V2(1.0f*2.0f, 1.0f*2.0f);
} }
else if (e->npc_kind == NPC_SimpleWorm)
{
return V2(1.0f, 1.0f);
}
else else
{ {
assert(false); assert(false);
@ -815,6 +819,7 @@ typedef struct Bone
{ {
struct Bone *next; struct Bone *next;
Mat4 matrix_local; Mat4 matrix_local;
Mat4 inverse_model_space_pos;
float length; float length;
} Bone; } Bone;
@ -907,7 +912,21 @@ Mat4 blender_to_handmade_mat(BlenderMat b)
return TransposeM4(to_return); return TransposeM4(to_return);
} }
Bone *load_armature(MD_Arena *arena, MD_String8 binary_file, MD_String8 armature_name) typedef struct PoseBone
{
struct PoseBone *next;
struct PoseBone *parent;
Mat4 parent_space_pose;
MD_String8 name;
} PoseBone;
typedef struct
{
Bone *bone_list;
PoseBone *pose_bone_list;
} Armature;
Armature load_armature(MD_Arena *arena, MD_String8 binary_file, MD_String8 armature_name)
{ {
MD_ArenaTemp scratch = MD_GetScratch(&arena, 1); MD_ArenaTemp scratch = MD_GetScratch(&arena, 1);
SerState ser = { SerState ser = {
@ -917,7 +936,7 @@ Bone *load_armature(MD_Arena *arena, MD_String8 binary_file, MD_String8 armature
.error_arena = scratch.arena, .error_arena = scratch.arena,
.serializing = false, .serializing = false,
}; };
Bone *to_return = 0; Armature to_return = {0};
MD_u64 num_bones; MD_u64 num_bones;
ser_MD_u64(&ser, &num_bones); ser_MD_u64(&ser, &num_bones);
@ -927,15 +946,94 @@ Bone *load_armature(MD_Arena *arena, MD_String8 binary_file, MD_String8 armature
{ {
Bone *next_bone = MD_PushArray(arena, Bone, 1); Bone *next_bone = MD_PushArray(arena, Bone, 1);
BlenderMat b; BlenderMat model_space_pose;
ser_BlenderMat(&ser, &b); BlenderMat inverse_model_space_pose;
next_bone->matrix_local = blender_to_handmade_mat(b);
ser_BlenderMat(&ser, &model_space_pose);
ser_BlenderMat(&ser, &inverse_model_space_pose);
ser_float(&ser, &next_bone->length); ser_float(&ser, &next_bone->length);
MD_StackPush(to_return, next_bone);
next_bone->matrix_local = blender_to_handmade_mat(model_space_pose);
next_bone->inverse_model_space_pos = blender_to_handmade_mat(inverse_model_space_pose);
MD_StackPush(to_return.bone_list, next_bone);
} }
MD_u64 num_pose_bones;
ser_MD_u64(&ser, &num_pose_bones);
assert(num_pose_bones == num_bones);
MD_i32 *parent_indices = MD_PushArray(scratch.arena, MD_i32, num_pose_bones);
for(MD_u64 i = 0; i < num_bones; i++)
parent_indices[i] = -1;
for(MD_u64 i = 0; i < num_pose_bones; i++)
{
PoseBone *next_pose_bone = MD_PushArray(arena, PoseBone, 1);
BlenderMat parent_space_pose;
ser_MD_String8(&ser, &next_pose_bone->name, arena);
ser_int(&ser, &parent_indices[i]);
ser_BlenderMat(&ser, &parent_space_pose);
next_pose_bone->parent_space_pose = blender_to_handmade_mat(parent_space_pose);
next_pose_bone->parent = 0;
MD_StackPush(to_return.pose_bone_list, next_pose_bone);
}
PoseBone *to_attach_parent_to = to_return.pose_bone_list;
// i goes backwards here, because in the pose_bone_list bones are in reverse order
// compared to how they're serialized, because MD_StackPush causes the list to be in
// reverse order! Each successive element is prepended to the beginning
for(int i = (int)num_pose_bones - 1; i >= 0; i--)
{
assert(to_attach_parent_to);
MD_i32 target_index = parent_indices[i];
if(target_index != -1)
{
int i = (int)num_pose_bones - 1;
for(PoseBone *cur = to_return.pose_bone_list; cur; cur = cur->next)
{
if(i == target_index)
{
to_attach_parent_to->parent = cur;
break;
}
i--;
}
assert(to_attach_parent_to->parent);
}
to_attach_parent_to = to_attach_parent_to->next;
}
assert(!ser.cur_error.failed); assert(!ser.cur_error.failed);
MD_ReleaseScratch(scratch); MD_ReleaseScratch(scratch);
// a sanity check
for(Bone *cur = to_return.bone_list; cur; cur = cur->next)
{
Mat4 should_be_identity = MulM4(cur->matrix_local, cur->inverse_model_space_pos);
for(int r = 0; r < 4; r++)
{
for(int c = 0; c < 4; c++)
{
if(r == c)
{
assert(should_be_identity.Elements[c][r] == 1.0f);
}
else
{
assert(should_be_identity.Elements[c][r] == 0.0f);
}
}
}
}
return to_return; return to_return;
} }
@ -1079,16 +1177,16 @@ ThreeDeeLevel load_level(MD_Arena *arena, MD_String8 binary_file)
new_placed->npc_kind = kind; new_placed->npc_kind = kind;
} }
} }
if(found)
{
BlenderTransform blender_transform = {0}; BlenderTransform blender_transform = {0};
ser_BlenderTransform(&ser, &blender_transform); ser_BlenderTransform(&ser, &blender_transform);
if(found)
{
new_placed->t = blender_to_game_transform(blender_transform); new_placed->t = blender_to_game_transform(blender_transform);
MD_StackPush(out.placed_entity_list, new_placed); MD_StackPush(out.placed_entity_list, new_placed);
} }
else else
{ {
Log("Couldn't find placed npc kind '%.*s'...\n", MD_S8VArg(placed_entity_name)); ser.cur_error = (SerError){.failed = true, .why = MD_S8Fmt(arena, "Couldn't find placed npc kind '%.*s'...\n", MD_S8VArg(placed_entity_name))};
} }
} }
} }
@ -2534,6 +2632,7 @@ void audio_stream_callback(float *buffer, int num_frames, int num_channels)
#define LIGHTBLUE ((Color) { 0.2f, 0.2f, 0.8f, 1.0f }) #define LIGHTBLUE ((Color) { 0.2f, 0.2f, 0.8f, 1.0f })
#define GREEN ((Color) { 0.0f, 1.0f, 0.0f, 1.0f }) #define GREEN ((Color) { 0.0f, 1.0f, 0.0f, 1.0f })
#define BROWN (colhex(0x4d3d25)) #define BROWN (colhex(0x4d3d25))
#define YELLOW (colhex(0xffdd00))
Color oflightness(float dark) Color oflightness(float dark)
{ {
@ -2673,8 +2772,9 @@ void do_serialization_tests()
} }
#endif #endif
Bone *bones = 0; Armature armature = {0};
Mesh mesh_player = {0}; Mesh mesh_player = {0};
Mesh mesh_simple_worm = {0};
void stbi_flip_into_correct_direction(bool do_it) void stbi_flip_into_correct_direction(bool do_it)
{ {
@ -2723,8 +2823,15 @@ void init(void)
MD_String8 binary_file = MD_LoadEntireFile(frame_arena, MD_S8Lit("assets/exported_3d/Player.bin")); MD_String8 binary_file = MD_LoadEntireFile(frame_arena, MD_S8Lit("assets/exported_3d/Player.bin"));
mesh_player = load_mesh(persistent_arena, binary_file, MD_S8Lit("Player.bin")); mesh_player = load_mesh(persistent_arena, binary_file, MD_S8Lit("Player.bin"));
binary_file = MD_LoadEntireFile(frame_arena, MD_S8Lit("assets/exported_3d/SimpleWorm.bin"));
mesh_simple_worm = load_mesh(persistent_arena, binary_file, MD_S8Lit("SimpleWorm.bin"));
binary_file = MD_LoadEntireFile(frame_arena, MD_S8Lit("assets/exported_3d/Armature.bin")); binary_file = MD_LoadEntireFile(frame_arena, MD_S8Lit("assets/exported_3d/Armature.bin"));
bones = load_armature(persistent_arena, binary_file, MD_S8Lit("Player.bin")); armature = load_armature(persistent_arena, binary_file, MD_S8Lit("Player.bin"));
MD_ArenaClear(frame_arena); MD_ArenaClear(frame_arena);
binary_file = MD_LoadEntireFile(frame_arena, MD_S8Lit("assets/exported_3d/level.bin")); binary_file = MD_LoadEntireFile(frame_arena, MD_S8Lit("assets/exported_3d/level.bin"));
@ -4368,7 +4475,7 @@ Transform entity_transform(Entity *e)
// the mods to e->rotation here are just chosen based on what looks right with model // the mods to e->rotation here are just chosen based on what looks right with model
// facing forward towards // facing forward towards
Quat entity_rot = QFromAxisAngle_RH(V3(0,1,0), AngleRad(-e->rotation - PI32/2.0f)); Quat entity_rot = QFromAxisAngle_RH(V3(0,1,0), AngleRad(-e->rotation - PI32/2.0f));
dbgplaneline(e->pos, AddV2(e->pos, RotateV2(V2(5.0f, 0.0), e->rotation))); //dbgplaneline(e->pos, AddV2(e->pos, RotateV2(V2(5.0f, 0.0), e->rotation)));
return (Transform){.offset = AddV3(plane_point(e->pos), V3(0,0,0)), .rotation = entity_rot, .scale = V3(1, 1, 1)}; return (Transform){.offset = AddV3(plane_point(e->pos), V3(0,0,0)), .rotation = entity_rot, .scale = V3(1, 1, 1)};
/* /*
@ -4482,18 +4589,63 @@ void frame(void)
} }
projection = Perspective_RH_NO(PI32/4.0f, screen_size().x / screen_size().y, 0.01f, 1000.0f); projection = Perspective_RH_NO(PI32/4.0f, screen_size().x / screen_size().y, 0.01f, 1000.0f);
for(Bone *cur = bones; cur; cur = cur->next) PoseBone *cur_pose_bone = armature.pose_bone_list;
for(Bone *cur = armature.bone_list; cur; cur = cur->next)
{ {
Vec3 offset = V3(5, 0, 5); Vec3 offset = V3(1.5, 0, 5);
Vec3 from = MulM4V3(cur->matrix_local, V3(0,0,0)); Vec3 from = MulM4V3(cur->matrix_local, V3(0,0,0));
Vec3 to = MulM4V3(cur->matrix_local, V3(0,cur->length,0)); Vec3 x = MulM4V3(cur->matrix_local, V3(cur->length,0,0));
Vec3 y = MulM4V3(cur->matrix_local, V3(0,cur->length,0));
Vec3 z = MulM4V3(cur->matrix_local, V3(0,0,cur->length));
Vec3 should_be_zero = MulM4V3(cur->inverse_model_space_pos, from);
assert(should_be_zero.x == 0.0);
assert(should_be_zero.y == 0.0);
assert(should_be_zero.z == 0.0);
if(cur_pose_bone->parent == 0)
{
// do some testing on the bone with no parent
Vec3 should_be_zero = MulM4V3(cur_pose_bone->parent_space_pose, V3(0,0,0));
assert(should_be_zero.x == 0.0);
assert(should_be_zero.y == 0.0);
assert(should_be_zero.z == 0.0);
}
// from, x, y, and z are like vertex points. They are model-space
// points *around* the bones they should be influenced by. Now we
// need to transform them according to how much the pose bones
// have moved and in the way they moved.
Mat4 final_mat = M4D(1.0f);
final_mat = MulM4(cur->inverse_model_space_pos, final_mat);
for(PoseBone *cur = cur_pose_bone; cur; cur = cur->parent)
{
final_mat = MulM4(cur->parent_space_pose, final_mat);
}
from = MulM4V3(final_mat, from);
x = MulM4V3(final_mat, x);
y = MulM4V3(final_mat, y);
z = MulM4V3(final_mat, z);
from = AddV3(from, offset); from = AddV3(from, offset);
to = AddV3(to, offset); x = AddV3(x, offset);
y = AddV3(y, offset);
z = AddV3(z, offset);
dbgcol(LIGHTBLUE) dbgcol(LIGHTBLUE)
dbgsquare3d(to); dbgsquare3d(y);
dbgcol(RED)
dbg3dline(from, x);
dbgcol(BLUE) dbgcol(BLUE)
dbg3dline(from, to); dbg3dline(from, y);
dbgcol(YELLOW)
dbg3dline(from, z);
cur_pose_bone = cur_pose_bone->next;
} }
@ -4508,9 +4660,16 @@ void frame(void)
if(it->is_npc || it->is_character) if(it->is_npc || it->is_character)
{ {
Transform draw_with = entity_transform(it); Transform draw_with = entity_transform(it);
if(it->npc_kind == NPC_SimpleWorm)
{
draw_placed(view, projection, &(PlacedMesh){.draw_with = &mesh_simple_worm, .t = draw_with, });
}
else
{
draw_placed(view, projection, &(PlacedMesh){.draw_with = &mesh_player, .t = draw_with, }); draw_placed(view, projection, &(PlacedMesh){.draw_with = &mesh_player, .t = draw_with, });
} }
} }
}
sg_end_pass(); sg_end_pass();

Loading…
Cancel
Save