summaryrefslogtreecommitdiff
path: root/kon
diff options
context:
space:
mode:
authorKonAki <konaki@anokonaki.org>2026-10-09 18:21:19 +0800
committerKonAki <konaki@anokonaki.org>2026-10-09 18:21:19 +0800
commit0d73e4712f8472bf8e004faadc79dab453684866 (patch)
treeeb1ddfc88a23eb96c9dfbff481ad9051b3b27c70 /kon
parent29ac0888951c423d6673fff10c93e60b0434f204 (diff)
Add asset groups and automatic model textures
Diffstat (limited to 'kon')
-rw-r--r--kon/konassets.c177
-rw-r--r--kon/konassets.h35
-rw-r--r--kon/konmodel.c12
-rw-r--r--kon/konmodel.h6
4 files changed, 214 insertions, 16 deletions
diff --git a/kon/konassets.c b/kon/konassets.c
index 173e085..75b2fae 100644
--- a/kon/konassets.c
+++ b/kon/konassets.c
@@ -47,6 +47,7 @@ typedef struct kon_assetEntry_ {
int refs;
size_t bytes;
int loose;
+ int unbound; /* set while collecting, once the object has given back what it was using */
} kon_assetEntry_t;
struct kon_assets {
@@ -310,6 +311,8 @@ static void kon_addLoader_(kon_assets_t *assets, int type, const char *name, con
assets->loaders[type].extensions = extensions;
assets->loaders[type].load = load;
assets->loaders[type].unload = unload;
+ assets->loaders[type].bind = NULL;
+ assets->loaders[type].unbind = NULL;
assets->loaderUsed[type] = 1;
}
@@ -362,6 +365,8 @@ int kon_assetsRegister(kon_assets_t *assets, const kon_assetLoader_t *loader) {
type = assets->nextCustom++;
kon_addLoader_(assets, type, loader->name, loader->extensions, loader->load, loader->unload);
+ assets->loaders[type].bind = loader->bind;
+ assets->loaders[type].unbind = loader->unbind;
return type;
}
@@ -433,8 +438,12 @@ void *kon_assetGetAs(kon_assets_t *assets, const char *name, int type) {
entry->refs = 1;
entry->bytes = bytes;
entry->loose = loose;
+ entry->unbound = 0;
assets->count++;
+ /* this can load more assets and move the entries, so entry is not used after it */
+ if (assets->loaders[found].bind) assets->loaders[found].bind(object, assets);
+
return object;
}
@@ -447,19 +456,37 @@ void kon_assetRelease(kon_assets_t *assets, const char *name) {
}
int kon_assetsCollect(kon_assets_t *assets) {
- int kept = 0, freed = 0;
+ int freed = 0, again;
if (!assets) return 0;
- for (int i = 0; i < assets->count; i++) {
- if (assets->entries[i].refs > 0) {
- assets->entries[kept++] = assets->entries[i];
- } else {
- kon_unloadEntry_(assets, &assets->entries[i]);
- freed++;
+ /* an object that is freed can give back the last use of another one, so go around until nothing changes */
+ do {
+ int kept = 0, before = freed;
+
+ /* first let everything that is not used say what it was using, which only lowers the use counts of others */
+ for (int i = 0; i < assets->count; i++) {
+ kon_assetEntry_t *entry = &assets->entries[i];
+ if (entry->refs > 0 || entry->unbound) continue;
+ entry->unbound = 1;
+ if (assets->loaders[entry->type].unbind) assets->loaders[entry->type].unbind(entry->object, assets);
}
- }
- assets->count = kept;
+
+ for (int i = 0; i < assets->count; i++) {
+ if (assets->entries[i].refs > 0 || !assets->entries[i].unbound) {
+ assets->entries[kept++] = assets->entries[i];
+ } else {
+ kon_unloadEntry_(assets, &assets->entries[i]);
+ freed++;
+ }
+ }
+ assets->count = kept;
+
+ /* something that just became unused has to be looked at again */
+ again = freed != before;
+ for (int i = 0; i < assets->count && !again; i++) again = assets->entries[i].refs <= 0 && !assets->entries[i].unbound;
+ } while (again);
+
return freed;
}
@@ -501,6 +528,138 @@ void kon_assetsDump(const kon_assets_t *assets, FILE *out) {
}
}
+/*** groups ***/
+
+struct kon_assetGroup {
+ kon_assets_t *assets;
+ char **names;
+ char *state; /* 0 waiting, 1 loaded, 2 failed */
+ int count, capacity, next;
+};
+
+kon_assetGroup_t *kon_assetGroupCreate(kon_assets_t *assets) {
+ kon_assetGroup_t *group;
+
+ if (!assets) return NULL;
+ group = calloc(1, sizeof *group);
+ if (group) group->assets = assets;
+ return group;
+}
+
+int kon_assetGroupQueue(kon_assetGroup_t *group, const char *name) {
+ char *copy;
+
+ if (!group || !name) return 0;
+ if (group->count == group->capacity) {
+ int capacity = group->capacity ? group->capacity * 2 : 16;
+ char **names = realloc(group->names, (size_t)capacity * sizeof *names);
+ char *state;
+
+ if (!names) return 0;
+ group->names = names;
+ state = realloc(group->state, (size_t)capacity);
+ if (!state) return 0;
+ group->state = state;
+ group->capacity = capacity;
+ }
+ copy = malloc(strlen(name) + 1);
+ if (!copy) return 0;
+ strcpy(copy, name);
+ group->names[group->count] = copy;
+ group->state[group->count] = 0;
+ group->count++;
+ return 1;
+}
+
+int kon_assetGroupQueueList(kon_assetGroup_t *group, const char *listName) {
+ kon_blob_t *blob;
+ char *line;
+ int queued = 0;
+
+ if (!group || !listName) return -1;
+ blob = kon_assetGetAs(group->assets, listName, KON_ASSET_BLOB);
+ if (!blob) return -1;
+
+ /* the blob has a 0 after the end, so each line is cut out in place and put back */
+ line = (char *)blob->data;
+ while (*line) {
+ char *end = line;
+ char *start, *stop;
+
+ while (*end && *end != '\n') end++;
+ start = line;
+ stop = end;
+ while (start < stop && (*start == ' ' || *start == '\t' || *start == '\r')) start++;
+ while (stop > start && (stop[-1] == ' ' || stop[-1] == '\t' || stop[-1] == '\r')) stop--;
+
+ if (stop > start && *start != '#') {
+ char saved = *stop;
+ int ok;
+ *stop = 0;
+ ok = kon_assetGroupQueue(group, start);
+ *stop = saved;
+ if (!ok) break;
+ queued++;
+ }
+ line = *end ? end + 1 : end;
+ }
+ kon_assetRelease(group->assets, listName);
+ return queued;
+}
+
+int kon_assetGroupStep(kon_assetGroup_t *group) {
+ if (!group) return 0;
+
+ while (group->next < group->count && group->state[group->next] != 0) group->next++;
+ if (group->next < group->count) {
+ int i = group->next++;
+
+ if (kon_assetGet(group->assets, group->names[i])) {
+ group->state[i] = 1;
+ } else {
+ group->state[i] = 2;
+#ifdef KON_DEVELOPER
+ fprintf(stderr, "asset group: could not load %s: %s\n", group->names[i], kon_assetsError(group->assets));
+#endif
+ }
+ }
+
+ while (group->next < group->count && group->state[group->next] != 0) group->next++;
+ return group->count - group->next;
+}
+
+int kon_assetGroupLoadAll(kon_assetGroup_t *group) {
+ int failed = 0, i;
+
+ if (!group) return 0;
+ while (kon_assetGroupStep(group) > 0) {}
+ for (i = 0; i < group->count; i++) if (group->state[i] == 2) failed++;
+ return failed;
+}
+
+void kon_assetGroupProgress(const kon_assetGroup_t *group, int *loaded, int *total) {
+ int i, done = 0;
+
+ if (group) for (i = 0; i < group->count; i++) if (group->state[i] == 1) done++;
+ if (loaded) *loaded = done;
+ if (total) *total = group ? group->count : 0;
+}
+
+void kon_assetGroupFree(kon_assetGroup_t *group) {
+ int i;
+
+ if (!group) return;
+ for (i = 0; i < group->count; i++) {
+ if (group->state[i] == 1) kon_assetRelease(group->assets, group->names[i]);
+ free(group->names[i]);
+ }
+ kon_assetsCollect(group->assets);
+ free(group->names);
+ free(group->state);
+ free(group);
+}
+
+
const char *kon_assetsError(const kon_assets_t *assets) {
return assets ? assets->error : "no assets";
}
diff --git a/kon/konassets.h b/kon/konassets.h
index 6dd65eb..3b35629 100644
--- a/kon/konassets.h
+++ b/kon/konassets.h
@@ -65,6 +65,10 @@ typedef struct kon_assetLoader {
const char *extensions;
void *(*load)(const uint8_t *data, size_t size, size_t *bytes);
void (*unload)(void *object);
+ /* both can be NULL. bind is called right after load and is the place to get other assets the object needs, like the textures of a model.
+ unbind is called by kon_assetsCollect before the object is freed, and gives those assets back. assets that depend on each other in a circle never get freed */
+ void (*bind)(void *object, kon_assets_t *assets);
+ void (*unbind)(void *object, kon_assets_t *assets);
} kon_assetLoader_t;
typedef struct kon_assetInfo {
@@ -89,7 +93,7 @@ int kon_assetsRegister(kon_assets_t *assets, const kon_assetLoader_t *loader);
NULL if it is not found or fails to load, see kon_assetsError */
void *kon_assetGet(kon_assets_t *assets, const char *name);
-/* same but NULL if the file is not of that type, without counting a use */
+/* same, but NULL if the file is not of that type. counts a use the same way, and a file of the wrong type isn't loaded */
void *kon_assetGetAs(kon_assets_t *assets, const char *name, int type);
#define kon_assetBlob(assets, name) ((kon_blob_t *)kon_assetGetAs((assets), (name), KON_ASSET_BLOB))
@@ -99,9 +103,36 @@ void *kon_assetGetAs(kon_assets_t *assets, const char *name, int type);
/* gives back one use. nothing is freed until kon_assetsCollect */
void kon_assetRelease(kon_assets_t *assets, const char *name);
-/* frees everything nobody is using, returns how many it freed */
+/* frees everything nobody is using, and then what only that was using, returns how many it freed */
int kon_assetsCollect(kon_assets_t *assets);
+/*** groups ***/
+
+/* a group is a list of assets that live and die together, like everything one level needs.
+ loading the next level's group before freeing the old one keeps what both use */
+typedef struct kon_assetGroup kon_assetGroup_t;
+
+kon_assetGroup_t *kon_assetGroupCreate(kon_assets_t *assets);
+
+/* adds a name to the waiting list, nothing is loaded yet. returns 0 if there was no memory */
+int kon_assetGroupQueue(kon_assetGroup_t *group, const char *name);
+
+/* queues every name in a text asset, one per line. empty lines and lines starting with # are skipped.
+ returns how many it queued, or -1 if the list can't be read */
+int kon_assetGroupQueueList(kon_assetGroup_t *group, const char *listName);
+
+/* loads one waiting asset and returns how many are still waiting, so a loading bar can call it once per frame */
+int kon_assetGroupStep(kon_assetGroup_t *group);
+
+/* loads everything that is waiting and returns how many could not be loaded */
+int kon_assetGroupLoadAll(kon_assetGroup_t *group);
+
+/* loaded counts the assets that are in, total also counts the ones waiting and the ones that failed */
+void kon_assetGroupProgress(const kon_assetGroup_t *group, int *loaded, int *total);
+
+/* gives back every use the group holds and frees what nothing else uses. the group is gone after this */
+void kon_assetGroupFree(kon_assetGroup_t *group);
+
/* for the debug view */
int kon_assetsCount(const kon_assets_t *assets);
int kon_assetsInfo(const kon_assets_t *assets, int index, kon_assetInfo_t *info);
diff --git a/kon/konmodel.c b/kon/konmodel.c
index a92fb89..78995c8 100644
--- a/kon/konmodel.c
+++ b/kon/konmodel.c
@@ -307,7 +307,15 @@ static void kon_modelAssetUnload_(void *object) {
kon_modelFree((kon_model_t *)object);
}
-static const kon_assetLoader_t kon_modelLoader_ = {"model", ".kmdl", kon_modelAssetLoad_, kon_modelAssetUnload_};
+static void kon_modelAssetBind_(void *object, kon_assets_t *assets) {
+ kon_modelBindTextures((kon_model_t *)object, assets);
+}
+
+static void kon_modelAssetUnbind_(void *object, kon_assets_t *assets) {
+ kon_modelUnbindTextures((kon_model_t *)object, assets);
+}
+
+static const kon_assetLoader_t kon_modelLoader_ = {"model", ".kmdl", kon_modelAssetLoad_, kon_modelAssetUnload_, kon_modelAssetBind_, kon_modelAssetUnbind_};
int kon_modelRegister(kon_assets_t *assets) {
return kon_assetsRegister(assets, &kon_modelLoader_);
@@ -320,9 +328,7 @@ void kon_modelBindTextures(kon_model_t *model, kon_assets_t *assets) {
for (i = 0; i < model->materialCount; i++) {
kon_modelMaterial_t *m = &model->materials[i];
if (!m->texture[0] || m->image) continue;
- if (!kon_assetGet(assets, m->texture)) continue;
m->image = kon_assetImage(assets, m->texture);
- if (!m->image) kon_assetRelease(assets, m->texture);
}
}
diff --git a/kon/konmodel.h b/kon/konmodel.h
index 1c64891..8ce36c8 100644
--- a/kon/konmodel.h
+++ b/kon/konmodel.h
@@ -107,10 +107,12 @@ typedef struct kon_model {
kon_model_t *kon_modelLoad(const uint8_t *data, size_t size);
void kon_modelFree(kon_model_t *model);
-/* loads .kmdl files through the asset manager, returns the type id for kon_assetGetAs or -1 */
+/* loads .kmdl files through the asset manager, returns the type id for kon_assetGetAs or -1.
+ a model gets its textures when it is loaded and gives them back when it is collected, you don't need to bind them yourself */
int kon_modelRegister(kon_assets_t *assets);
-/* gets every texture the materials name and keeps it in use until unbind. missing ones are left untextured */
+/* gets every texture the materials name and keeps it in use until unbind. missing ones are left untextured.
+ only needed for models you loaded without the asset manager */
void kon_modelBindTextures(kon_model_t *model, kon_assets_t *assets);
void kon_modelUnbindTextures(kon_model_t *model, kon_assets_t *assets);