#define STB_IMAGE_IMPLEMENTATION #include "lib/stb_image.h" #include #include "util/file.h" #include "util/err.h" #include "resource.h" #include "cube.h" static void image_copy(unsigned int dst_size[2], rgba_data dst, unsigned int src_size[2], const rgba_data src, unsigned int off[2]) { assert(off[0] + dst_size[0] <= src_size[0]); assert(off[1] + dst_size[1] <= src_size[1]); for (unsigned int y = 0; y < dst_size[1]; y++) memcpy( (char *)dst + dst_size[0]*y * 4, (const char *)src + ((off[1] + y)*src_size[0] + off[0]) * 4, dst_size[0]*4); } static texture_id create_texture(struct texture_source *tex, struct source *src, struct draw_backend *draw) { FILE *f = file_open(tex->path.str, "rb"); if (!f) source_error(src, tex->path.loc, "%.*s: failed to open file", PSTR(tex->path.str)); int width, height; void *img_data = stbi_load_from_file(f, &width, &height, nullptr, 4); if (!img_data) source_error(src, tex->path.loc, "%.*s: failed to load image", PSTR(tex->path.str)); unsigned int img_size[2] = { width, height }; texture_id id; switch (tex->type) { case TEXTURE_2D: if (!draw->create_texture(tex->type, img_size, &img_data, &id)) source_error(src, tex->path.loc, "%.*s: failed to create texture", PSTR(tex->path.str)); break; case TEXTURE_CUBEMAP: { if (img_size[0] % 4) source_error(src, tex->path.loc, "%.*s: cubemap image width is not divisible by 4", PSTR(tex->path.str)); if (img_size[1] % 3) source_error(src, tex->path.loc, "%.*s: cubemap image height is not divisible by 3", PSTR(tex->path.str)); unsigned int face_size[2] = { img_size[0] / 4, img_size[1] / 3 }; static const unsigned int coords[6][2] = { // right, left, top, bottom, front, back { 2, 1 }, { 0, 1 }, { 1, 0 }, { 1, 2 }, { 1, 1 }, { 1, 3 }, }; rgba_data faces[6]; size_t face_len = 4 * face_size[0] * face_size[1]; void *buffer = malloc(6 * face_len); for (size_t i = 0; i < 6; i++) image_copy( face_size, faces[i] = (char *)buffer + face_len*i, img_size, img_data, (unsigned int [2]) { coords[i][0] * face_size[0], coords[i][1] * face_size[1] }); if (!draw->create_texture(tex->type, face_size, faces, &id)) source_error(src, tex->path.loc, "%.*s: failed to create texture", PSTR(tex->path.str)); free(buffer); } } stbi_image_free(img_data); return id; } static mesh_id create_mesh(struct mesh_source *mesh, struct source *src, struct draw_backend *draw) { (void) draw; source_error(src, mesh->path.loc, "TODO: obj loading"); } #define LOAD_CACHED(what, cache_field) do { \ what ## _id id, *p_id = nullptr; \ map_find(&cache->cache_field, what->source.path.str, &p_id); \ if (p_id) { \ id = *p_id; \ } else { \ id = create_ ## what(&what->source, src, draw); \ map_insert_end(&cache->cache_field, what->source.path.str, id); \ } \ what->id = id; \ } while (0) void texture_load(struct texture *texture, struct source *src, struct draw_backend *draw, struct cache *cache) { LOAD_CACHED(texture, textures[texture->source.type]); } void mesh_load(struct mesh *mesh, struct source *src, struct draw_backend *draw, struct cache *cache) { switch (mesh->source.type) { case MESH_CUBE: if (cache->cube_mesh.present) { mesh->id = cache->cube_mesh.data; } else { if (!draw->create_mesh(sizeof cube_verts / sizeof *cube_verts, cube_verts, &mesh->id)) eprintf("failed to create cube mesh\n"); cache->cube_mesh.present = true; cache->cube_mesh.data = mesh->id; } break; case MESH_OBJ: LOAD_CACHED(mesh, meshes); break; } } void cache_free(struct cache *cache) { free(cache->textures[TEXTURE_2D].ptr); free(cache->textures[TEXTURE_CUBEMAP].ptr); free(cache->meshes.ptr); }