Chunks, models, and more
This commit is contained in:
+19
-9
@@ -411,16 +411,26 @@ errorret_t assetDispose(void) {
|
||||
assertIsMainThread("Must be called from the main thread.");
|
||||
threadStop(&ASSET.loadThread);
|
||||
|
||||
// Dispose every non-null entry so type-specific dispose callbacks
|
||||
// (e.g. assetScriptDispose freeing jerry values) run before the
|
||||
// scripting engine is torn down.
|
||||
assetentry_t *entry = ASSET.entries;
|
||||
// Drain-dispose: repeatedly find and dispose zero-ref LOADED entries
|
||||
// until none remain. This handles dependency chains where an entry
|
||||
// (e.g. a model) holds refs on child entries (mesh, texture): dispose
|
||||
// parents first so child ref counts drop to zero, then pick up the
|
||||
// children on the next pass. Without this, a forward-only scan fails
|
||||
// when a shared child entry appears before a parent that still holds a
|
||||
// ref to it.
|
||||
bool_t any;
|
||||
do {
|
||||
if(entry->type != ASSET_LOADER_TYPE_NULL) {
|
||||
errorChain(assetEntryDispose(entry));
|
||||
}
|
||||
entry++;
|
||||
} while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX);
|
||||
any = false;
|
||||
assetentry_t *e = ASSET.entries;
|
||||
do {
|
||||
if(e->type == ASSET_LOADER_TYPE_NULL) { e++; continue; }
|
||||
if(e->state != ASSET_ENTRY_STATE_LOADED) { e++; continue; }
|
||||
if(e->refs.count > 0) { e++; continue; }
|
||||
errorChain(assetEntryDispose(e));
|
||||
any = true;
|
||||
e++;
|
||||
} while(e < ASSET.entries + ASSET_ENTRY_COUNT_MAX);
|
||||
} while(any);
|
||||
|
||||
// Cleanup zip file.
|
||||
if(ASSET.zip != NULL) {
|
||||
|
||||
@@ -16,6 +16,12 @@ assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT] = {
|
||||
.dispose = assetMeshDispose
|
||||
},
|
||||
|
||||
[ASSET_LOADER_TYPE_MODEL] = {
|
||||
.loadSync = assetModelLoaderSync,
|
||||
.loadAsync = assetModelLoaderAsync,
|
||||
.dispose = assetModelDispose
|
||||
},
|
||||
|
||||
[ASSET_LOADER_TYPE_TEXTURE] = {
|
||||
.loadSync = assetTextureLoaderSync,
|
||||
.loadAsync = assetTextureLoaderAsync,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
#include "asset/loader/dmf/assetmeshloader.h"
|
||||
#include "asset/loader/dmf/assetmodelloader.h"
|
||||
#include "asset/loader/display/assettextureloader.h"
|
||||
#include "asset/loader/display/assettilesetloader.h"
|
||||
#include "asset/loader/locale/assetlocaleloader.h"
|
||||
@@ -17,6 +18,7 @@ typedef enum {
|
||||
ASSET_LOADER_TYPE_NULL,
|
||||
|
||||
ASSET_LOADER_TYPE_MESH,
|
||||
ASSET_LOADER_TYPE_MODEL,
|
||||
ASSET_LOADER_TYPE_TEXTURE,
|
||||
ASSET_LOADER_TYPE_TILESET,
|
||||
ASSET_LOADER_TYPE_LOCALE,
|
||||
@@ -28,6 +30,7 @@ typedef enum {
|
||||
|
||||
typedef union {
|
||||
assetmeshloaderloading_t mesh;
|
||||
assetmodelloaderloading_t model;
|
||||
assettextureloaderloading_t texture;
|
||||
assettilesetloaderloading_t tileset;
|
||||
assetlocaleloaderloading_t locale;
|
||||
@@ -37,6 +40,7 @@ typedef union {
|
||||
|
||||
typedef union {
|
||||
assetmeshoutput_t mesh;
|
||||
assetmodeloutput_t model;
|
||||
assettextureoutput_t texture;
|
||||
assettilesetoutput_t tileset;
|
||||
assetlocaleoutput_t locale;
|
||||
|
||||
@@ -100,8 +100,8 @@ errorret_t assetChunkLoaderSync(assetloading_t *loading) {
|
||||
) {
|
||||
nameLen++;
|
||||
}
|
||||
memoryCopy(out->meshNames[m], data + offset, nameLen);
|
||||
out->meshNames[m][nameLen] = '\0';
|
||||
memoryCopy(out->modelNames[m], data + offset, nameLen);
|
||||
out->modelNames[m][nameLen] = '\0';
|
||||
offset += nameLen + 1;
|
||||
|
||||
memoryCopy(
|
||||
|
||||
@@ -34,7 +34,7 @@ typedef struct {
|
||||
typedef struct {
|
||||
tile_t tiles[CHUNK_TILE_COUNT];
|
||||
uint8_t meshCount;
|
||||
char_t meshNames[CHUNK_MESH_COUNT_MAX][CHUNK_MESH_NAME_MAX];
|
||||
char_t modelNames[CHUNK_MESH_COUNT_MAX][CHUNK_MESH_NAME_MAX];
|
||||
vec3 meshOffsets[CHUNK_MESH_COUNT_MAX];
|
||||
} assetchunkoutput_t;
|
||||
|
||||
@@ -51,7 +51,7 @@ errorret_t assetChunkLoaderAsync(assetloading_t *loading);
|
||||
/**
|
||||
* Synchronous loader for chunk assets. Validates the DCF binary previously
|
||||
* read by the async phase and populates the output assetchunkoutput_t with
|
||||
* tile data and DMF mesh names.
|
||||
* tile data and model paths.
|
||||
*
|
||||
* @param loading Loading information for the asset being loaded.
|
||||
* @return Error code indicating success or failure of the load operation.
|
||||
|
||||
@@ -6,4 +6,5 @@
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
assetmeshloader.c
|
||||
assetmodelloader.c
|
||||
)
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "assetmodelloader.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/string.h"
|
||||
#include "asset/asset.h"
|
||||
#include "asset/loader/assetloading.h"
|
||||
#include "asset/loader/assetentry.h"
|
||||
#include "asset/loader/json/assetjsonloader.h"
|
||||
#include "display/texture/texture.h"
|
||||
#include "yyjson.h"
|
||||
|
||||
errorret_t assetModelLoaderAsync(assetloading_t *loading) {
|
||||
assertNotNull(loading, "Loading cannot be NULL");
|
||||
assertNotMainThread("Should be called from an async thread.");
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetModelLoaderSync(assetloading_t *loading) {
|
||||
assertNotNull(loading, "Loading cannot be NULL");
|
||||
assertTrue(loading->type == ASSET_LOADER_TYPE_MODEL, "Invalid type.");
|
||||
assertIsMainThread("Must be called from the main thread.");
|
||||
|
||||
switch(loading->loading.model.state) {
|
||||
case ASSET_MODEL_LOADING_STATE_INITIAL:
|
||||
loading->loading.model.state = ASSET_MODEL_LOADING_STATE_LOCK_ASSETS;
|
||||
loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC;
|
||||
errorOk();
|
||||
break;
|
||||
|
||||
case ASSET_MODEL_LOADING_STATE_LOCK_ASSETS: {
|
||||
// Lock the model's JSON descriptor as a JSON sub-asset. The entry
|
||||
// key is prefixed with "json:" to avoid a type-collision with the
|
||||
// model entry itself (both share the same filename). The JSON loader
|
||||
// reads the real file path supplied in the input.
|
||||
char_t jsonKey[ASSET_FILE_NAME_MAX];
|
||||
stringFormat(
|
||||
jsonKey, sizeof(jsonKey), "json:%s", loading->entry->name
|
||||
);
|
||||
assetloaderinput_t jsonInput;
|
||||
memoryZero(&jsonInput, sizeof(jsonInput));
|
||||
stringCopy(
|
||||
jsonInput.json.path, loading->entry->name, ASSET_FILE_NAME_MAX
|
||||
);
|
||||
assetentry_t *jsonEntry = assetLock(
|
||||
jsonKey, ASSET_LOADER_TYPE_JSON, &jsonInput
|
||||
);
|
||||
errorret_t ret = assetRequireLoaded(jsonEntry);
|
||||
if(errorIsNotOk(ret)) {
|
||||
assetUnlockEntry(jsonEntry);
|
||||
loading->entry->state = ASSET_ENTRY_STATE_ERROR;
|
||||
errorChain(ret);
|
||||
}
|
||||
|
||||
yyjson_val *root = yyjson_doc_get_root(jsonEntry->data.json);
|
||||
|
||||
// Parse required mesh path.
|
||||
yyjson_val *meshVal = yyjson_obj_get(root, "mesh");
|
||||
if(!meshVal || !yyjson_is_str(meshVal)) {
|
||||
assetUnlockEntry(jsonEntry);
|
||||
loading->entry->state = ASSET_ENTRY_STATE_ERROR;
|
||||
errorThrow("Model JSON missing 'mesh' string");
|
||||
}
|
||||
const char *meshStr = yyjson_get_str(meshVal);
|
||||
size_t meshLen = yyjson_get_len(meshVal);
|
||||
if(meshLen >= ASSET_FILE_NAME_MAX) {
|
||||
assetUnlockEntry(jsonEntry);
|
||||
loading->entry->state = ASSET_ENTRY_STATE_ERROR;
|
||||
errorThrow("Model mesh path exceeds max length");
|
||||
}
|
||||
char_t meshName[ASSET_FILE_NAME_MAX];
|
||||
memoryCopy(meshName, meshStr, meshLen + 1);
|
||||
|
||||
// Parse optional texture path (absent = color only, no texture).
|
||||
char_t textureName[ASSET_FILE_NAME_MAX];
|
||||
textureName[0] = '\0';
|
||||
yyjson_val *texVal = yyjson_obj_get(root, "texture");
|
||||
if(texVal && yyjson_is_str(texVal)) {
|
||||
const char *texStr = yyjson_get_str(texVal);
|
||||
size_t texLen = yyjson_get_len(texVal);
|
||||
if(texLen >= ASSET_FILE_NAME_MAX) {
|
||||
assetUnlockEntry(jsonEntry);
|
||||
loading->entry->state = ASSET_ENTRY_STATE_ERROR;
|
||||
errorThrow("Model texture path exceeds max length");
|
||||
}
|
||||
memoryCopy(textureName, texStr, texLen + 1);
|
||||
}
|
||||
|
||||
// Parse optional color (RGBA array 0-255, default white).
|
||||
color_t color = COLOR_WHITE;
|
||||
yyjson_val *colorVal = yyjson_obj_get(root, "color");
|
||||
if(colorVal && yyjson_is_arr(colorVal)) {
|
||||
uint8_t ch[4] = {255, 255, 255, 255};
|
||||
size_t colorIdx, colorLen;
|
||||
yyjson_val *colorElem;
|
||||
yyjson_arr_foreach(colorVal, colorIdx, colorLen, colorElem) {
|
||||
if(colorIdx >= 4) break;
|
||||
if(yyjson_is_int(colorElem)) {
|
||||
ch[colorIdx] = (uint8_t)yyjson_get_int(colorElem);
|
||||
}
|
||||
}
|
||||
color.r = ch[0];
|
||||
color.g = ch[1];
|
||||
color.b = ch[2];
|
||||
color.a = ch[3];
|
||||
}
|
||||
|
||||
// Release JSON entry; all needed fields are now in local variables.
|
||||
assetUnlockEntry(jsonEntry);
|
||||
|
||||
// Lock and load the mesh sub-asset.
|
||||
assetentry_t *meshEntry = assetLock(meshName, ASSET_LOADER_TYPE_MESH, NULL);
|
||||
ret = assetRequireLoaded(meshEntry);
|
||||
if(errorIsNotOk(ret)) {
|
||||
assetUnlockEntry(meshEntry);
|
||||
loading->entry->state = ASSET_ENTRY_STATE_ERROR;
|
||||
errorChain(ret);
|
||||
}
|
||||
|
||||
// Lock and load the optional texture sub-asset.
|
||||
assetentry_t *texEntry = NULL;
|
||||
if(textureName[0] != '\0') {
|
||||
assetloaderinput_t texInput = { .texture = TEXTURE_FORMAT_RGBA };
|
||||
texEntry = assetLock(textureName, ASSET_LOADER_TYPE_TEXTURE, &texInput);
|
||||
ret = assetRequireLoaded(texEntry);
|
||||
if(errorIsNotOk(ret)) {
|
||||
assetUnlockEntry(texEntry);
|
||||
assetUnlockEntry(meshEntry);
|
||||
loading->entry->state = ASSET_ENTRY_STATE_ERROR;
|
||||
errorChain(ret);
|
||||
}
|
||||
}
|
||||
|
||||
assetmodeloutput_t *out = &loading->entry->data.model;
|
||||
out->meshEntry = meshEntry;
|
||||
out->texEntry = texEntry;
|
||||
out->color = color;
|
||||
loading->entry->state = ASSET_ENTRY_STATE_LOADED;
|
||||
errorOk();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetModelDispose(assetentry_t *entry) {
|
||||
assertNotNull(entry, "Entry cannot be NULL");
|
||||
assertTrue(entry->type == ASSET_LOADER_TYPE_MODEL, "Invalid type.");
|
||||
assertIsMainThread("Must be called from the main thread.");
|
||||
|
||||
assetmodeloutput_t *out = &entry->data.model;
|
||||
if(out->meshEntry != NULL) {
|
||||
assetUnlockEntry(out->meshEntry);
|
||||
out->meshEntry = NULL;
|
||||
}
|
||||
if(out->texEntry != NULL) {
|
||||
assetUnlockEntry(out->texEntry);
|
||||
out->texEntry = NULL;
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "display/color.h"
|
||||
|
||||
typedef struct assetloading_s assetloading_t;
|
||||
typedef struct assetentry_s assetentry_t;
|
||||
|
||||
typedef enum {
|
||||
ASSET_MODEL_LOADING_STATE_INITIAL,
|
||||
ASSET_MODEL_LOADING_STATE_LOCK_ASSETS
|
||||
} assetmodelloadingstate_t;
|
||||
|
||||
typedef struct {
|
||||
assetmodelloadingstate_t state;
|
||||
} assetmodelloaderloading_t;
|
||||
|
||||
typedef struct {
|
||||
assetentry_t *meshEntry;
|
||||
assetentry_t *texEntry;
|
||||
color_t color;
|
||||
} assetmodeloutput_t;
|
||||
|
||||
/**
|
||||
* Async loader stub for model assets. Model loading is performed entirely
|
||||
* on the main thread; this callback is never reached.
|
||||
*
|
||||
* @param loading Loading information for the asset being loaded.
|
||||
* @returns Always success.
|
||||
*/
|
||||
errorret_t assetModelLoaderAsync(assetloading_t *loading);
|
||||
|
||||
/**
|
||||
* Synchronous loader for model assets. Locks the JSON descriptor file as
|
||||
* a sub-asset, waits for it to load, parses the mesh path, optional
|
||||
* texture path, and color tint, then releases the JSON entry. Locks and
|
||||
* waits for mesh and optional texture sub-assets sequentially to stay
|
||||
* within ASSET_LOADING_COUNT_MAX slot limits.
|
||||
*
|
||||
* @param loading Loading information for the asset being loaded.
|
||||
* @returns Error code indicating success or failure.
|
||||
*/
|
||||
errorret_t assetModelLoaderSync(assetloading_t *loading);
|
||||
|
||||
/**
|
||||
* Disposer for model assets. Releases the locks on the mesh and texture
|
||||
* sub-asset entries.
|
||||
*
|
||||
* @param entry Asset entry containing the model to dispose.
|
||||
* @returns Error code indicating success or failure.
|
||||
*/
|
||||
errorret_t assetModelDispose(assetentry_t *entry);
|
||||
@@ -22,9 +22,11 @@ errorret_t assetJsonLoaderAsync(assetloading_t *loading) {
|
||||
assertNull(loading->loading.json.buffer, "Buffer already defined?");
|
||||
|
||||
assetfile_t *file = &loading->loading.json.file;
|
||||
assetLoaderErrorChain(loading,
|
||||
assetFileInit(file, loading->entry->name, NULL, NULL)
|
||||
);
|
||||
const char_t *filePath = (
|
||||
loading->entry->input != NULL &&
|
||||
loading->entry->input->json.path[0] != '\0'
|
||||
) ? loading->entry->input->json.path : loading->entry->name;
|
||||
assetLoaderErrorChain(loading, assetFileInit(file, filePath, NULL, NULL));
|
||||
|
||||
if(file->size > ASSET_JSON_FILE_SIZE_MAX) {
|
||||
assetLoaderErrorThrow(loading, "JSON exceeds maximum allowed size");
|
||||
|
||||
@@ -14,7 +14,16 @@
|
||||
typedef struct assetloading_s assetloading_t;
|
||||
typedef struct assetentry_s assetentry_t;
|
||||
|
||||
typedef struct { void *nothing; } assetjsonloaderinput_t;
|
||||
/**
|
||||
* Optional file path override. When path[0] is non-zero the JSON loader
|
||||
* reads this path from the archive instead of loading->entry->name. Use
|
||||
* this to decouple the asset pool key from the actual file location (e.g.
|
||||
* when a parent loader locks a JSON entry under a synthetic key to avoid
|
||||
* a type-collision with an entry of the same name but different type).
|
||||
*/
|
||||
typedef struct {
|
||||
char_t path[ASSET_FILE_NAME_MAX];
|
||||
} assetjsonloaderinput_t;
|
||||
|
||||
typedef enum {
|
||||
ASSET_JSON_LOADING_STATE_INITIAL,
|
||||
|
||||
@@ -17,7 +17,7 @@ console_t CONSOLE;
|
||||
|
||||
void consoleInit(void) {
|
||||
memoryZero(&CONSOLE, sizeof(console_t));
|
||||
CONSOLE.visible = false;
|
||||
CONSOLE.visible = true;
|
||||
|
||||
#ifdef DUSK_CONSOLE_POSIX
|
||||
threadMutexInit(&CONSOLE.printMutex);
|
||||
|
||||
@@ -22,10 +22,10 @@ typedef struct chunk_s {
|
||||
assetentry_t *dcfEntry;
|
||||
|
||||
uint8_t meshCount;
|
||||
char_t meshNames[CHUNK_MESH_COUNT_MAX][CHUNK_MESH_NAME_MAX];
|
||||
char_t modelNames[CHUNK_MESH_COUNT_MAX][CHUNK_MESH_NAME_MAX];
|
||||
vec3 meshOffsets[CHUNK_MESH_COUNT_MAX];
|
||||
mat4 meshModels[CHUNK_MESH_COUNT_MAX];
|
||||
assetentry_t *meshEntries[CHUNK_MESH_COUNT_MAX];
|
||||
assetentry_t *modelEntries[CHUNK_MESH_COUNT_MAX];
|
||||
|
||||
uint8_t entities[CHUNK_ENTITY_COUNT_MAX];
|
||||
} chunk_t;
|
||||
|
||||
@@ -148,9 +148,9 @@ void mapChunkUnload(chunk_t *chunk) {
|
||||
}
|
||||
|
||||
for(uint8_t m = 0; m < chunk->meshCount; m++) {
|
||||
if(chunk->meshEntries[m] == NULL) continue;
|
||||
assetUnlockEntry(chunk->meshEntries[m]);
|
||||
chunk->meshEntries[m] = NULL;
|
||||
if(chunk->modelEntries[m] == NULL) continue;
|
||||
assetUnlockEntry(chunk->modelEntries[m]);
|
||||
chunk->modelEntries[m] = NULL;
|
||||
}
|
||||
chunk->meshCount = 0;
|
||||
}
|
||||
@@ -183,6 +183,7 @@ errorret_t mapChunkLoad(chunk_t *chunk) {
|
||||
}
|
||||
|
||||
errorret_t mapWaitForChunks() {
|
||||
// Phase 1: Wait for all DCF entries to finish loading.
|
||||
bool_t anyPending;
|
||||
do {
|
||||
anyPending = false;
|
||||
@@ -190,76 +191,70 @@ errorret_t mapWaitForChunks() {
|
||||
|
||||
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
||||
chunk_t *chunk = &MAP.chunks[i];
|
||||
if(chunk->dcfEntry == NULL) continue;
|
||||
|
||||
if(chunk->dcfEntry != NULL) {
|
||||
assetentrystate_t state = chunk->dcfEntry->state;
|
||||
assetentrystate_t state = chunk->dcfEntry->state;
|
||||
if(state == ASSET_ENTRY_STATE_LOADED) {
|
||||
uint8_t meshCount = chunk->dcfEntry->data.chunk.meshCount;
|
||||
memoryCopy(
|
||||
chunk->tiles,
|
||||
chunk->dcfEntry->data.chunk.tiles,
|
||||
sizeof(chunk->tiles)
|
||||
);
|
||||
worldpos_t wp;
|
||||
chunkPosToWorldPos(&chunk->position, &wp);
|
||||
vec3 wpf = { (float_t)wp.x, (float_t)wp.y, (float_t)wp.z };
|
||||
|
||||
if(state == ASSET_ENTRY_STATE_LOADED) {
|
||||
uint8_t meshCount = chunk->dcfEntry->data.chunk.meshCount;
|
||||
memoryCopy(
|
||||
chunk->tiles,
|
||||
chunk->dcfEntry->data.chunk.tiles,
|
||||
sizeof(chunk->tiles)
|
||||
for(uint8_t m = 0; m < meshCount; m++) {
|
||||
stringCopy(
|
||||
chunk->modelNames[m],
|
||||
chunk->dcfEntry->data.chunk.modelNames[m],
|
||||
CHUNK_MESH_NAME_MAX
|
||||
);
|
||||
worldpos_t wp;
|
||||
chunkPosToWorldPos(&chunk->position, &wp);
|
||||
vec3 wpf = { (float_t)wp.x, (float_t)wp.y, (float_t)wp.z };
|
||||
|
||||
for(uint8_t m = 0; m < meshCount; m++) {
|
||||
stringCopy(
|
||||
chunk->meshNames[m],
|
||||
chunk->dcfEntry->data.chunk.meshNames[m],
|
||||
CHUNK_MESH_NAME_MAX
|
||||
);
|
||||
glm_vec3_copy(
|
||||
chunk->dcfEntry->data.chunk.meshOffsets[m],
|
||||
chunk->meshOffsets[m]
|
||||
);
|
||||
vec3 pos;
|
||||
glm_vec3_add(wpf, chunk->meshOffsets[m], pos);
|
||||
glm_translate_make(chunk->meshModels[m], pos);
|
||||
}
|
||||
assetUnlockEntry(chunk->dcfEntry);
|
||||
chunk->dcfEntry = NULL;
|
||||
|
||||
for(uint8_t m = 0; m < meshCount; m++) {
|
||||
chunk->meshEntries[m] = assetLock(
|
||||
chunk->meshNames[m], ASSET_LOADER_TYPE_MESH, NULL
|
||||
);
|
||||
if(chunk->meshEntries[m] == NULL) {
|
||||
for(uint8_t j = 0; j < m; j++) {
|
||||
assetUnlockEntry(chunk->meshEntries[j]);
|
||||
chunk->meshEntries[j] = NULL;
|
||||
}
|
||||
errorThrow("Failed to lock mesh: %s", chunk->meshNames[m]);
|
||||
}
|
||||
}
|
||||
chunk->meshCount = meshCount;
|
||||
// Fall through to the mesh-state check below so cached meshes
|
||||
// don't force an unnecessary extra sleep iteration.
|
||||
|
||||
} else if(state == ASSET_ENTRY_STATE_ERROR) {
|
||||
assetUnlockEntry(chunk->dcfEntry);
|
||||
chunk->dcfEntry = NULL;
|
||||
memorySet(chunk->tiles, TILE_SHAPE_GROUND, sizeof(chunk->tiles));
|
||||
continue;
|
||||
} else {
|
||||
anyPending = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for(uint8_t m = 0; m < chunk->meshCount; m++) {
|
||||
if(chunk->meshEntries[m] == NULL) continue;
|
||||
if(chunk->meshEntries[m]->state != ASSET_ENTRY_STATE_LOADED) {
|
||||
anyPending = true;
|
||||
break;
|
||||
glm_vec3_copy(
|
||||
chunk->dcfEntry->data.chunk.meshOffsets[m],
|
||||
chunk->meshOffsets[m]
|
||||
);
|
||||
vec3 pos;
|
||||
glm_vec3_add(wpf, chunk->meshOffsets[m], pos);
|
||||
glm_translate_make(chunk->meshModels[m], pos);
|
||||
}
|
||||
assetUnlockEntry(chunk->dcfEntry);
|
||||
chunk->dcfEntry = NULL;
|
||||
chunk->meshCount = meshCount;
|
||||
} else if(state == ASSET_ENTRY_STATE_ERROR) {
|
||||
assetUnlockEntry(chunk->dcfEntry);
|
||||
chunk->dcfEntry = NULL;
|
||||
memorySet(chunk->tiles, TILE_SHAPE_GROUND, sizeof(chunk->tiles));
|
||||
} else {
|
||||
anyPending = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(anyPending) usleep(1000);
|
||||
} while(anyPending);
|
||||
|
||||
// Phase 2: Load model entries one at a time.
|
||||
// Models are locked and waited on sequentially so that only one model
|
||||
// entry occupies a loading slot at a time. This leaves the remaining
|
||||
// slots free for the model's mesh and texture sub-assets, preventing
|
||||
// the slot-exhaustion deadlock that occurs when multiple models are
|
||||
// assigned slots simultaneously and each blocks waiting for sub-assets
|
||||
// that have nowhere to run.
|
||||
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
||||
chunk_t *chunk = &MAP.chunks[i];
|
||||
for(uint8_t m = 0; m < chunk->meshCount; m++) {
|
||||
if(chunk->modelEntries[m] != NULL) continue;
|
||||
chunk->modelEntries[m] = assetLock(
|
||||
chunk->modelNames[m], ASSET_LOADER_TYPE_MODEL, NULL
|
||||
);
|
||||
if(chunk->modelEntries[m] == NULL) {
|
||||
errorThrow("Failed to lock model: %s", chunk->modelNames[m]);
|
||||
}
|
||||
errorChain(assetRequireLoaded(chunk->modelEntries[m]));
|
||||
}
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,27 +24,8 @@
|
||||
#include "asset/loader/assetloader.h"
|
||||
#include "asset/loader/assetentry.h"
|
||||
|
||||
#define TEXTURE_CHUNK_SIZE 16
|
||||
|
||||
static texture_t TEXTURE_CHUNK;
|
||||
static color_t TEXTURE_CHUNK_PIXELS[TEXTURE_CHUNK_SIZE * TEXTURE_CHUNK_SIZE];
|
||||
|
||||
errorret_t sceneOverworldInit(scenedata_t *sceneData) {
|
||||
assertNotNull(sceneData, "Scene data cannot be null");
|
||||
|
||||
for(uint32_t i = 0; i < TEXTURE_CHUNK_SIZE * TEXTURE_CHUNK_SIZE; i++) {
|
||||
uint8_t r = (uint8_t)((i & 7) * 36);
|
||||
uint8_t g = (uint8_t)(((i >> 3) & 7) * 36);
|
||||
uint8_t b = (uint8_t)((i >> 6) * 85);
|
||||
TEXTURE_CHUNK_PIXELS[i] = color4b(r, g, b, 255);
|
||||
}
|
||||
errorChain(textureInit(
|
||||
&TEXTURE_CHUNK,
|
||||
TEXTURE_CHUNK_SIZE, TEXTURE_CHUNK_SIZE,
|
||||
TEXTURE_FORMAT_RGBA,
|
||||
(texturedata_t){ .rgbaColors = TEXTURE_CHUNK_PIXELS }
|
||||
));
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -138,25 +119,31 @@ errorret_t sceneOverworldRender(scenedata_t *sceneData) {
|
||||
}
|
||||
|
||||
errorret_t sceneOverworldDrawChunks() {
|
||||
shadermaterial_t chunkMaterial = {
|
||||
.unlit = {
|
||||
.color = COLOR_WHITE,
|
||||
.texture = &TEXTURE_CHUNK
|
||||
}
|
||||
};
|
||||
|
||||
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
||||
chunk_t *chunk = &MAP.chunks[i];
|
||||
if(chunk->meshCount == 0) continue;
|
||||
|
||||
for(uint8_t m = 0; m < chunk->meshCount; m++) {
|
||||
if(chunk->meshEntries[m] == NULL) continue;
|
||||
if(chunk->modelEntries[m] == NULL) continue;
|
||||
if(chunk->modelEntries[m]->state != ASSET_ENTRY_STATE_LOADED) continue;
|
||||
|
||||
assetmodeloutput_t *model = &chunk->modelEntries[m]->data.model;
|
||||
if(model->meshEntry == NULL) continue;
|
||||
|
||||
texture_t *tex = model->texEntry != NULL
|
||||
? &model->texEntry->data.texture
|
||||
: NULL;
|
||||
|
||||
shadermaterial_t mat = {
|
||||
.unlit = { .color = model->color, .texture = tex }
|
||||
};
|
||||
errorChain(shaderSetMatrix(
|
||||
&SHADER_UNLIT, SHADER_UNLIT_MODEL, chunk->meshModels[m]
|
||||
));
|
||||
errorChain(shaderSetMaterial(&SHADER_UNLIT, &chunkMaterial));
|
||||
errorChain(meshDraw(&chunk->meshEntries[m]->data.mesh.mesh, 0, -1));
|
||||
errorChain(shaderSetMaterial(&SHADER_UNLIT, &mat));
|
||||
errorChain(meshDraw(
|
||||
&model->meshEntry->data.mesh.mesh, 0, -1
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,8 +158,5 @@ errorret_t sceneOverworldDrawChunks() {
|
||||
|
||||
errorret_t sceneOverworldDispose(scenedata_t *sceneData) {
|
||||
assertNotNull(sceneData, "Scene data cannot be null");
|
||||
|
||||
errorChain(textureDispose(&TEXTURE_CHUNK));
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user