542 lines
14 KiB
C
542 lines
14 KiB
C
/**
|
|
* Copyright (c) 2021 Dominic Masters
|
|
*
|
|
* This software is released under the MIT License.
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#include "assetmanager.h"
|
|
|
|
assetmanagerloaderdefinition_t ASSET_MANAGER_LOADERS[] = {
|
|
{
|
|
&_assetManagerLoaderTextureAsync,
|
|
&_assetManagerLoaderTextureSync,
|
|
&_assetManagerLoaderTextureDispose
|
|
},
|
|
{
|
|
&_assetManagerLoaderFontAsync,
|
|
&_assetManagerLoaderFontSync,
|
|
&_assetManagerLoaderFontDispose
|
|
},
|
|
{
|
|
&_assetManagerLoaderShaderAsync,
|
|
&_assetManagerLoaderShaderSync,
|
|
&_assetManagerLoaderShaderDispose
|
|
},
|
|
{
|
|
&_assetManagerLoaderScaledTextureAsync,
|
|
NULL,
|
|
NULL
|
|
},
|
|
{
|
|
&_assetManagerLoaderTextureScaleAsync,
|
|
&_assetManagerLoaderTextureScaleSync,
|
|
&_assetManagerLoaderTextureScaleDispose
|
|
}
|
|
};
|
|
|
|
|
|
void assetManagerInit(assetmanager_t *manager) {
|
|
threadInit(&manager->thread, &_assetManagerThread);
|
|
manager->thread.user = manager;
|
|
manager->itemCount = 0;
|
|
manager->finished = false;
|
|
manager->holderCount = 0;
|
|
manager->running = false;
|
|
}
|
|
|
|
float assetManagerProgressGet(assetmanager_t *manager) {
|
|
float done;
|
|
uint8_t i;
|
|
|
|
done = 0.0f;
|
|
|
|
for(i = 0; i < manager->itemCount; i++) {
|
|
if(!assetManagerItemIsFinished(manager->items + i)) continue;
|
|
done++;
|
|
}
|
|
|
|
return done / ((float) manager->itemCount);
|
|
}
|
|
|
|
assetmanagerowner_t assetManagerHolderCreate(assetmanager_t *man) {
|
|
uint8_t i;
|
|
|
|
// Find first available number.
|
|
for(i = 0; i < 0xFF; i++) {
|
|
if(arrayFind(
|
|
sizeof(assetmanagerowner_t), man->holders, man->holderCount, &i
|
|
) == -1) break;
|
|
}
|
|
|
|
man->holders[man->holderCount++] = i;
|
|
return i;// No slots left.
|
|
}
|
|
|
|
void assetManagerHolderRelease(assetmanager_t *man, assetmanagerowner_t hold) {
|
|
int32_t i;
|
|
uint8_t j;
|
|
assetmanageritem_t *item;
|
|
size_t s;
|
|
|
|
s = sizeof(assetmanagerowner_t);
|
|
i = arrayFind(s, man->holders,man->holderCount,&hold);
|
|
if(i == -1) return;
|
|
|
|
arraySplice(sizeof(uint8_t), man->holders, i, 1, man->holderCount);
|
|
man->holderCount--;
|
|
|
|
for(j = 0; j < man->itemCount; j++) {
|
|
item = man->items + j;
|
|
i = arrayFind(s, item->holders, item->holderCount, &hold);
|
|
if(i == -1) continue;
|
|
arraySplice(s, item->holders, i, 1, item->holderCount);
|
|
item->holderCount--;
|
|
}
|
|
}
|
|
|
|
void assetManagerDisposeReleased(assetmanager_t *man) {
|
|
uint8_t i;
|
|
assetmanagerloader_t *disp;
|
|
assetmanageritem_t *item;
|
|
|
|
for(i = 0; i < man->itemCount; i++) {
|
|
item = man->items + i;
|
|
disp = ASSET_MANAGER_LOADERS[item->type].dispose;
|
|
if(item->holderCount > 0) continue;
|
|
if(disp != NULL) disp(item);
|
|
arraySplice(sizeof(assetmanageritem_t), man->items, i, 1, man->itemCount);
|
|
man->itemCount--;
|
|
}
|
|
}
|
|
|
|
void assetManagerDispose(assetmanager_t *man) {
|
|
uint8_t i;
|
|
assetmanagerloader_t *disp;
|
|
assetmanageritem_t *item;
|
|
|
|
for(i = 0; i < man->itemCount; i++) {
|
|
item = man->items + i;
|
|
disp = ASSET_MANAGER_LOADERS[item->type].dispose;
|
|
if(item->holderCount > 0) continue;
|
|
if(disp != NULL) disp(item);
|
|
}
|
|
|
|
man->itemCount = 0;
|
|
man->holderCount = 0;
|
|
}
|
|
|
|
// Thread Management
|
|
void assetManagerStart(assetmanager_t *manager) {
|
|
if(manager->running) return;
|
|
manager->running = true;
|
|
threadStart(&manager->thread);
|
|
}
|
|
|
|
int32_t _assetManagerThread(thread_t *thread) {
|
|
// TODO: Can I allow multiple threads to run?
|
|
uint8_t i;
|
|
assetmanager_t *manager;
|
|
assetmanageritem_t *item;
|
|
assetmanagerloaderdefinition_t *definition;
|
|
bool result;
|
|
manager = thread->user;
|
|
|
|
for(i = 0; i < manager->itemCount; i++) {
|
|
item = manager->items + i;
|
|
definition = ASSET_MANAGER_LOADERS + item->type;
|
|
|
|
// Only bother with ASYNC items
|
|
if(definition->loadAsync == NULL) continue;
|
|
|
|
// Are we already loading, not ready, or already tried to load?
|
|
if(item->state != ASSET_MANAGER_STATE_PENDING) continue;
|
|
|
|
// Begin loading
|
|
item->state = ASSET_MANAGER_STATE_ASYNC_LOADING;
|
|
result = definition->loadAsync(item);
|
|
|
|
// Finish Loading
|
|
if(!result) {
|
|
item->state = ASSET_MANAGER_STATE_ASYNC_ERROR;
|
|
} else {
|
|
item->state = ASSET_MANAGER_STATE_ASYNC_DONE;
|
|
}
|
|
}
|
|
|
|
manager->finished = assetManagerProgressGet(manager) >= 1.0f;
|
|
manager->running = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void assetManagerUpdate(assetmanager_t *manager) {
|
|
uint8_t i;
|
|
assetmanageritem_t *item;
|
|
assetmanagerloaderdefinition_t *definition;
|
|
bool result;
|
|
|
|
// Autostart
|
|
if(assetManagerProgressGet(manager) < 1.0f && !manager->running) {
|
|
assetManagerStart(manager);
|
|
}
|
|
|
|
for(i = 0; i < manager->itemCount; i++) {
|
|
item = manager->items + i;
|
|
definition = ASSET_MANAGER_LOADERS + item->type;
|
|
|
|
// Update not ready state (Synchronously)
|
|
if(item->state == ASSET_MANAGER_STATE_NOT_READY) {
|
|
item->state = ASSET_MANAGER_STATE_PENDING;
|
|
continue;
|
|
}
|
|
|
|
// If requires ASYNC loading, then confirm it has finished loading.
|
|
if(definition->loadAsync != NULL) {
|
|
if(item->state != ASSET_MANAGER_STATE_ASYNC_DONE) continue;
|
|
} else if(item->state != ASSET_MANAGER_STATE_PENDING) {
|
|
continue;
|
|
}
|
|
|
|
if(definition->loadSync == NULL) {
|
|
continue;
|
|
}
|
|
|
|
// Begin sync loading
|
|
item->state = ASSET_MANAGER_STATE_SYNC_LOADING;
|
|
result = definition->loadSync(item);
|
|
|
|
// Finish loading
|
|
if(!result) {
|
|
item->state = ASSET_MANAGER_STATE_SYNC_ERROR;
|
|
} else {
|
|
item->state = ASSET_MANAGER_STATE_SYNC_DONE;
|
|
}
|
|
}
|
|
|
|
manager->finished = assetManagerProgressGet(manager) >= 1.0f;
|
|
}
|
|
|
|
assetmanageritem_t * assetManagerItemGet(assetmanager_t *man, char *key) {
|
|
uint8_t i;
|
|
assetmanageritem_t *item;
|
|
|
|
for(i = 0; i < man->itemCount; i++) {
|
|
item = man->items + i;
|
|
if(strcmp(item->key, key) == 0) return item;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
assetmanageritem_t * assetManagerItemAdd(assetmanager_t *manager, char *key) {
|
|
// Check if key already exists.
|
|
assetmanageritem_t *item = manager->items + manager->itemCount++;
|
|
item->state = ASSET_MANAGER_STATE_NOT_READY;
|
|
memcpy(item->key, key, strlen(key) + 1);
|
|
item->holderCount = 0x00;
|
|
return item;
|
|
}
|
|
|
|
uint8_t assetManagerItemGetOrAddHolder(
|
|
assetmanageritem_t *item, assetmanagerowner_t owner
|
|
) {
|
|
uint8_t i, firstEmpty;
|
|
firstEmpty = 0xFF;
|
|
|
|
for(i = 0; i < item->holderCount; i++) {
|
|
if(item->holders[i] == owner) return i;
|
|
if(firstEmpty == 0xFF && item->holders[i] == 0xFF) {
|
|
firstEmpty = i;
|
|
}
|
|
}
|
|
|
|
if(firstEmpty == 0xFF) firstEmpty = item->holderCount++;
|
|
item->holders[firstEmpty] = owner;
|
|
return firstEmpty;
|
|
}
|
|
|
|
bool assetManagerItemIsFinished(assetmanageritem_t *item) {
|
|
// Sync done is always done
|
|
if(item->state == ASSET_MANAGER_STATE_SYNC_DONE) return true;
|
|
// Only check if ASYNC is done.
|
|
if(item->state != ASSET_MANAGER_STATE_ASYNC_DONE) return false;
|
|
// Does it need to still sync load?
|
|
if(ASSET_MANAGER_LOADERS[item->type].loadSync == NULL) return true;
|
|
return false;
|
|
}
|
|
|
|
// Font
|
|
assetmanageritem_t * assetManagerLoadFont(
|
|
assetmanager_t *manager, assetmanagerowner_t owner, char *fileName
|
|
) {
|
|
assetmanageritem_t *item;
|
|
item = assetManagerItemGet(manager, fileName);
|
|
if(item == NULL) {
|
|
item = assetManagerItemAdd(manager, fileName);
|
|
item->type = ASSET_MANAGER_TYPE_FONT;
|
|
item->data.font.fileName = fileName;
|
|
}
|
|
assetManagerItemGetOrAddHolder(item, owner);
|
|
|
|
return item;
|
|
}
|
|
|
|
bool _assetManagerLoaderFontAsync(assetmanageritem_t *item) {
|
|
item->data.font.data = assetStringLoad(item->data.font.fileName);
|
|
return item->data.font.data != NULL;
|
|
}
|
|
|
|
bool _assetManagerLoaderFontSync(assetmanageritem_t *item) {
|
|
fontInit(&item->data.font.font, item->data.font.data);
|
|
free(item->data.font.data);
|
|
return true;
|
|
}
|
|
|
|
bool _assetManagerLoaderFontDispose(assetmanageritem_t *item) {
|
|
fontDispose(&item->data.font.font);
|
|
return true;
|
|
}
|
|
|
|
// Texture
|
|
assetmanageritem_t * assetManagerLoadTexture(
|
|
assetmanager_t *manager, assetmanagerowner_t owner, char *fileName
|
|
) {
|
|
assetmanageritem_t *item;
|
|
item = assetManagerItemGet(manager, fileName);
|
|
if(item == NULL) {
|
|
item = assetManagerItemAdd(manager, fileName);
|
|
item->type = ASSET_MANAGER_TYPE_TEXTURE;
|
|
item->data.texture.fileName = fileName;
|
|
}
|
|
assetManagerItemGetOrAddHolder(item, owner);
|
|
|
|
return item;
|
|
}
|
|
|
|
bool _assetManagerLoaderTextureAsync(assetmanageritem_t *item) {
|
|
assetbuffer_t *buffer;
|
|
int channels;
|
|
stbi_io_callbacks OPENGL_STBI_CALLBACKS;
|
|
|
|
buffer = assetBufferOpen(item->data.texture.fileName);
|
|
if(buffer == NULL) return false;
|
|
|
|
// Setup the interface for STBI
|
|
OPENGL_STBI_CALLBACKS.read = &assetBufferRead;
|
|
OPENGL_STBI_CALLBACKS.skip = &assetBufferSkip;
|
|
OPENGL_STBI_CALLBACKS.eof = &assetBufferEnd;
|
|
|
|
// Buffer the image
|
|
channels = 0;
|
|
item->data.texture.data = (pixel_t *)stbi_load_from_callbacks(
|
|
&OPENGL_STBI_CALLBACKS, buffer,
|
|
&item->data.texture.width, &item->data.texture.height,
|
|
&channels, STBI_rgb_alpha
|
|
);
|
|
|
|
// Close the buffer
|
|
assetBufferClose(buffer);
|
|
if(item->data.texture.data == NULL) return false;
|
|
return true;
|
|
}
|
|
|
|
bool _assetManagerLoaderTextureSync(assetmanageritem_t *item) {
|
|
// Turn into a texture.
|
|
textureInit(
|
|
&item->data.texture.texture,
|
|
item->data.texture.width,
|
|
item->data.texture.height,
|
|
item->data.texture.data
|
|
);
|
|
|
|
// Cleanup
|
|
stbi_image_free(item->data.texture.data);
|
|
return true;
|
|
}
|
|
|
|
bool _assetManagerLoaderTextureDispose(assetmanageritem_t *item) {
|
|
textureDispose(&item->data.texture.texture);
|
|
return true;
|
|
}
|
|
|
|
// Scaled Texture
|
|
assetmanageritem_t * assetManagerLoadScaledTexture(
|
|
assetmanager_t *manager, assetmanagerowner_t owner, char *path, char *file
|
|
) {
|
|
assetmanageritem_t *item;
|
|
scaledtexture_t *st;
|
|
char buffer[ASSET_MANAGER_ITEM_NAME_MAX];
|
|
sprintf(buffer, "%s/%s", path, file);
|
|
item = assetManagerItemGet(manager, buffer);
|
|
if(item == NULL) {
|
|
item = assetManagerItemAdd(manager, buffer);
|
|
|
|
item->type = ASSET_MANAGER_TYPE_SCALED_TEXTURE;
|
|
st = &item->data.scaledTexture.scaledTexture;
|
|
st->scaleCount = 0;
|
|
st->baseWidth = 0;
|
|
st->baseHeight = 0;
|
|
st->path = path;
|
|
st->file = file;
|
|
}
|
|
assetManagerItemGetOrAddHolder(item, owner);
|
|
|
|
return item;
|
|
}
|
|
|
|
bool _assetManagerLoaderScaledTextureAsync(assetmanageritem_t *item) {
|
|
char buffer[128];
|
|
char *xmlData;
|
|
xml_t xml;
|
|
xml_t *child;
|
|
int16_t i, j;
|
|
scaledtexture_t *st;
|
|
st = &item->data.scaledTexture.scaledTexture;
|
|
|
|
// Begin loading texture XML
|
|
sprintf(buffer, "%s/%s.xml", st->path, st->file);
|
|
|
|
xmlData = assetStringLoad(buffer);
|
|
if(xmlData == NULL) return false;
|
|
xmlLoad(&xml, xmlData);
|
|
free(xmlData);
|
|
|
|
// Parse root texture info
|
|
i = xmlGetAttributeByName(&xml, "channels");
|
|
st->channels = (uint8_t)atoi(xml.attributeDatas[i]);
|
|
i = xmlGetAttributeByName(&xml, "width");
|
|
st->baseWidth = (int16_t)atoi(xml.attributeDatas[i]);
|
|
i = xmlGetAttributeByName(&xml, "height");
|
|
st->baseHeight = (int16_t)atoi(xml.attributeDatas[i]);
|
|
|
|
for(j = 0; j < xml.childrenCount; j++) {
|
|
child = xml.children + j;
|
|
i = xmlGetAttributeByName(child, "scale");
|
|
st->scales[st->scaleCount].scale = (uint8_t)atoi(child->attributeDatas[i]);
|
|
i = xmlGetAttributeByName(child, "width");
|
|
st->scales[st->scaleCount].width = (int16_t)atoi(child->attributeDatas[i]);
|
|
i = xmlGetAttributeByName(child, "height");
|
|
st->scales[st->scaleCount].height = (int16_t)atoi(child->attributeDatas[i]);
|
|
st->scaleCount++;
|
|
}
|
|
|
|
// Cleanup XML
|
|
xmlDispose(&xml);
|
|
return true;
|
|
}
|
|
|
|
// Texture Scale
|
|
assetmanageritem_t * assetManagerLoadTextureScale(
|
|
assetmanager_t *manager, assetmanagerowner_t owner,
|
|
scaledtexture_t *st, uint8_t scale
|
|
) {
|
|
assetmanageritem_t *item;
|
|
char buffer[ASSET_MANAGER_ITEM_NAME_MAX];
|
|
sprintf(buffer, "%s/%s_%u", st->path, st->file, scale);
|
|
item = assetManagerItemGet(manager, buffer);
|
|
if(item == NULL) {
|
|
item = assetManagerItemAdd(manager, buffer);
|
|
item->type = ASSET_MANAGER_TYPE_SCALE_TEXTURE;
|
|
item->data.scaleTexture.scale = scale;
|
|
item->data.scaleTexture.scaledTexture = st;
|
|
}
|
|
assetManagerItemGetOrAddHolder(item, owner);
|
|
return item;
|
|
}
|
|
|
|
bool _assetManagerLoaderTextureScaleAsync(assetmanageritem_t *item) {
|
|
char buffer[128];
|
|
scaledtexture_t *st;
|
|
scaledtexturescale_t *sts;
|
|
size_t length;
|
|
|
|
st = item->data.scaleTexture.scaledTexture;
|
|
sts = st->scales + item->data.scaleTexture.scale;
|
|
|
|
// Get filename
|
|
sprintf(buffer, "%s/%s_%i.texture", st->path, st->file, sts->scale);
|
|
|
|
// Create some space
|
|
length = assetRawLoad(buffer, NULL);
|
|
if(length == 0) return false;
|
|
item->data.scaleTexture.data = malloc(sizeof(pixel_t) * length);
|
|
|
|
// Load
|
|
length = assetRawLoad(buffer, (uint8_t *)item->data.scaleTexture.data);
|
|
if(length == 0) {
|
|
free(item->data.scaleTexture.data);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool _assetManagerLoaderTextureScaleSync(assetmanageritem_t *item) {
|
|
scaledtexture_t *st;
|
|
scaledtexturescale_t *sts;
|
|
st = item->data.scaleTexture.scaledTexture;
|
|
sts = st->scales + item->data.scaleTexture.scale;
|
|
|
|
textureInit(
|
|
&item->data.scaleTexture.texture,
|
|
sts->width, sts->height,
|
|
item->data.scaleTexture.data
|
|
);
|
|
|
|
free(item->data.scaleTexture.data);
|
|
return true;
|
|
}
|
|
|
|
bool _assetManagerLoaderTextureScaleDispose(assetmanageritem_t *item) {
|
|
textureDispose(&item->data.scaleTexture.texture);
|
|
return true;
|
|
}
|
|
|
|
// Shader
|
|
assetmanageritem_t * assetManagerLoadShader(
|
|
assetmanager_t *manager, assetmanagerowner_t owner,
|
|
char *fileVert, char *fileFrag
|
|
) {
|
|
assetmanageritem_t *item;
|
|
char buffer[ASSET_MANAGER_ITEM_NAME_MAX];
|
|
sprintf(buffer, "%s/%s", fileVert, fileFrag);
|
|
item = assetManagerItemGet(manager, buffer);
|
|
if(item == NULL) {
|
|
item = assetManagerItemAdd(manager, buffer);
|
|
item->type = ASSET_MANAGER_TYPE_SHADER;
|
|
item->data.shader.fileVert = fileVert;
|
|
item->data.shader.fileFrag = fileFrag;
|
|
}
|
|
assetManagerItemGetOrAddHolder(item, owner);
|
|
return item;
|
|
}
|
|
|
|
bool _assetManagerLoaderShaderAsync(assetmanageritem_t *item) {
|
|
item->data.shader.dataVert = assetStringLoad(item->data.shader.fileVert);
|
|
if(item->data.shader.dataVert == NULL) return false;
|
|
|
|
item->data.shader.dataFrag = assetStringLoad(item->data.shader.fileFrag);
|
|
if(item->data.shader.dataFrag == NULL) {
|
|
free(item->data.shader.fileVert);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool _assetManagerLoaderShaderSync(assetmanageritem_t *item) {
|
|
shaderInit(
|
|
&item->data.shader.shader,
|
|
item->data.shader.dataVert,
|
|
item->data.shader.dataFrag
|
|
);
|
|
free(item->data.shader.dataFrag);
|
|
free(item->data.shader.dataVert);
|
|
return true;
|
|
}
|
|
|
|
bool _assetManagerLoaderShaderDispose(assetmanageritem_t *item) {
|
|
shaderDispose(&item->data.shader.shader);
|
|
return true;
|
|
} |