Files
lity3d/Engine/Core/asset_manager.c
T

193 lines
5.9 KiB
C

#include "asset_manager.h"
#include "../Rendering/mesh.h"
#include "../Rendering/texture.h"
#include "../Rendering/shader.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define ASSET_BUCKET_COUNT 256
struct AssetManager {
Asset* buckets[ASSET_BUCKET_COUNT];
uint32_t next_id;
};
static uint32_t hash_str(const char* s) {
uint32_t h = 5381;
while (*s) h = ((h << 5) + h) + (unsigned char)*s++;
return h % ASSET_BUCKET_COUNT;
}
static int str_ends_with(const char* s, const char* suffix) {
size_t ls = strlen(s), lf = strlen(suffix);
return ls >= lf && strcmp(s + ls - lf, suffix) == 0;
}
static Asset* asset_alloc(AssetManager* am, AssetType type, const char* path, void* data, void (*destroy)(void*)) {
Asset* a = (Asset*)calloc(1, sizeof(Asset));
a->id = ++am->next_id;
a->type = type;
a->refcount = 1;
a->owner = am;
strncpy(a->path, path, sizeof(a->path) - 1);
a->data = data;
a->destroy = destroy;
uint32_t h = hash_str(path);
a->next = am->buckets[h];
am->buckets[h] = a;
return a;
}
static void asset_free(AssetManager* am, Asset* a) {
if (!a) return;
uint32_t h = hash_str(a->path);
Asset** cur = &am->buckets[h];
while (*cur) {
if (*cur == a) {
*cur = a->next;
break;
}
cur = &(*cur)->next;
}
if (a->destroy) a->destroy(a->data);
free(a);
}
AssetManager* asset_manager_create(void) {
AssetManager* am = (AssetManager*)calloc(1, sizeof(AssetManager));
return am;
}
void asset_manager_destroy(AssetManager* am) {
if (!am) return;
for (int i = 0; i < ASSET_BUCKET_COUNT; i++) {
Asset* a = am->buckets[i];
while (a) {
Asset* next = a->next;
if (a->destroy) a->destroy(a->data);
free(a);
a = next;
}
}
free(am);
}
Asset* asset_manager_find(AssetManager* am, const char* path) {
if (!am || !path) return NULL;
uint32_t h = hash_str(path);
for (Asset* a = am->buckets[h]; a; a = a->next) {
if (strcmp(a->path, path) == 0) return a;
}
return NULL;
}
Asset* asset_manager_get(AssetManager* am, uint32_t id) {
if (!am || id == 0) return NULL;
for (int i = 0; i < ASSET_BUCKET_COUNT; i++) {
for (Asset* a = am->buckets[i]; a; a = a->next) {
if (a->id == id) return a;
}
}
return NULL;
}
void* asset_data(Asset* a) { return a ? a->data : NULL; }
void asset_acquire(Asset* a) { if (a) a->refcount++; }
void asset_release(Asset* a) {
if (!a) return;
a->refcount--;
if (a->refcount <= 0 && a->owner) {
asset_free(a->owner, a);
}
}
// ------------------------------------------------------------------
// Mesh
// ------------------------------------------------------------------
static void destroy_mesh_asset(void* data) { mesh_destroy((Mesh*)data); }
Asset* asset_manager_load_mesh(AssetManager* am, ID3D11Device* dev, const char* path) {
if (!am || !dev || !path) return NULL;
Asset* existing = asset_manager_find(am, path);
if (existing) { asset_acquire(existing); return existing; }
Mesh* mesh = NULL;
if (strcmp(path, "cube") == 0) mesh = mesh_create_cube(dev);
else if (strcmp(path, "sphere") == 0) mesh = mesh_create_sphere(dev, 1.0f, 16);
else if (strcmp(path, "plane") == 0) mesh = mesh_create_plane(dev, 10.0f);
else if (str_ends_with(path, ".obj")) mesh = mesh_load_obj(dev, path);
if (!mesh) return NULL;
return asset_alloc(am, ASSET_MESH, path, mesh, destroy_mesh_asset);
}
// ------------------------------------------------------------------
// Texture
// ------------------------------------------------------------------
static void destroy_texture_asset(void* data) { texture_destroy((Texture*)data); }
Asset* asset_manager_load_texture(AssetManager* am, ID3D11Device* dev, const char* path) {
if (!am || !dev || !path) return NULL;
Asset* existing = asset_manager_find(am, path);
if (existing) { asset_acquire(existing); return existing; }
Texture* tex = NULL;
if (strcmp(path, "checkerboard") == 0) tex = texture_create_checkerboard(dev, 256, 8);
else tex = texture_load_from_file(dev, path);
if (!tex) return NULL;
return asset_alloc(am, ASSET_TEXTURE, path, tex, destroy_texture_asset);
}
// ------------------------------------------------------------------
// Shader
// ------------------------------------------------------------------
static void destroy_shader_asset(void* data) { shader_destroy((Shader*)data); }
static char* read_file(const char* path, uint32_t* out_len) {
FILE* f = fopen(path, "rb");
if (!f) return NULL;
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
if (size <= 0) { fclose(f); return NULL; }
char* buf = (char*)malloc((size_t)size + 1);
if (!buf) { fclose(f); return NULL; }
fread(buf, 1, (size_t)size, f);
buf[size] = '\0';
fclose(f);
if (out_len) *out_len = (uint32_t)size;
return buf;
}
Asset* asset_manager_load_shader(AssetManager* am, ID3D11Device* dev, const char* vs_path, const char* ps_path) {
if (!am || !dev) return NULL;
char key[512];
if (vs_path && ps_path) snprintf(key, sizeof(key), "%s|%s", vs_path, ps_path);
else strcpy(key, "default");
Asset* existing = asset_manager_find(am, key);
if (existing) { asset_acquire(existing); return existing; }
Shader* s = shader_create();
if (vs_path && ps_path) {
uint32_t vs_len = 0, ps_len = 0;
char* vs = read_file(vs_path, &vs_len);
char* ps = read_file(ps_path, &ps_len);
if (!vs || !ps || !shader_compile(s, dev, vs, ps, vs_len, ps_len)) {
shader_destroy(s); free(vs); free(ps); return NULL;
}
free(vs); free(ps);
} else {
if (!shader_compile(s, dev, NULL, NULL, 0, 0)) {
shader_destroy(s); return NULL;
}
}
return asset_alloc(am, ASSET_SHADER, key, s, destroy_shader_asset);
}