From c9b6ce0c3edca5ff574b1d82123e1f0a388b336c Mon Sep 17 00:00:00 2001 From: Cameron Reikes Date: Thu, 6 Jul 2023 17:04:00 -0700 Subject: [PATCH] Meshes refer to exported images, image caching system (only load path once) --- art/Exporter.py | 34 ++++++++++++++++++++++++++++++++-- codegen.c | 2 +- main.c | 37 ++++++++++++++++++++++++++++++++----- 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/art/Exporter.py b/art/Exporter.py index e8b2510..a34f6c5 100644 --- a/art/Exporter.py +++ b/art/Exporter.py @@ -80,6 +80,32 @@ with open(bpy.path.abspath(f"//{EXPORT_DIRECTORY}/shorttest.bin"), "wb") as f: for i in range(4): write_u16(f, i) +saved_images = set() +def ensure_tex_saved_and_get_name(o) -> str: + """returns the path to the mesh's texture's png in the exported directory""" + mesh_name = o.to_mesh().name + img_obj = None + assert len(o.material_slots) == 1, f"Don't know which material slot to pick from in mesh {mesh_name} object {o.name}" + mat = o.material_slots[0] + for node in mat.material.node_tree.nodes: + if node.type == "TEX_IMAGE": + img_obj = node.image + break + assert img_obj, f"Mesh {mesh_name} in its material doesn't have an image object" + image_filename = f"{img_obj.name}.png" + if image_filename in saved_images: + pass + else: + save_to = f"//{EXPORT_DIRECTORY}/{image_filename}" + print(f"Saving image {image_filename} to {bpy.path.abspath((save_to))}...") + if img_obj.packed_file: + img_obj.save(filepath=bpy.path.abspath(save_to)) + else: + assert img_obj.filepath != "", f"{img_obj.filepath} in mesh {mesh_name} Isn't there but should be, as it has no packed image" + shutil.copyfile(bpy.path.abspath(img_obj.filepath),bpy.path.abspath(save_to)) + + return image_filename + # meshes can either be Meshes, or Armatures. Armatures contain all mesh data to draw it, and any anims it has for o in D.objects: @@ -235,13 +261,14 @@ for o in D.objects: else: # if the parent type isn't an armature, i.e just a bog standard 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}") + print(f"Object {o.name} has mesh name {o.to_mesh().name} and image file {image_filename}") assert(o.rotation_euler.order == 'XYZ') level_object_data.append(object_transform_info) else: @@ -250,12 +277,15 @@ for o in D.objects: if mesh_name in saved_meshes: continue saved_meshes.add(mesh_name) + image_filename = ensure_tex_saved_and_get_name(o) 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: - write_b8(f, False) + write_b8(f, False) # if it's an armature or not, first byte of the file + write_string(f, image_filename) # the image filename! + bm = bmesh.new() mesh = o.to_mesh() bm.from_mesh(mesh) diff --git a/codegen.c b/codegen.c index 0d5e8db..b194296 100644 --- a/codegen.c +++ b/codegen.c @@ -194,7 +194,7 @@ int main(int argc, char **argv) fclose(asset_file); MD_S8ListPush(cg_arena, &declarations_list, MD_S8Fmt(cg_arena, "sg_image %.*s = {0};\n", MD_S8VArg(variable_name))); - MD_S8ListPush(cg_arena, &load_list, MD_S8Fmt(cg_arena, "%.*s = load_image(\"%.*s\");\n", MD_S8VArg(variable_name), MD_S8VArg(filepath))); + MD_S8ListPush(cg_arena, &load_list, MD_S8Fmt(cg_arena, "%.*s = load_image(MD_S8Lit(\"%.*s\"));\n", MD_S8VArg(variable_name), MD_S8VArg(filepath))); } } diff --git a/main.c b/main.c index 89bf046..69f97ce 100644 --- a/main.c +++ b/main.c @@ -750,18 +750,39 @@ AABB entity_aabb(Entity *e) return entity_aabb_at(e, at); } -sg_image load_image(const char *path) +typedef struct LoadedImage { + struct LoadedImage *next; + MD_String8 name; + sg_image image; +} LoadedImage; + +LoadedImage *loaded_images = 0; + +sg_image load_image(MD_String8 path) +{ + for(LoadedImage *cur = loaded_images; cur; cur = cur->next) + { + if(MD_S8Match(cur->name, path, 0)) + { + return cur->image; + } + } + + LoadedImage *loaded = MD_PushArray(persistent_arena, LoadedImage, 1); + loaded->name = MD_S8Copy(persistent_arena, path); + MD_StackPush(loaded_images, loaded); + sg_image to_return = { 0 }; int png_width, png_height, num_channels; const int desired_channels = 4; stbi_uc* pixels = stbi_load( - path, + (const char*)nullterm(frame_arena, path).str, &png_width, &png_height, &num_channels, 0); assert(pixels); - Log("Pah %s | Loading image with dimensions %d %d\n", path, png_width, png_height); + Log("Path %.*s | Loading image with dimensions %d %d\n", MD_S8VArg(path), png_width, png_height); to_return = sg_make_image(&(sg_image_desc) { .width = png_width, @@ -779,6 +800,7 @@ sg_image load_image(const char *path) } }); stbi_image_free(pixels); + loaded->image = to_return; return to_return; } @@ -839,6 +861,7 @@ typedef struct Mesh MD_u64 num_vertices; sg_buffer loaded_buffer; + sg_image mesh_image; MD_String8 name; } Mesh; @@ -910,8 +933,12 @@ Mesh load_mesh(MD_Arena *arena, MD_String8 binary_file, MD_String8 mesh_name) ser_bool(&ser, &is_armature); assert(!is_armature); + MD_String8 image_filename; + ser_MD_String8(&ser, &image_filename, scratch.arena); + out.mesh_image = load_image(MD_S8Fmt(scratch.arena, "assets/exported_3d/%.*s", MD_S8VArg(image_filename))); + ser_MD_u64(&ser, &out.num_vertices); - Log("Mesh %.*s has %llu vertices\n", MD_S8VArg(mesh_name), out.num_vertices); + Log("Mesh %.*s has %llu vertices and image filename '%.*s'\n", MD_S8VArg(mesh_name), out.num_vertices, MD_S8VArg(image_filename)); out.vertices = MD_ArenaPush(arena, sizeof(*out.vertices) * out.num_vertices); for(MD_u64 i = 0; i < out.num_vertices; i++) @@ -2767,7 +2794,7 @@ void draw_placed(Mat4 view, Mat4 projection, Mat4 light_matrix, PlacedMesh *cur) { *it = (sg_image){0}; } - state.threedee_bind.fs_images[SLOT_threedee_tex] = image_gigatexture; + state.threedee_bind.fs_images[SLOT_threedee_tex] = drawing->mesh_image; state.threedee_bind.fs_images[SLOT_threedee_shadow_map] = state.shadows.color_img; state.threedee_bind.vertex_buffers[0] = drawing->loaded_buffer; sg_apply_bindings(&state.threedee_bind);