/** * 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; } 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--; } } // Thread Management void assetManagerStart(assetmanager_t *manager) { 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 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; return 0; } void assetManagerUpdate(assetmanager_t *manager) { uint8_t i; assetmanageritem_t *item; assetmanagerloaderdefinition_t *definition; bool result; for(i = 0; i < manager->itemCount; i++) { item = manager->items + i; definition = ASSET_MANAGER_LOADERS + item->type; // 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_PENDING; 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, font_t *font, 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; item->data.font.font = font; } 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, texture_t *texture, 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; item->data.texture.texture = texture; } 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, scaledtexture_t *st, char *path, char *file ) { assetmanageritem_t *item; 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; item->data.scaledTexture.scaledTexture = st; 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, texture_t *text, 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.texture = text; 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, shader_t *shader, 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.shader = 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; }