From 76d209f0b4dbd9763da928bffda691c55bed75ad Mon Sep 17 00:00:00 2001 From: Cameron Reikes Date: Thu, 29 Jun 2023 21:37:05 -0700 Subject: [PATCH] Export armature as bones, import and debug draw in 3d. Flipped coord systems right now. --- art/Exporter.py | 126 ++++++++++++++++++++++++++++-------------------- art/art.blend | 4 +- main.c | 84 ++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 55 deletions(-) diff --git a/art/Exporter.py b/art/Exporter.py index 78b69ef..0d082f6 100644 --- a/art/Exporter.py +++ b/art/Exporter.py @@ -30,6 +30,11 @@ def write_string(f, s: str): write_u64(f, len(encoded)) f.write(encoded) +def write_4x4matrix(f, m): + # writes each row, sequentially, row major + for row in range(4): + for col in range(4): + write_f32(f, m[row][col]) # for the level.bin level_object_data = [] @@ -37,64 +42,78 @@ collision_cubes = [] placed_entities = [] saved_meshes = set() +mapping = axis_conversion( + from_forward = "Y", + from_up = "Z", + to_forward = "-Z", + to_up = "Y", +) + for o in D.objects: - mapping = axis_conversion( - from_forward = "Y", - from_up = "Z", - to_forward = "-Z", - to_up = "Y", - ) - mesh_name = o.to_mesh().name # use this over o.name so instanced objects which refer to the same mesh, both use the same serialized mesh. - - object_transform_info = (mesh_name, mapping @ o.location, o.rotation_euler, o.scale) - - if o.users_collection[0].name == 'Level' and mesh_name == "CollisionCube": - collision_cubes.append((o.location, o.dimensions)) - else: - if o.users_collection[0].name == 'Level': - print(f"Object {o.name} has mesh name {o.to_mesh().name}") - assert(o.rotation_euler.order == 'XYZ') - level_object_data.append(object_transform_info) + if o.type == "ARMATURE": + armature_name = o.data.name + output_filepath = bpy.path.abspath(f"//{EXPORT_DIRECTORY}/{armature_name}.bin") + print(f"Exporting armature to {output_filepath}") + with open(output_filepath, "wb") as f: + write_u64(f, len(o.data.bones)) + for b in o.data.bones: + in_game_coordinate_system = mapping @ b.matrix_local + write_4x4matrix(f, b.matrix_local) + + elif o.type == "MESH": + + mesh_name = o.to_mesh().name # use this over o.name so instanced objects which refer to the same mesh, both use the same serialized mesh. + + object_transform_info = (mesh_name, mapping @ o.location, o.rotation_euler, o.scale) + + if o.users_collection[0].name == 'Level' and mesh_name == "CollisionCube": + collision_cubes.append((o.location, o.dimensions)) + else: + if o.users_collection[0].name == 'Level': + print(f"Object {o.name} has mesh name {o.to_mesh().name}") + assert(o.rotation_euler.order == 'XYZ') + level_object_data.append(object_transform_info) + else: + placed_entities.append((o.name,) + object_transform_info) + if mesh_name in saved_meshes: continue saved_meshes.add(mesh_name) - else: - placed_entities.append((o.name,) + object_transform_info) - - mapping.resize_4x4() - assert(mesh_name != LEVEL_EXPORT_NAME) - output_filepath = bpy.path.abspath(f"//{EXPORT_DIRECTORY}/{mesh_name}.bin") - print(f"Exporting {output_filepath}") - with open(output_filepath, "wb") as f: - bm = bmesh.new() - mesh = o.to_mesh() - bm.from_mesh(mesh) - bmesh.ops.triangulate(bm, faces=bm.faces) - bm.transform(mapping) - bm.to_mesh(mesh) - - vertices = [] + mapping.resize_4x4() + assert(mesh_name != LEVEL_EXPORT_NAME) + output_filepath = bpy.path.abspath(f"//{EXPORT_DIRECTORY}/{mesh_name}.bin") + print(f"Exporting mesh to {output_filepath}") + with open(output_filepath, "wb") as f: + bm = bmesh.new() + mesh = o.to_mesh() + bm.from_mesh(mesh) + bmesh.ops.triangulate(bm, faces=bm.faces) + bm.transform(mapping) + bm.to_mesh(mesh) + + + vertices = [] - for polygon in mesh.polygons: - if len(polygon.loop_indices) == 3: - for loopIndex in polygon.loop_indices: - loop = mesh.loops[loopIndex] - position = mesh.vertices[loop.vertex_index].undeformed_co - uv = mesh.uv_layers.active.data[loop.index].uv - normal = loop.normal - - vertices.append((position, uv)) - - write_u64(f, len(vertices)) - for v_and_uv in vertices: - v, uv = v_and_uv - write_f32(f, v.x) - write_f32(f, v.y) - write_f32(f, v.z) - write_f32(f, uv.x) - write_f32(f, uv.y) - print(f"Wrote {len(vertices)} vertices") + for polygon in mesh.polygons: + if len(polygon.loop_indices) == 3: + for loopIndex in polygon.loop_indices: + loop = mesh.loops[loopIndex] + position = mesh.vertices[loop.vertex_index].undeformed_co + uv = mesh.uv_layers.active.data[loop.index].uv + normal = loop.normal + + vertices.append((position, uv)) + + write_u64(f, len(vertices)) + for v_and_uv in vertices: + v, uv = v_and_uv + write_f32(f, v.x) + write_f32(f, v.y) + write_f32(f, v.z) + write_f32(f, uv.x) + write_f32(f, uv.y) + print(f"Wrote {len(vertices)} vertices") with open(bpy.path.abspath(f"//{EXPORT_DIRECTORY}/{LEVEL_EXPORT_NAME}.bin"), "wb") as f: write_u64(f, len(level_object_data)) @@ -140,4 +159,5 @@ with open(bpy.path.abspath(f"//{EXPORT_DIRECTORY}/{LEVEL_EXPORT_NAME}.bin"), "wb write_f32(f, blender_scale.x) write_f32(f, blender_scale.y) - write_f32(f, blender_scale.z) \ No newline at end of file + write_f32(f, blender_scale.z) + \ No newline at end of file diff --git a/art/art.blend b/art/art.blend index 15465f3..aacaa2f 100644 --- a/art/art.blend +++ b/art/art.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ca4275f54627a7db3e9ec3773895822ab61cfc0632f2fea917ad6de095d53e7 -size 7248900 +oid sha256:b6d5d6ea3b9a9a46fdb4b980c586e932283d661712da713a22d083816c7cee30 +size 7169720 diff --git a/main.c b/main.c index 0cb609f..ac29c42 100644 --- a/main.c +++ b/main.c @@ -811,6 +811,12 @@ typedef struct Mesh MD_String8 name; } Mesh; +typedef struct Bone +{ + struct Bone *next; + Mat4 matrix_local; +} Bone; + typedef struct { Vec3 offset; @@ -879,6 +885,59 @@ Mesh load_mesh(MD_Arena *arena, MD_String8 binary_file, MD_String8 mesh_name) return out; } +// stored in row major +typedef struct +{ + float elems[4 * 4]; +} BlenderMat; + +void ser_BlenderMat(SerState *ser, BlenderMat *b) +{ + for(int i = 0; i < 4 * 4; i++) + { + ser_float(ser, &b->elems[i]); + } +} +Mat4 blender_to_handmade_mat(BlenderMat b) +{ + Mat4 to_return; + assert(sizeof(to_return) == sizeof(b)); + memcpy(&to_return, &b, sizeof(to_return)); + return TransposeM4(to_return); +} + +Bone *load_armature(MD_Arena *arena, MD_String8 binary_file, MD_String8 armature_name) +{ + MD_ArenaTemp scratch = MD_GetScratch(&arena, 1); + SerState ser = { + .data = binary_file.str, + .max = binary_file.size, + .arena = arena, + .error_arena = scratch.arena, + .serializing = false, + }; + Bone *to_return = 0; + + MD_u64 num_bones; + ser_MD_u64(&ser, &num_bones); + Log("Armature %.*s has %llu vertices\n", MD_S8VArg(armature_name), num_bones); + + for(MD_u64 i = 0; i < num_bones; i++) + { + Bone *next_bone = MD_PushArray(arena, Bone, 1); + + BlenderMat b; + ser_BlenderMat(&ser, &b); + next_bone->matrix_local = blender_to_handmade_mat(b); + + MD_StackPush(to_return, next_bone); + } + assert(!ser.cur_error.failed); + MD_ReleaseScratch(scratch); + + return to_return; +} + typedef struct CollisionCube { struct CollisionCube *next; @@ -900,6 +959,7 @@ void ser_BlenderTransform(SerState *ser, BlenderTransform *t) ser_Vec3(ser, &t->scale); } + Transform blender_to_game_transform(BlenderTransform blender_transform) { Transform to_return = {0}; @@ -1058,6 +1118,11 @@ Vec4 IsPoint(Vec3 point) return V4(point.x, point.y, point.z, 1.0f); } +Vec3 MulM4V3(Mat4 m, Vec3 v) +{ + return MulM4V4(m, IsPoint(v)).xyz; +} + typedef struct { Vec3 right; // X+ @@ -2606,6 +2671,7 @@ void do_serialization_tests() } #endif +Bone *bones = 0; Mesh mesh_player = {0}; void stbi_flip_into_correct_direction(bool do_it) @@ -2655,6 +2721,8 @@ void init(void) 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")); + binary_file = MD_LoadEntireFile(frame_arena, MD_S8Lit("assets/exported_3d/Armature.bin")); + bones = load_armature(persistent_arena, binary_file, MD_S8Lit("Player.bin")); MD_ArenaClear(frame_arena); binary_file = MD_LoadEntireFile(frame_arena, MD_S8Lit("assets/exported_3d/level.bin")); @@ -4406,6 +4474,22 @@ void frame(void) } 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) + { + Vec3 offset = V3(5, 0, 5); + if(cur->next) + { + Vec3 from = MulM4V3(cur->matrix_local, V3(0,0,0)); + Vec3 to = MulM4V3(cur->next->matrix_local, V3(0,0,0)); + + from = AddV3(from, offset); + to = AddV3(to, offset); + dbgcol(BLUE) + dbg3dline(from, to); + } + } + + for(PlacedMesh *cur = level_threedee.placed_mesh_list; cur; cur = cur->next) { draw_placed(view, projection, cur);