fogd-engine/src/core/resources.c
2024-09-16 23:28:55 +02:00

179 lines
6.5 KiB
C

#include "resources.h"
#include "stdbool.h"
#include "utils/hash_map.h"
#include "utils/dictionary.h"
#include "utils/debug.h"
#include "utils/strutil.h"
char const *RESOURCE_DIRECTORY = "resources";
typedef enum ResourceType {
RESOURCE_MODEL, RESOURCE_TEXTURE,
RESOURCE_MAX
} ResourceType;
typedef struct ResourceContainer {
char const *path;
char const *name;
unsigned use_counter;
bool is_loaded;
ResourceType type;
union {
Model model;
Texture texture;
};
} ResourceContainer;
typedef void (*LoadResourceFn)(ResourceContainer *resource);
typedef void (*UnloadResourceFn)(ResourceContainer *resource);
static void Internal_LoadModelResource(ResourceContainer *resource);
static void Internal_UnloadModelResource(ResourceContainer *resource);
static void Internal_LoadTextureResource(ResourceContainer *resource);
static void Internal_UnloadTextureResource(ResourceContainer *resource);
LoadResourceFn g_load_functions[] = {Internal_LoadModelResource, Internal_LoadTextureResource};
UnloadResourceFn g_unload_functions[] = {Internal_UnloadModelResource, Internal_UnloadTextureResource};
static HashMap g_resource_map;
static FilePathList g_resource_files;
//! hash function for hashmaps with char* as key type.
static uintptr_t HashMapHashString(char const **str_ptr) {
return strhash(*str_ptr);
}
//! Use file extensions to figure out the
static ResourceType ResourceGetTypeFromPath(char const *path) {
// Texture files
if(IsFileExtension(path, ".png")) return RESOURCE_TEXTURE;
if(IsFileExtension(path, ".jpg")) return RESOURCE_TEXTURE;
if(IsFileExtension(path, ".jpeg")) return RESOURCE_TEXTURE;
// Model files
if(IsFileExtension(path, ".glb")) return RESOURCE_MODEL;
if(IsFileExtension(path, ".gltf")) return RESOURCE_MODEL;
return RESOURCE_MAX;
}
//! Find which resource directory to use: Relative to working directory, or relative to application directory
//! Working directory resources are only supposed to be used for debugging.
static inline
void Internal_InitializeResourceDirectory() {
// if there is a resource directory in the working directory, prioritize that for debugging
if(DirectoryExists(RESOURCE_DIRECTORY)) {
ChangeDirectory(TextFormat("%s/%s", GetWorkingDirectory(), RESOURCE_DIRECTORY));
LOG_WARNING("Using working dir resources, this is intended for debug purposes only.");
return;
}
// check application installation directory for a resource directory
// this is the default running environment
char const *installation_resource_dir = TextFormat("%s/%s", GetApplicationDirectory(), RESOURCE_DIRECTORY);
if(DirectoryExists(installation_resource_dir)) {
ChangeDirectory(installation_resource_dir);
return;
}
UNREACHABLE("Failed to find a valid resource directory");
}
static inline
void Internal_IndexResourceDirectory() {
g_resource_files = LoadDirectoryFilesEx(GetWorkingDirectory(), NULL, true);
g_resource_map = hash_map_from_types(char const *, ResourceContainer, HashMapHashString);
ResourceContainer placeholder;
for(size_t i = 0; i < g_resource_files.count; ++i) {
placeholder = (ResourceContainer){
.is_loaded = false,
.use_counter = 0,
.path = g_resource_files.paths[i],
.name = GetFileName(g_resource_files.paths[i]),
.type = ResourceGetTypeFromPath(g_resource_files.paths[i])
};
// only index resources that the engine knows how to load
if(placeholder.type != RESOURCE_MAX) {
LOG_INFO("Internal_IndexResourceDirectory: Indexing %s as %s", placeholder.path, placeholder.name);
hash_map_insert(&g_resource_map, &placeholder.name, &placeholder);
}
}
}
void InitializeResourceSubsystem() {
Internal_InitializeResourceDirectory();
Internal_IndexResourceDirectory();
}
void CleanResourceSubsystem() {
List resources = hash_map_values(&g_resource_map);
list_foreach(ResourceContainer *,resource, &resources)
g_unload_functions[resource->type](resource);
hash_map_empty(&g_resource_map);
UnloadDirectoryFiles(g_resource_files);
}
bool GetModelResource(char const *path, ModelResource *out) {
ResourceContainer *container = hash_map_get_as(ResourceContainer, &g_resource_map, &path);
*out = ResourceEmpty(Model);
// assert some assumptions about the found resource
ASSERT_RETURN(container != NULL, false, "GetTextureResource: Resource %s not in index.", path);
ASSERT_RETURN(container->type == RESOURCE_MODEL, false, "GetTextureResource: Resource %s is not a Texture.", path);
++container->use_counter;
*out = (ModelResource) {
.handle = container,
.resource = &container->model
};
return true;
}
bool GetTextureResource(char const *path, TextureResource *out) {
ResourceContainer *container = hash_map_get_as(ResourceContainer, &g_resource_map, &path);
*out = ResourceEmpty(Texture);
// assert some assumptions about the found resource
ASSERT_RETURN(container != NULL, false, "GetTextureResource: Resource %s not in index.", path);
ASSERT_RETURN(container->type != RESOURCE_TEXTURE, false, "GetTextureResource: Resource %s is not a Texture.", path);
*out = (TextureResource) {
.handle = container,
.resource = &container->texture
};
return true;
}
bool IsResourceLoaded(ResourceHandle handle) {
return handle->is_loaded;
}
void LoadResource(ResourceHandle handle) {
ASSERT_RETURN(handle != NULL,, "LoadResource: Resource handle invalid");
g_load_functions[handle->type](handle);
handle->is_loaded = true;
++handle->use_counter;
}
void ReleaseResource(ResourceHandle handle) {
ASSERT_RETURN(handle != NULL,, "ReleaseResource: Resource handle invalid");
ASSERT_RETURN_WARN(handle->is_loaded,, "ReleaseResource: Resource %s is not loaded.", handle->path);
g_unload_functions[handle->type](handle);
handle->is_loaded = false;
--handle->use_counter;
}
static
void Internal_LoadModelResource(ResourceContainer *resource) {
resource->model = LoadModel(resource->path);
}
static
void Internal_UnloadModelResource(ResourceContainer *resource) {
UnloadModel(resource->model);
resource->model = (Model){0};
}
static
void Internal_LoadTextureResource(ResourceContainer *resource) {
resource->texture = LoadTexture(resource->path);
}
static
void Internal_UnloadTextureResource(ResourceContainer *resource) {
UnloadTexture(resource->texture);
resource->texture = (Texture){0};
}