Asset refactor
This commit is contained in:
@@ -8,6 +8,8 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
asset.c
|
||||
assetfile.c
|
||||
assetcache.c
|
||||
assetbatch.c
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
|
||||
@@ -16,11 +16,12 @@ asset_t ASSET;
|
||||
|
||||
errorret_t assetInit(void) {
|
||||
memoryZero(&ASSET, sizeof(asset_t));
|
||||
|
||||
|
||||
// assetInitPlatform must either define ASSET.zip or throw an error.
|
||||
errorChain(assetInitPlatform());
|
||||
assertNotNull(ASSET.zip, "Asset zip null without error.");
|
||||
|
||||
assetCacheInit(&ASSET.cache);
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -50,13 +51,15 @@ errorret_t assetLoad(
|
||||
}
|
||||
|
||||
errorret_t assetDispose(void) {
|
||||
assetCacheDispose(&ASSET.cache);
|
||||
|
||||
if(ASSET.zip != NULL) {
|
||||
if(zip_close(ASSET.zip) != 0) {
|
||||
errorThrow("Failed to close asset zip archive.");
|
||||
}
|
||||
ASSET.zip = NULL;
|
||||
}
|
||||
|
||||
|
||||
errorChain(assetDisposePlatform());
|
||||
errorOk();
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "error/error.h"
|
||||
#include "asset/assetplatform.h"
|
||||
#include "assetfile.h"
|
||||
#include "assetcache.h"
|
||||
|
||||
#ifndef assetInitPlatform
|
||||
#error "Platform must define assetInitPlatform function."
|
||||
@@ -23,6 +24,7 @@
|
||||
typedef struct asset_s {
|
||||
zip_t *zip;
|
||||
assetplatform_t platform;
|
||||
assetcache_t cache;
|
||||
} asset_t;
|
||||
|
||||
extern asset_t ASSET;
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "assetbatch.h"
|
||||
#include "asset.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/string.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
void assetBatchInit(
|
||||
assetbatch_t *batch,
|
||||
assetbatchcomplete_t onComplete,
|
||||
assetbatcherror_t onError,
|
||||
void *context
|
||||
) {
|
||||
memoryZero(batch, sizeof(assetbatch_t));
|
||||
batch->onComplete = onComplete;
|
||||
batch->onError = onError;
|
||||
batch->context = context;
|
||||
}
|
||||
|
||||
void assetBatchAdd(
|
||||
assetbatch_t *batch,
|
||||
const char_t *path,
|
||||
assetfileloader_t loader,
|
||||
const void *params,
|
||||
size_t paramsSize,
|
||||
size_t dataSize,
|
||||
assetcachedisposer_t disposer,
|
||||
void **outPtr
|
||||
) {
|
||||
assertTrue(batch->count < ASSET_BATCH_MAX, "Asset batch is full.");
|
||||
assertTrue(paramsSize <= ASSET_BATCH_PARAMS_SIZE, "Asset batch params too large.");
|
||||
assertNotNull(outPtr, "Batch output pointer cannot be NULL.");
|
||||
|
||||
assetbatchitem_t *item = &batch->items[batch->count++];
|
||||
stringCopy(item->path, path, ASSET_FILE_NAME_MAX);
|
||||
item->loader = loader;
|
||||
if (params != NULL && paramsSize > 0) {
|
||||
memoryCopy(item->params, params, paramsSize);
|
||||
}
|
||||
item->dataSize = dataSize;
|
||||
item->disposer = disposer;
|
||||
item->outPtr = outPtr;
|
||||
}
|
||||
|
||||
errorret_t assetBatchLoad(assetbatch_t *batch) {
|
||||
for (uint8_t i = 0; i < batch->count; i++) {
|
||||
assetbatchitem_t *item = &batch->items[i];
|
||||
|
||||
void *cached = assetCacheLookup(&ASSET.cache, item->path);
|
||||
if (cached != NULL) {
|
||||
assetCacheRetain(&ASSET.cache, item->path);
|
||||
*item->outPtr = cached;
|
||||
continue;
|
||||
}
|
||||
|
||||
void *data = memoryAllocate(item->dataSize);
|
||||
errorret_t err = assetLoad(item->path, item->loader, item->params, data);
|
||||
if (err.code != ERROR_OK) {
|
||||
memoryFree(data);
|
||||
if (batch->onError != NULL) {
|
||||
batch->onError(batch, batch->context, err);
|
||||
}
|
||||
return errorChainImpl(err, __FILE__, __func__, __LINE__);
|
||||
}
|
||||
|
||||
assetCacheInsert(&ASSET.cache, item->path, data, item->disposer);
|
||||
*item->outPtr = data;
|
||||
}
|
||||
|
||||
if (batch->onComplete != NULL) {
|
||||
batch->onComplete(batch, batch->context);
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "assetcache.h"
|
||||
#include "error/error.h"
|
||||
|
||||
#ifndef ASSET_BATCH_MAX
|
||||
#define ASSET_BATCH_MAX 16
|
||||
#endif
|
||||
|
||||
#define ASSET_BATCH_PARAMS_SIZE 16
|
||||
|
||||
typedef struct assetbatch_s assetbatch_t;
|
||||
|
||||
typedef void (*assetbatchcomplete_t)(assetbatch_t *batch, void *context);
|
||||
typedef void (*assetbatcherror_t)(
|
||||
assetbatch_t *batch,
|
||||
void *context,
|
||||
errorret_t error
|
||||
);
|
||||
|
||||
typedef struct {
|
||||
char_t path[ASSET_FILE_NAME_MAX];
|
||||
assetfileloader_t loader;
|
||||
uint8_t params[ASSET_BATCH_PARAMS_SIZE];
|
||||
size_t dataSize;
|
||||
assetcachedisposer_t disposer;
|
||||
void **outPtr;
|
||||
} assetbatchitem_t;
|
||||
|
||||
struct assetbatch_s {
|
||||
assetbatchitem_t items[ASSET_BATCH_MAX];
|
||||
uint8_t count;
|
||||
assetbatchcomplete_t onComplete;
|
||||
assetbatcherror_t onError;
|
||||
void *context;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes a batch, optionally with completion and error callbacks.
|
||||
*
|
||||
* @param batch The batch to initialize.
|
||||
* @param onComplete Called when all items have loaded successfully. May be NULL.
|
||||
* @param onError Called if any item fails to load. May be NULL.
|
||||
* @param context Passed through to both callbacks.
|
||||
*/
|
||||
void assetBatchInit(
|
||||
assetbatch_t *batch,
|
||||
assetbatchcomplete_t onComplete,
|
||||
assetbatcherror_t onError,
|
||||
void *context
|
||||
);
|
||||
|
||||
/**
|
||||
* Adds an item to the batch. Params are copied into inline storage.
|
||||
*
|
||||
* @param batch The batch to add to.
|
||||
* @param path Asset path, used as the cache key.
|
||||
* @param loader The loader function for this asset type.
|
||||
* @param params Loader-specific parameters. Copied — safe to pass stack address.
|
||||
* @param paramsSize Size of params in bytes. Must be <= ASSET_BATCH_PARAMS_SIZE.
|
||||
* @param dataSize Size in bytes to allocate for the output struct.
|
||||
* @param disposer Called on the data before freeing when released. May be NULL.
|
||||
* @param outPtr Caller's pointer variable. Set to the cache-owned data on load.
|
||||
*/
|
||||
void assetBatchAdd(
|
||||
assetbatch_t *batch,
|
||||
const char_t *path,
|
||||
assetfileloader_t loader,
|
||||
const void *params,
|
||||
size_t paramsSize,
|
||||
size_t dataSize,
|
||||
assetcachedisposer_t disposer,
|
||||
void **outPtr
|
||||
);
|
||||
|
||||
/**
|
||||
* Loads all items in the batch, checking the cache for each. Fires onComplete
|
||||
* on success or onError on the first failure. Also returns the error for
|
||||
* synchronous callers.
|
||||
*
|
||||
* @param batch The batch to execute.
|
||||
* @return Error if any item failed to load.
|
||||
*/
|
||||
errorret_t assetBatchLoad(assetbatch_t *batch);
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "assetcache.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/string.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
void assetCacheInit(assetcache_t *cache) {
|
||||
memoryZero(cache, sizeof(assetcache_t));
|
||||
}
|
||||
|
||||
void *assetCacheLookup(assetcache_t *cache, const char_t *path) {
|
||||
for (uint8_t i = 0; i < cache->count; i++) {
|
||||
if (stringCompare(cache->entries[i].path, path) == 0) {
|
||||
return cache->entries[i].data;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void assetCacheInsert(
|
||||
assetcache_t *cache,
|
||||
const char_t *path,
|
||||
void *data,
|
||||
assetcachedisposer_t disposer
|
||||
) {
|
||||
assertTrue(cache->count < ASSET_CACHE_MAX, "Asset cache is full.");
|
||||
|
||||
assetcacheentry_t *entry = &cache->entries[cache->count++];
|
||||
stringCopy(entry->path, path, ASSET_FILE_NAME_MAX);
|
||||
entry->data = data;
|
||||
entry->refcount = 1;
|
||||
entry->disposer = disposer;
|
||||
}
|
||||
|
||||
void assetCacheRetain(assetcache_t *cache, const char_t *path) {
|
||||
for (uint8_t i = 0; i < cache->count; i++) {
|
||||
if (stringCompare(cache->entries[i].path, path) == 0) {
|
||||
cache->entries[i].refcount++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
assertTrue(false, "Asset not found in cache for retain.");
|
||||
}
|
||||
|
||||
static void assetCacheEntryDispose(assetcacheentry_t *entry) {
|
||||
if (entry->disposer != NULL) {
|
||||
entry->disposer(entry->data);
|
||||
}
|
||||
memoryFree(entry->data);
|
||||
}
|
||||
|
||||
void assetCacheRelease(assetcache_t *cache, const char_t *path) {
|
||||
for (uint8_t i = 0; i < cache->count; i++) {
|
||||
if (stringCompare(cache->entries[i].path, path) == 0) {
|
||||
assetcacheentry_t *entry = &cache->entries[i];
|
||||
entry->refcount--;
|
||||
if (entry->refcount == 0) {
|
||||
assetCacheEntryDispose(entry);
|
||||
for (uint8_t j = i; j < cache->count - 1; j++) {
|
||||
cache->entries[j] = cache->entries[j + 1];
|
||||
}
|
||||
cache->count--;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
assertTrue(false, "Asset not found in cache for release.");
|
||||
}
|
||||
|
||||
void assetCacheDispose(assetcache_t *cache) {
|
||||
for (uint8_t i = 0; i < cache->count; i++) {
|
||||
assetCacheEntryDispose(&cache->entries[i]);
|
||||
}
|
||||
memoryZero(cache, sizeof(assetcache_t));
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "assetfile.h"
|
||||
|
||||
#ifndef ASSET_CACHE_MAX
|
||||
#define ASSET_CACHE_MAX 64
|
||||
#endif
|
||||
|
||||
typedef void (*assetcachedisposer_t)(void *data);
|
||||
|
||||
typedef struct {
|
||||
char_t path[ASSET_FILE_NAME_MAX];
|
||||
void *data;
|
||||
uint32_t refcount;
|
||||
assetcachedisposer_t disposer;
|
||||
} assetcacheentry_t;
|
||||
|
||||
typedef struct {
|
||||
assetcacheentry_t entries[ASSET_CACHE_MAX];
|
||||
uint8_t count;
|
||||
} assetcache_t;
|
||||
|
||||
/**
|
||||
* Initializes the asset cache.
|
||||
*
|
||||
* @param cache The cache to initialize.
|
||||
*/
|
||||
void assetCacheInit(assetcache_t *cache);
|
||||
|
||||
/**
|
||||
* Looks up a cached asset by path.
|
||||
*
|
||||
* @param cache The cache to search.
|
||||
* @param path The asset path to look up.
|
||||
* @return Pointer to the cached data, or NULL if not found.
|
||||
*/
|
||||
void *assetCacheLookup(assetcache_t *cache, const char_t *path);
|
||||
|
||||
/**
|
||||
* Inserts a newly loaded asset into the cache with refcount 1.
|
||||
*
|
||||
* @param cache The cache to insert into.
|
||||
* @param path The asset path key.
|
||||
* @param data Heap-allocated asset data. The cache takes ownership.
|
||||
* @param disposer Called with data before freeing when refcount reaches 0.
|
||||
*/
|
||||
void assetCacheInsert(
|
||||
assetcache_t *cache,
|
||||
const char_t *path,
|
||||
void *data,
|
||||
assetcachedisposer_t disposer
|
||||
);
|
||||
|
||||
/**
|
||||
* Increments the refcount for a cached asset.
|
||||
*
|
||||
* @param cache The cache containing the asset.
|
||||
* @param path The asset path.
|
||||
*/
|
||||
void assetCacheRetain(assetcache_t *cache, const char_t *path);
|
||||
|
||||
/**
|
||||
* Decrements the refcount. Disposes and frees the asset when it reaches 0.
|
||||
*
|
||||
* @param cache The cache containing the asset.
|
||||
* @param path The asset path.
|
||||
*/
|
||||
void assetCacheRelease(assetcache_t *cache, const char_t *path);
|
||||
|
||||
/**
|
||||
* Disposes all remaining cache entries and resets the cache.
|
||||
*
|
||||
* @param cache The cache to dispose.
|
||||
*/
|
||||
void assetCacheDispose(assetcache_t *cache);
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "assettextureloader.h"
|
||||
#include "asset/assetbatch.h"
|
||||
#include "assert/assert.h"
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
@@ -112,4 +113,26 @@ errorret_t assetTextureLoad(
|
||||
.format = format
|
||||
};
|
||||
return assetLoad(path, assetTextureLoader, ¶ms, out);
|
||||
}
|
||||
|
||||
static void assetTextureCacheDispose(void *data) {
|
||||
errorCatch(textureDispose((texture_t*)data));
|
||||
}
|
||||
|
||||
void assetBatchTexture(
|
||||
assetbatch_t *batch,
|
||||
const char_t *path,
|
||||
textureformat_t format,
|
||||
texture_t **out
|
||||
) {
|
||||
assettextureloaderparams_t params = { .format = format };
|
||||
assetBatchAdd(
|
||||
batch,
|
||||
path,
|
||||
assetTextureLoader,
|
||||
¶ms, sizeof(params),
|
||||
sizeof(texture_t),
|
||||
assetTextureCacheDispose,
|
||||
(void**)out
|
||||
);
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
#include "asset/asset.h"
|
||||
#include "asset/assetbatch.h"
|
||||
#include "display/texture/texture.h"
|
||||
|
||||
typedef struct {
|
||||
@@ -47,4 +48,20 @@ errorret_t assetTextureLoad(
|
||||
const char_t *path,
|
||||
texture_t *out,
|
||||
const textureformat_t format
|
||||
);
|
||||
|
||||
/**
|
||||
* Adds a texture load request to a batch. The cache-owned texture_t* is
|
||||
* written to *out when the batch completes.
|
||||
*
|
||||
* @param batch The batch to add to.
|
||||
* @param path Path to the texture asset.
|
||||
* @param format Format of the texture to load.
|
||||
* @param out Receives a pointer to the loaded texture on completion.
|
||||
*/
|
||||
void assetBatchTexture(
|
||||
assetbatch_t *batch,
|
||||
const char_t *path,
|
||||
textureformat_t format,
|
||||
texture_t **out
|
||||
);
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "assettilesetloader.h"
|
||||
#include "asset/assetbatch.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/endian.h"
|
||||
@@ -77,4 +78,20 @@ errorret_t assetTilesetLoad(
|
||||
tileset_t *out
|
||||
) {
|
||||
return assetLoad(path, assetTilesetLoader, NULL, out);
|
||||
}
|
||||
|
||||
void assetBatchTileset(
|
||||
assetbatch_t *batch,
|
||||
const char_t *path,
|
||||
tileset_t **out
|
||||
) {
|
||||
assetBatchAdd(
|
||||
batch,
|
||||
path,
|
||||
assetTilesetLoader,
|
||||
NULL, 0,
|
||||
sizeof(tileset_t),
|
||||
NULL,
|
||||
(void**)out
|
||||
);
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
#include "asset/asset.h"
|
||||
#include "asset/assetbatch.h"
|
||||
#include "display/texture/tileset.h"
|
||||
|
||||
/**
|
||||
@@ -27,4 +28,18 @@ errorret_t assetTilesetLoader(assetfile_t *file);
|
||||
errorret_t assetTilesetLoad(
|
||||
const char_t *path,
|
||||
tileset_t *out
|
||||
);
|
||||
|
||||
/**
|
||||
* Adds a tileset load request to a batch. The cache-owned tileset_t* is
|
||||
* written to *out when the batch completes.
|
||||
*
|
||||
* @param batch The batch to add to.
|
||||
* @param path Path to the tileset asset.
|
||||
* @param out Receives a pointer to the loaded tileset on completion.
|
||||
*/
|
||||
void assetBatchTileset(
|
||||
assetbatch_t *batch,
|
||||
const char_t *path,
|
||||
tileset_t **out
|
||||
);
|
||||
@@ -67,7 +67,7 @@ errorret_t consoleDraw(void) {
|
||||
|
||||
for(uint32_t i = 0; i < CONSOLE_HISTORY_MAX; i++) {
|
||||
errorChain(textDraw(
|
||||
0, FONT_DEFAULT.tileset.tileHeight * i,
|
||||
0, FONT_DEFAULT.tileset->tileHeight * i,
|
||||
CONSOLE.line[i],
|
||||
COLOR_WHITE,
|
||||
&FONT_DEFAULT
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
#include "display/texture/tileset.h"
|
||||
|
||||
typedef struct {
|
||||
texture_t texture;
|
||||
tileset_t tileset;
|
||||
texture_t *texture;
|
||||
tileset_t *tileset;
|
||||
} font_t;
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
#include "asset/asset.h"
|
||||
#include "asset/assetbatch.h"
|
||||
#include "asset/loader/display/assettextureloader.h"
|
||||
#include "asset/loader/display/assettilesetloader.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
@@ -16,15 +18,19 @@
|
||||
font_t FONT_DEFAULT;
|
||||
|
||||
errorret_t textInit(void) {
|
||||
errorChain(assetTextureLoad(
|
||||
"ui/minogram.png", &FONT_DEFAULT.texture, TEXTURE_FORMAT_RGBA
|
||||
));
|
||||
errorChain(assetTilesetLoad("ui/minogram.dtf", &FONT_DEFAULT.tileset));
|
||||
assetbatch_t batch;
|
||||
assetBatchInit(&batch, NULL, NULL, NULL);
|
||||
assetBatchTexture(&batch, "ui/minogram.png", TEXTURE_FORMAT_RGBA, &FONT_DEFAULT.texture);
|
||||
assetBatchTileset(&batch, "ui/minogram.dtf", &FONT_DEFAULT.tileset);
|
||||
errorChain(assetBatchLoad(&batch));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t textDispose(void) {
|
||||
errorChain(textureDispose(&FONT_DEFAULT.texture));
|
||||
assetCacheRelease(&ASSET.cache, "ui/minogram.png");
|
||||
assetCacheRelease(&ASSET.cache, "ui/minogram.dtf");
|
||||
FONT_DEFAULT.texture = NULL;
|
||||
FONT_DEFAULT.tileset = NULL;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -38,21 +44,21 @@ errorret_t textDrawChar(
|
||||
font_t *font
|
||||
) {
|
||||
int32_t tileIndex = (int32_t)(c) - TEXT_CHAR_START;
|
||||
if(tileIndex < 0 || tileIndex >= font->tileset.tileCount) {
|
||||
if(tileIndex < 0 || tileIndex >= font->tileset->tileCount) {
|
||||
tileIndex = ((int32_t)'@') - TEXT_CHAR_START;
|
||||
}
|
||||
assertTrue(
|
||||
tileIndex >= 0 && tileIndex <= font->tileset.tileCount,
|
||||
tileIndex >= 0 && tileIndex <= font->tileset->tileCount,
|
||||
"Character is out of bounds for font tiles"
|
||||
);
|
||||
|
||||
vec4 uv;
|
||||
tilesetTileGetUV(&font->tileset, tileIndex, uv);
|
||||
tilesetTileGetUV(font->tileset, tileIndex, uv);
|
||||
|
||||
errorChain(spriteBatchPush(
|
||||
x, y,
|
||||
x + font->tileset.tileWidth,
|
||||
y + font->tileset.tileHeight,
|
||||
x + font->tileset->tileWidth,
|
||||
y + font->tileset->tileHeight,
|
||||
#if MESH_ENABLE_COLOR
|
||||
color,
|
||||
#endif
|
||||
@@ -73,7 +79,7 @@ errorret_t textDraw(
|
||||
float_t posX = x;
|
||||
float_t posY = y;
|
||||
|
||||
errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, &font->texture));
|
||||
errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, font->texture));
|
||||
|
||||
#if MESH_ENABLE_COLOR
|
||||
#else
|
||||
@@ -85,12 +91,12 @@ errorret_t textDraw(
|
||||
while((c = text[i++]) != '\0') {
|
||||
if(c == '\n') {
|
||||
posX = x;
|
||||
posY += font->tileset.tileHeight;
|
||||
posY += font->tileset->tileHeight;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(c == ' ') {
|
||||
posX += font->tileset.tileWidth;
|
||||
posX += font->tileset->tileWidth;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -101,7 +107,7 @@ errorret_t textDraw(
|
||||
#endif
|
||||
font
|
||||
));
|
||||
posX += font->tileset.tileWidth;
|
||||
posX += font->tileset->tileWidth;
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
@@ -117,7 +123,7 @@ void textMeasure(
|
||||
assertNotNull(outHeight, "Output height pointer cannot be NULL");
|
||||
|
||||
int32_t width = 0;
|
||||
int32_t height = font->tileset.tileHeight;
|
||||
int32_t height = font->tileset->tileHeight;
|
||||
int32_t lineWidth = 0;
|
||||
|
||||
char_t c;
|
||||
@@ -126,11 +132,11 @@ void textMeasure(
|
||||
if(c == '\n') {
|
||||
if(lineWidth > width) width = lineWidth;
|
||||
lineWidth = 0;
|
||||
height += font->tileset.tileHeight;
|
||||
height += font->tileset->tileHeight;
|
||||
continue;
|
||||
}
|
||||
|
||||
lineWidth += font->tileset.tileWidth;
|
||||
lineWidth += font->tileset->tileWidth;
|
||||
}
|
||||
|
||||
if(lineWidth > width) width = lineWidth;
|
||||
|
||||
@@ -26,8 +26,6 @@
|
||||
#include "item/backpack.h"
|
||||
#include "save/save.h"
|
||||
|
||||
#include "scene/initial/initialscene.h"
|
||||
|
||||
engine_t ENGINE;
|
||||
|
||||
errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
||||
@@ -57,7 +55,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
||||
|
||||
/* Run the init script. */
|
||||
consolePrint("Engine initialized");
|
||||
sceneSet(SCENE_TYPE_INITIAL);
|
||||
sceneSet(SCENE_TYPE_OVERWORLD);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ void entityCameraInit(const entityid_t ent, const componentid_t comp) {
|
||||
ent, comp, COMPONENT_TYPE_CAMERA
|
||||
);
|
||||
cam->nearClip = 0.1f;
|
||||
cam->farClip = 100.0f;
|
||||
cam->farClip = 5000.0f;
|
||||
cam->projType = ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE;
|
||||
cam->perspective.fov = glm_rad(45.0f);
|
||||
}
|
||||
@@ -83,6 +83,30 @@ void entityCameraGetForward(const entityid_t entityId, vec2 out) {
|
||||
out[1] = fz;
|
||||
}
|
||||
|
||||
void entityCameraLookAtPixelPerfect(
|
||||
const entityid_t ent,
|
||||
const componentid_t posComp,
|
||||
const componentid_t camComp,
|
||||
const vec3 point,
|
||||
const vec3 eyeOffset,
|
||||
const float_t scale
|
||||
) {
|
||||
entitycamera_t *cam = (entitycamera_t *)componentGetData(
|
||||
ent, camComp, COMPONENT_TYPE_CAMERA
|
||||
);
|
||||
float_t dist = (
|
||||
(float_t)SCREEN.height / (2.0f * scale * tanf(cam->perspective.fov * 0.5f))
|
||||
);
|
||||
|
||||
vec3 eye = {
|
||||
point[0] + eyeOffset[0],
|
||||
point[1] + dist + eyeOffset[1],
|
||||
point[2] + eyeOffset[2]
|
||||
};
|
||||
vec3 up = { 0.0f, 0.0f, -1.0f };
|
||||
entityPositionLookAt(ent, posComp, eye, (float_t *)point, up);
|
||||
}
|
||||
|
||||
void entityCameraGetRight(const entityid_t entityId, vec2 out) {
|
||||
componentid_t posComp = entityGetComponent(entityId, COMPONENT_TYPE_POSITION);
|
||||
entityposition_t *pos = entityPositionGet(entityId, posComp);
|
||||
|
||||
@@ -76,4 +76,24 @@ void entityCameraGetForward(const entityid_t entityId, vec2 out);
|
||||
* @param entityId The camera entity ID.
|
||||
* @param out Output vec2: {rightX, rightZ} normalized.
|
||||
*/
|
||||
void entityCameraGetRight(const entityid_t entityId, vec2 out);
|
||||
void entityCameraGetRight(const entityid_t entityId, vec2 out);
|
||||
|
||||
/**
|
||||
* Positions the camera to look at a 3D point at a pixel-perfect distance
|
||||
* derived from the camera's FOV and screen height.
|
||||
*
|
||||
* @param ent The camera entity ID.
|
||||
* @param posComp The position component ID.
|
||||
* @param camComp The camera component ID.
|
||||
* @param point World position to look at.
|
||||
* @param eyeOffset Offset added to the eye position only (not the target).
|
||||
* @param scale Pixels per world unit. 1.0 = pixel perfect, 2.0 = 2px per unit.
|
||||
*/
|
||||
void entityCameraLookAtPixelPerfect(
|
||||
const entityid_t ent,
|
||||
const componentid_t posComp,
|
||||
const componentid_t camComp,
|
||||
const vec3 point,
|
||||
const vec3 eyeOffset,
|
||||
const float_t scale
|
||||
);
|
||||
@@ -61,4 +61,4 @@ void entityRenderableSetDraw(
|
||||
errorret_t entityRenderableDraw(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
);
|
||||
);
|
||||
|
||||
@@ -10,4 +10,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
)
|
||||
|
||||
# Subdirectories
|
||||
add_subdirectory(initial)
|
||||
add_subdirectory(initial)
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(overworld)
|
||||
@@ -7,118 +7,14 @@
|
||||
|
||||
#include "initialscene.h"
|
||||
#include "console/console.h"
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
#include "display/screen/screen.h"
|
||||
#include "display/text/text.h"
|
||||
#include "entity/entitymanager.h"
|
||||
#include "input/input.h"
|
||||
|
||||
#define INITIAL_SCENE_TEST_ENTITY_MAX (ENTITY_COUNT_MAX - 1)
|
||||
|
||||
static entityid_t cameraEntityId;
|
||||
static componentid_t cameraCompId;
|
||||
static entityid_t testEntities[INITIAL_SCENE_TEST_ENTITY_MAX];
|
||||
static uint8_t testEntityCount = 0;
|
||||
|
||||
static void initialSceneSpawnTestEntity(void) {
|
||||
if(testEntityCount >= INITIAL_SCENE_TEST_ENTITY_MAX) return;
|
||||
|
||||
entityid_t entity = entityManagerAdd();
|
||||
componentid_t posComp = entityAddComponent(entity, COMPONENT_TYPE_POSITION);
|
||||
(void)entityAddComponent(entity, COMPONENT_TYPE_RENDERABLE);
|
||||
|
||||
const int32_t cols = 20;
|
||||
const float_t spacing = 1.5f;
|
||||
int32_t col = testEntityCount % cols;
|
||||
int32_t row = testEntityCount / cols;
|
||||
vec3 pos = {
|
||||
((float_t)col - (cols - 1) * 0.5f) * spacing,
|
||||
0.0f,
|
||||
(float_t)row * spacing
|
||||
};
|
||||
entityPositionSetLocalPosition(entity, posComp, pos);
|
||||
|
||||
testEntities[testEntityCount++] = entity;
|
||||
}
|
||||
|
||||
static void initialSceneUpdateCamera(void) {
|
||||
if(testEntityCount == 0) return;
|
||||
|
||||
float_t minX = FLT_MAX, maxX = -FLT_MAX;
|
||||
float_t minZ = FLT_MAX, maxZ = -FLT_MAX;
|
||||
|
||||
for(entityid_t i = 0; i < testEntityCount; i++) {
|
||||
componentid_t posComp = entityGetComponent(
|
||||
testEntities[i], COMPONENT_TYPE_POSITION
|
||||
);
|
||||
if(posComp == COMPONENT_ID_INVALID) continue;
|
||||
vec3 pos;
|
||||
entityPositionGetLocalPosition(testEntities[i], posComp, pos);
|
||||
if(pos[0] < minX) minX = pos[0];
|
||||
if(pos[0] > maxX) maxX = pos[0];
|
||||
if(pos[2] < minZ) minZ = pos[2];
|
||||
if(pos[2] > maxZ) maxZ = pos[2];
|
||||
}
|
||||
|
||||
float_t centerX = (minX + maxX) * 0.5f;
|
||||
float_t centerZ = (minZ + maxZ) * 0.5f;
|
||||
float_t extentX = (maxX - minX) * 0.5f + 0.5f;
|
||||
float_t extentZ = (maxZ - minZ) * 0.5f + 0.5f;
|
||||
float_t extent = extentX > extentZ ? extentX : extentZ;
|
||||
float_t dist = extent * 1.5f + 2.0f;
|
||||
|
||||
vec3 target = { centerX, 0.0f, centerZ };
|
||||
vec3 eye = { centerX + dist, dist, centerZ + dist };
|
||||
vec3 up = { 0.0f, 1.0f, 0.0f };
|
||||
entityPositionLookAt(cameraEntityId, cameraCompId, eye, target, up);
|
||||
}
|
||||
|
||||
void initialSceneInit(void) {
|
||||
consolePrint("Initial scene initialized");
|
||||
testEntityCount = 0;
|
||||
|
||||
cameraEntityId = entityManagerAdd();
|
||||
cameraCompId = entityAddComponent(cameraEntityId, COMPONENT_TYPE_POSITION);
|
||||
(void)entityAddComponent(cameraEntityId, COMPONENT_TYPE_CAMERA);
|
||||
|
||||
vec3 eye, target, up;
|
||||
glm_vec3_zero(target);
|
||||
glm_vec3_copy((vec3){ 3.0f, 3.0f, 3.0f }, eye);
|
||||
glm_vec3_copy((vec3){ 0.0f, 1.0f, 0.0f }, up);
|
||||
entityPositionLookAt(cameraEntityId, cameraCompId, eye, target, up);
|
||||
|
||||
for(int i = 0; i < 5; i++) initialSceneSpawnTestEntity();
|
||||
}
|
||||
|
||||
errorret_t initialSceneUpdate(void) {
|
||||
if(inputPressed(INPUT_ACTION_ACCEPT)) {
|
||||
for(int i = 0; i < 5; i++) initialSceneSpawnTestEntity();
|
||||
}
|
||||
|
||||
if(inputPressed(INPUT_ACTION_CANCEL)) {
|
||||
for(int i = 0; i < 5 && testEntityCount > 0; i++) {
|
||||
entityDispose(testEntities[--testEntityCount]);
|
||||
}
|
||||
}
|
||||
|
||||
initialSceneUpdateCamera();
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t initialSceneEntityCountDraw(void) {
|
||||
char_t buf[32];
|
||||
snprintf(buf, sizeof(buf), "Entities: %u", (uint32_t)testEntityCount);
|
||||
errorChain(textDraw(
|
||||
0, (float_t)(SCREEN.height - FONT_DEFAULT.tileset.tileHeight),
|
||||
buf, COLOR_WHITE,
|
||||
&FONT_DEFAULT
|
||||
));
|
||||
return spriteBatchFlush();
|
||||
}
|
||||
|
||||
void initialSceneDispose(void) {
|
||||
testEntityCount = 0;
|
||||
cameraEntityId = ENTITY_ID_INVALID;
|
||||
cameraCompId = COMPONENT_ID_INVALID;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
@@ -12,26 +12,6 @@ typedef struct {
|
||||
void *nothing;
|
||||
} initialscene_t;
|
||||
|
||||
/**
|
||||
* Initial scene initializer.
|
||||
*/
|
||||
void initialSceneInit(void);
|
||||
|
||||
/**
|
||||
* Initial scene updater.
|
||||
*
|
||||
* @return Any error state that happened.
|
||||
*/
|
||||
errorret_t initialSceneUpdate(void);
|
||||
|
||||
/**
|
||||
* Initial scene disposer.
|
||||
*/
|
||||
void initialSceneDispose(void);
|
||||
|
||||
/**
|
||||
* Draws the entity count UI label for the initial scene.
|
||||
*
|
||||
* @return Any error state that happened.
|
||||
*/
|
||||
errorret_t initialSceneEntityCountDraw(void);
|
||||
@@ -0,0 +1,11 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
overworldground.c
|
||||
overworldplayer.c
|
||||
overworldscene.c
|
||||
)
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "overworldground.h"
|
||||
#include "entity/entitymanager.h"
|
||||
#include "entity/component/display/entityrenderable.h"
|
||||
#include "entity/component/physics/entityphysics.h"
|
||||
#include "display/mesh/plane.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
|
||||
#define OVERWORLD_GROUND_SIZE 20.0f
|
||||
|
||||
static errorret_t overworldGroundDraw(void) {
|
||||
errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, COLOR_MAGENTA));
|
||||
errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, NULL));
|
||||
return meshDraw(&PLANE_MESH_SIMPLE, 0, -1);
|
||||
}
|
||||
|
||||
void overworldGroundAdd(overworldground_t *ground) {
|
||||
ground->entityId = entityManagerAdd();
|
||||
ground->posCompId = entityAddComponent(
|
||||
ground->entityId, COMPONENT_TYPE_POSITION
|
||||
);
|
||||
componentid_t renderComp = entityAddComponent(
|
||||
ground->entityId, COMPONENT_TYPE_RENDERABLE
|
||||
);
|
||||
|
||||
vec3 pos = { -OVERWORLD_GROUND_SIZE, 0.0f, -OVERWORLD_GROUND_SIZE };
|
||||
vec3 scale = { OVERWORLD_GROUND_SIZE * 2.0f, 1.0f, OVERWORLD_GROUND_SIZE * 2.0f };
|
||||
entityPositionSetLocalPosition(ground->entityId, ground->posCompId, pos);
|
||||
entityPositionSetLocalScale(ground->entityId, ground->posCompId, scale);
|
||||
entityRenderableSetDraw(ground->entityId, renderComp, overworldGroundDraw);
|
||||
|
||||
ground->physCompId = entityAddComponent(ground->entityId, COMPONENT_TYPE_PHYSICS);
|
||||
entityPhysicsSetBodyType(ground->entityId, ground->physCompId, PHYSICS_BODY_STATIC);
|
||||
physicsshape_t shape = {
|
||||
.type = PHYSICS_SHAPE_PLANE,
|
||||
.data.plane = { .normal = { 0.0f, 1.0f, 0.0f }, .distance = 0.0f }
|
||||
};
|
||||
entityPhysicsSetShape(ground->entityId, ground->physCompId, shape);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "entity/entitybase.h"
|
||||
|
||||
typedef struct {
|
||||
entityid_t entityId;
|
||||
componentid_t posCompId;
|
||||
componentid_t physCompId;
|
||||
} overworldground_t;
|
||||
|
||||
/**
|
||||
* Creates the ground entity and adds it to the world.
|
||||
*
|
||||
* @param ground The ground state to initialize.
|
||||
*/
|
||||
void overworldGroundAdd(overworldground_t *ground);
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "overworldplayer.h"
|
||||
#include "entity/entitymanager.h"
|
||||
#include "entity/component/physics/entityphysics.h"
|
||||
#include "input/input.h"
|
||||
|
||||
#define OVERWORLD_PLAYER_SPEED 4.0f
|
||||
#define OVERWORLD_PLAYER_RUN_SPEED 8.0f
|
||||
|
||||
void overworldPlayerAdd(overworldplayer_t *player) {
|
||||
player->entityId = entityManagerAdd();
|
||||
player->posCompId = entityAddComponent(player->entityId, COMPONENT_TYPE_POSITION);
|
||||
(void)entityAddComponent(player->entityId, COMPONENT_TYPE_RENDERABLE);
|
||||
player->physCompId = entityAddComponent(player->entityId, COMPONENT_TYPE_PHYSICS);
|
||||
|
||||
vec3 pos = { 0.0f, 0.5f, 0.0f };
|
||||
entityPositionSetLocalPosition(player->entityId, player->posCompId, pos);
|
||||
|
||||
entityPhysicsSetBodyType(player->entityId, player->physCompId, PHYSICS_BODY_DYNAMIC);
|
||||
physicsshape_t shape = {
|
||||
.type = PHYSICS_SHAPE_CAPSULE,
|
||||
.data.capsule = { .radius = 0.4f, .halfHeight = 0.1f }
|
||||
};
|
||||
entityPhysicsSetShape(player->entityId, player->physCompId, shape);
|
||||
}
|
||||
|
||||
void overworldPlayerUpdate(overworldplayer_t *player) {
|
||||
vec2 dir;
|
||||
inputAngle2D(
|
||||
INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT,
|
||||
INPUT_ACTION_UP, INPUT_ACTION_DOWN,
|
||||
dir
|
||||
);
|
||||
|
||||
float_t speed = inputIsDown(INPUT_ACTION_CANCEL)
|
||||
? OVERWORLD_PLAYER_RUN_SPEED
|
||||
: OVERWORLD_PLAYER_SPEED;
|
||||
|
||||
vec3 vel;
|
||||
entityPhysicsGetVelocity(player->entityId, player->physCompId, vel);
|
||||
vel[0] = dir[0] * speed;
|
||||
vel[2] = dir[1] * speed;
|
||||
entityPhysicsSetVelocity(player->entityId, player->physCompId, vel);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "entity/entitybase.h"
|
||||
|
||||
typedef struct {
|
||||
entityid_t entityId;
|
||||
componentid_t posCompId;
|
||||
componentid_t physCompId;
|
||||
} overworldplayer_t;
|
||||
|
||||
/**
|
||||
* Creates the player entity and adds it to the world.
|
||||
*
|
||||
* @param player The player state to initialize.
|
||||
*/
|
||||
void overworldPlayerAdd(overworldplayer_t *player);
|
||||
|
||||
/**
|
||||
* Updates the player entity, reading input and moving on the XZ plane.
|
||||
*
|
||||
* @param player The player state to update.
|
||||
*/
|
||||
void overworldPlayerUpdate(overworldplayer_t *player);
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "overworldscene.h"
|
||||
#include "overworldplayer.h"
|
||||
#include "overworldground.h"
|
||||
#include "console/console.h"
|
||||
#include "entity/entitymanager.h"
|
||||
#include "entity/component/physics/entityphysics.h"
|
||||
#include "scene/scene.h"
|
||||
|
||||
#define OVERWORLD (SCENE.data.overworld)
|
||||
|
||||
void overworldSceneInit(void) {
|
||||
consolePrint("Overworld scene initialized");
|
||||
|
||||
OVERWORLD.cameraEntityId = entityManagerAdd();
|
||||
OVERWORLD.cameraPosCompId = entityAddComponent(
|
||||
OVERWORLD.cameraEntityId, COMPONENT_TYPE_POSITION
|
||||
);
|
||||
OVERWORLD.cameraCamCompId = entityAddComponent(
|
||||
OVERWORLD.cameraEntityId, COMPONENT_TYPE_CAMERA
|
||||
);
|
||||
|
||||
overworldGroundAdd(&OVERWORLD.ground);
|
||||
overworldPlayerAdd(&OVERWORLD.player);
|
||||
|
||||
OVERWORLD.refCubeEntityId = entityManagerAdd();
|
||||
componentid_t refPosComp = entityAddComponent(
|
||||
OVERWORLD.refCubeEntityId, COMPONENT_TYPE_POSITION
|
||||
);
|
||||
(void)entityAddComponent(OVERWORLD.refCubeEntityId, COMPONENT_TYPE_RENDERABLE);
|
||||
vec3 refPos = { 3.0f, 0.5f, 3.0f };
|
||||
entityPositionSetLocalPosition(OVERWORLD.refCubeEntityId, refPosComp, refPos);
|
||||
|
||||
componentid_t refPhysComp = entityAddComponent(
|
||||
OVERWORLD.refCubeEntityId, COMPONENT_TYPE_PHYSICS
|
||||
);
|
||||
entityPhysicsSetBodyType(OVERWORLD.refCubeEntityId, refPhysComp, PHYSICS_BODY_STATIC);
|
||||
physicsshape_t refShape = {
|
||||
.type = PHYSICS_SHAPE_CAPSULE,
|
||||
.data.capsule = { .radius = 0.4f, .halfHeight = 0.1f }
|
||||
};
|
||||
entityPhysicsSetShape(OVERWORLD.refCubeEntityId, refPhysComp, refShape);
|
||||
}
|
||||
|
||||
errorret_t overworldSceneUpdate(void) {
|
||||
overworldPlayerUpdate(&OVERWORLD.player);
|
||||
|
||||
vec3 pos;
|
||||
entityPositionGetLocalPosition(
|
||||
OVERWORLD.player.entityId, OVERWORLD.player.posCompId, pos
|
||||
);
|
||||
vec3 center = { pos[0] + 0.5f, pos[1] + 0.5f, pos[2] + 0.5f };
|
||||
vec3 eyeOffset = { 0.0f, 0.0f, 5.0f };
|
||||
entityCameraLookAtPixelPerfect(
|
||||
OVERWORLD.cameraEntityId,
|
||||
OVERWORLD.cameraPosCompId,
|
||||
OVERWORLD.cameraCamCompId,
|
||||
center, eyeOffset, 32.0f
|
||||
);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void overworldSceneDispose(void) {
|
||||
OVERWORLD.cameraEntityId = ENTITY_ID_INVALID;
|
||||
OVERWORLD.cameraPosCompId = COMPONENT_ID_INVALID;
|
||||
OVERWORLD.cameraCamCompId = COMPONENT_ID_INVALID;
|
||||
OVERWORLD.ground.entityId = ENTITY_ID_INVALID;
|
||||
OVERWORLD.ground.posCompId = COMPONENT_ID_INVALID;
|
||||
OVERWORLD.player.entityId = ENTITY_ID_INVALID;
|
||||
OVERWORLD.player.posCompId = COMPONENT_ID_INVALID;
|
||||
OVERWORLD.refCubeEntityId = ENTITY_ID_INVALID;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 "entity/entitybase.h"
|
||||
#include "scene/overworld/overworldplayer.h"
|
||||
#include "scene/overworld/overworldground.h"
|
||||
|
||||
typedef struct {
|
||||
entityid_t cameraEntityId;
|
||||
componentid_t cameraPosCompId;
|
||||
componentid_t cameraCamCompId;
|
||||
overworldplayer_t player;
|
||||
overworldground_t ground;
|
||||
entityid_t refCubeEntityId;
|
||||
} overworldscene_t;
|
||||
|
||||
void overworldSceneInit(void);
|
||||
errorret_t overworldSceneUpdate(void);
|
||||
void overworldSceneDispose(void);
|
||||
@@ -8,6 +8,8 @@
|
||||
#pragma once
|
||||
#include "asset/assetfile.h"
|
||||
#include "scene/initial/initialscene.h"
|
||||
#include "scene/test/testscene.h"
|
||||
#include "scene/overworld/overworldscene.h"
|
||||
|
||||
#define SCENE_EVENT_UPDATE_MAX 16
|
||||
|
||||
|
||||
@@ -10,4 +10,6 @@
|
||||
((void))
|
||||
#endif
|
||||
|
||||
X(initialscene_t, initial, INITIAL, initialSceneInit, initialSceneUpdate, initialSceneDispose)
|
||||
X(initialscene_t, initial, INITIAL, initialSceneInit, initialSceneUpdate, initialSceneDispose)
|
||||
X(testscene_t, test, TEST, testSceneInit, testSceneUpdate, testSceneDispose)
|
||||
X(overworldscene_t, overworld, OVERWORLD, overworldSceneInit, overworldSceneUpdate, overworldSceneDispose)
|
||||
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
testscene.c
|
||||
)
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "testscene.h"
|
||||
#include "console/console.h"
|
||||
#include "entity/entitymanager.h"
|
||||
#include "input/input.h"
|
||||
|
||||
#define TEST_SCENE_ENTITY_MAX (ENTITY_COUNT_MAX - 1)
|
||||
|
||||
static entityid_t cameraEntityId;
|
||||
static componentid_t cameraCompId;
|
||||
static entityid_t testEntities[TEST_SCENE_ENTITY_MAX];
|
||||
static uint8_t testEntityCount = 0;
|
||||
|
||||
static void testSceneSpawnTestEntity(void) {
|
||||
if(testEntityCount >= TEST_SCENE_ENTITY_MAX) return;
|
||||
|
||||
entityid_t entity = entityManagerAdd();
|
||||
componentid_t posComp = entityAddComponent(entity, COMPONENT_TYPE_POSITION);
|
||||
(void)entityAddComponent(entity, COMPONENT_TYPE_RENDERABLE);
|
||||
|
||||
const int32_t cols = 20;
|
||||
const float_t spacing = 1.5f;
|
||||
int32_t col = testEntityCount % cols;
|
||||
int32_t row = testEntityCount / cols;
|
||||
vec3 pos = {
|
||||
((float_t)col - (cols - 1) * 0.5f) * spacing,
|
||||
0.0f,
|
||||
(float_t)row * spacing
|
||||
};
|
||||
entityPositionSetLocalPosition(entity, posComp, pos);
|
||||
|
||||
testEntities[testEntityCount++] = entity;
|
||||
}
|
||||
|
||||
static void testSceneUpdateCamera(void) {
|
||||
if(testEntityCount == 0) return;
|
||||
|
||||
float_t minX = FLT_MAX, maxX = -FLT_MAX;
|
||||
float_t minZ = FLT_MAX, maxZ = -FLT_MAX;
|
||||
|
||||
for(entityid_t i = 0; i < testEntityCount; i++) {
|
||||
componentid_t posComp = entityGetComponent(
|
||||
testEntities[i], COMPONENT_TYPE_POSITION
|
||||
);
|
||||
if(posComp == COMPONENT_ID_INVALID) continue;
|
||||
vec3 pos;
|
||||
entityPositionGetLocalPosition(testEntities[i], posComp, pos);
|
||||
if(pos[0] < minX) minX = pos[0];
|
||||
if(pos[0] > maxX) maxX = pos[0];
|
||||
if(pos[2] < minZ) minZ = pos[2];
|
||||
if(pos[2] > maxZ) maxZ = pos[2];
|
||||
}
|
||||
|
||||
float_t centerX = (minX + maxX) * 0.5f;
|
||||
float_t centerZ = (minZ + maxZ) * 0.5f;
|
||||
float_t extentX = (maxX - minX) * 0.5f + 0.5f;
|
||||
float_t extentZ = (maxZ - minZ) * 0.5f + 0.5f;
|
||||
float_t extent = extentX > extentZ ? extentX : extentZ;
|
||||
float_t dist = extent * 1.5f + 2.0f;
|
||||
|
||||
vec3 target = { centerX, 0.0f, centerZ };
|
||||
vec3 eye = { centerX + dist, dist, centerZ + dist };
|
||||
vec3 up = { 0.0f, 1.0f, 0.0f };
|
||||
entityPositionLookAt(cameraEntityId, cameraCompId, eye, target, up);
|
||||
}
|
||||
|
||||
void testSceneInit(void) {
|
||||
consolePrint("Test scene initialized");
|
||||
testEntityCount = 0;
|
||||
|
||||
cameraEntityId = entityManagerAdd();
|
||||
cameraCompId = entityAddComponent(cameraEntityId, COMPONENT_TYPE_POSITION);
|
||||
(void)entityAddComponent(cameraEntityId, COMPONENT_TYPE_CAMERA);
|
||||
|
||||
vec3 eye, target, up;
|
||||
glm_vec3_zero(target);
|
||||
glm_vec3_copy((vec3){ 3.0f, 3.0f, 3.0f }, eye);
|
||||
glm_vec3_copy((vec3){ 0.0f, 1.0f, 0.0f }, up);
|
||||
entityPositionLookAt(cameraEntityId, cameraCompId, eye, target, up);
|
||||
|
||||
for(int i = 0; i < 5; i++) testSceneSpawnTestEntity();
|
||||
}
|
||||
|
||||
errorret_t testSceneUpdate(void) {
|
||||
if(inputPressed(INPUT_ACTION_ACCEPT)) {
|
||||
for(int i = 0; i < 5; i++) testSceneSpawnTestEntity();
|
||||
}
|
||||
|
||||
if(inputPressed(INPUT_ACTION_CANCEL)) {
|
||||
for(int i = 0; i < 5 && testEntityCount > 0; i++) {
|
||||
entityDispose(testEntities[--testEntityCount]);
|
||||
}
|
||||
}
|
||||
|
||||
testSceneUpdateCamera();
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void testSceneDispose(void) {
|
||||
testEntityCount = 0;
|
||||
cameraEntityId = ENTITY_ID_INVALID;
|
||||
cameraCompId = COMPONENT_ID_INVALID;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
|
||||
typedef struct {
|
||||
void *nothing;
|
||||
} testscene_t;
|
||||
|
||||
void testSceneInit(void);
|
||||
errorret_t testSceneUpdate(void);
|
||||
void testSceneDispose(void);
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "engine/engine.h"
|
||||
#include "ui/uitextbox.h"
|
||||
#include "ui/uifullbox.h"
|
||||
#include "scene/initial/initialscene.h"
|
||||
|
||||
uielement_t UI_ELEMENTS[] = {
|
||||
// Fullbox under: above scene, below system UI.
|
||||
@@ -23,8 +22,6 @@ uielement_t UI_ELEMENTS[] = {
|
||||
{ .type = UI_ELEMENT_TYPE_NATIVE, .draw = consoleDraw },
|
||||
{ .type = UI_ELEMENT_TYPE_NATIVE, .draw = uiFPSDraw },
|
||||
{ .type = UI_ELEMENT_TYPE_NATIVE, .draw = uiTextboxDraw },
|
||||
{ .type = UI_ELEMENT_TYPE_NATIVE, .draw = initialSceneEntityCountDraw },
|
||||
|
||||
// Fullbox over: above absolutely everything.
|
||||
{ .type = UI_ELEMENT_TYPE_NATIVE, .draw = uiFullboxOverDraw },
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ errorret_t uiTextboxInit(void) {
|
||||
|
||||
UI_TEXTBOX.font = &FONT_DEFAULT;
|
||||
|
||||
float_t fontW = (float_t)FONT_DEFAULT.tileset.tileWidth;
|
||||
float_t fontH = (float_t)FONT_DEFAULT.tileset.tileHeight;
|
||||
float_t fontW = (float_t)FONT_DEFAULT.tileset->tileWidth;
|
||||
float_t fontH = (float_t)FONT_DEFAULT.tileset->tileHeight;
|
||||
float_t tbHeight = (
|
||||
(float_t)UI_TEXTBOX_LINES_PER_PAGE_MAX * fontH +
|
||||
(float_t)(UI_TEXTBOX_LINES_PER_PAGE_MAX - 1) * UI_TEXTBOX_LINE_SPACING +
|
||||
@@ -39,8 +39,8 @@ errorret_t uiTextboxInit(void) {
|
||||
UI_TEXTBOX.frame.tileset.columns = 3;
|
||||
UI_TEXTBOX.frame.tileset.rows = 3;
|
||||
UI_TEXTBOX.frame.tileset.tileCount = 9;
|
||||
UI_TEXTBOX.frame.tileset.tileWidth = FONT_DEFAULT.tileset.tileWidth;
|
||||
UI_TEXTBOX.frame.tileset.tileHeight = FONT_DEFAULT.tileset.tileHeight;
|
||||
UI_TEXTBOX.frame.tileset.tileWidth = FONT_DEFAULT.tileset->tileWidth;
|
||||
UI_TEXTBOX.frame.tileset.tileHeight = FONT_DEFAULT.tileset->tileHeight;
|
||||
UI_TEXTBOX.frame.tileset.uv[0] = 1.0f / 3.0f;
|
||||
UI_TEXTBOX.frame.tileset.uv[1] = 1.0f / 3.0f;
|
||||
UI_TEXTBOX.frame.texture = &TEXTURE_WHITE;
|
||||
@@ -57,8 +57,8 @@ void uiTextboxBuildLayout(void) {
|
||||
|
||||
float_t frameTileW = (float_t)UI_TEXTBOX.frame.tileset.tileWidth;
|
||||
float_t frameTileH = (float_t)UI_TEXTBOX.frame.tileset.tileHeight;
|
||||
float_t fontW = (float_t)UI_TEXTBOX.font->tileset.tileWidth;
|
||||
float_t fontH = (float_t)UI_TEXTBOX.font->tileset.tileHeight;
|
||||
float_t fontW = (float_t)UI_TEXTBOX.font->tileset->tileWidth;
|
||||
float_t fontH = (float_t)UI_TEXTBOX.font->tileset->tileHeight;
|
||||
|
||||
if(fontW <= 0.0f || fontH <= 0.0f) return;
|
||||
|
||||
@@ -217,7 +217,7 @@ errorret_t uiTextboxDraw(void) {
|
||||
errorChain(spriteBatchFlush());
|
||||
|
||||
errorChain(shaderSetTexture(
|
||||
&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, &UI_TEXTBOX.font->texture
|
||||
&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, UI_TEXTBOX.font->texture
|
||||
));
|
||||
#if MESH_ENABLE_COLOR
|
||||
#else
|
||||
@@ -228,8 +228,8 @@ errorret_t uiTextboxDraw(void) {
|
||||
|
||||
float_t frameTileW = (float_t)UI_TEXTBOX.frame.tileset.tileWidth;
|
||||
float_t frameTileH = (float_t)UI_TEXTBOX.frame.tileset.tileHeight;
|
||||
float_t fontW = (float_t)UI_TEXTBOX.font->tileset.tileWidth;
|
||||
float_t fontH = (float_t)UI_TEXTBOX.font->tileset.tileHeight;
|
||||
float_t fontW = (float_t)UI_TEXTBOX.font->tileset->tileWidth;
|
||||
float_t fontH = (float_t)UI_TEXTBOX.font->tileset->tileHeight;
|
||||
float_t contentX = UI_TEXTBOX.x + frameTileW;
|
||||
float_t contentY = UI_TEXTBOX.y + frameTileH;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user