179 lines
6.5 KiB
C
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};
|
|
}
|