diff --git a/src/dusk/CMakeLists.txt b/src/dusk/CMakeLists.txt index 9cc34b57..9cb16d47 100644 --- a/src/dusk/CMakeLists.txt +++ b/src/dusk/CMakeLists.txt @@ -62,7 +62,6 @@ add_subdirectory(log) add_subdirectory(engine) add_subdirectory(entity) add_subdirectory(error) -add_subdirectory(event) add_subdirectory(input) add_subdirectory(locale) add_subdirectory(physics) diff --git a/src/dusk/event/CMakeLists.txt b/src/dusk/event/CMakeLists.txt deleted file mode 100644 index a9596470..00000000 --- a/src/dusk/event/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - event.c -) \ No newline at end of file diff --git a/src/dusk/event/event.c b/src/dusk/event/event.c deleted file mode 100644 index 52d222ef..00000000 --- a/src/dusk/event/event.c +++ /dev/null @@ -1,239 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "event.h" -#include "assert/assert.h" -#include "util/memory.h" -#include "console/console.h" - -void eventInit( - event_t *event, - eventlistener_t *array, - const uint16_t maxListeners -) { - assertNotNull(event, "Event cannot be NULL"); - assertNotNull(array, "Listener array cannot be NULL"); - assertTrue(maxListeners > 0, "Max listeners must be greater than 0"); - - memoryZero(event, sizeof(event_t)); - - event->listenerArray = array; - event->maxListeners = maxListeners; -} - -eventsub_t eventSubscribeUser( - event_t *event, - const eventtype_t type, - const eventuserdata_t user -) { - assertNotNull(event, "Event cannot be NULL"); - assertTrue( - event->listenerCount < event->maxListeners, - "Maximum number of listeners reached" - ); - - if(type == EVENT_TYPE_C) { - assertNotNull( - user.c.callback, - "C event listener callback cannot be NULL" - ); - } else if(type == EVENT_TYPE_SCRIPT) { - assertNotNull( - user.script.context, - "Script event listener context cannot be NULL" - ); - assertTrue( - user.script.funcValue != 0, - "Script event listener function reference is invalid" - ); - } else { - assertUnreachable("Unknown event listener type"); - } - - eventsub_t id = event->nextId++; - assertTrue(event->nextId != 0, "Event subscription ID overflow"); - - eventlistener_t *listener = &event->listenerArray[event->listenerCount++]; - memoryZero(listener, sizeof(eventlistener_t)); - listener->user = user; - listener->id = id; - listener->type = type; - - return id; -} - -eventsub_t eventSubscribe( - event_t *event, - const eventcallback_t callback, - const void *user -) { - return eventSubscribeUser( - event, - EVENT_TYPE_C, - (eventuserdata_t){ .c = { .callback = callback, .user = (void *)user } } - ); -} - -eventsub_t eventSubscribeScriptContext( - event_t *event, - scriptmanager_t *context, - jerry_value_t funcValue -) { - assertNotNull(context, "Script context cannot be NULL"); - assertTrue(funcValue != 0, "Script function value is invalid"); - - bool_t alreadySubbed = false; - uint8_t i = 0; - do { - if(context->subscribedEvents[i] == event) { - alreadySubbed = true; - break; - } - i++; - } while(i < SCRIPT_MANAGER_MAX_EVENT_SUBSCRIPTIONS); - - if(!alreadySubbed) { - i = 0; - do { - if(context->subscribedEvents[i] == NULL) { - context->subscribedEvents[i] = event; - break; - } - i++; - } while(i < SCRIPT_MANAGER_MAX_EVENT_SUBSCRIPTIONS); - - assertTrue( - i < SCRIPT_MANAGER_MAX_EVENT_SUBSCRIPTIONS, - "Script context has reached maximum event subscriptions" - ); - } - - return eventSubscribeUser( - event, - EVENT_TYPE_SCRIPT, - (eventuserdata_t){ - .script = { - .context = context, - .funcValue = funcValue - } - } - ); -} - -void eventUnsubscribe(event_t *event, const eventsub_t id) { - assertNotNull(event, "Event cannot be NULL"); - assertFalse(event->isInvoking, "Cannot unsubscribe while invoking event"); - - if(event->listenerCount == 0) return; - - uint16_t index = 0; - do { - if(event->listenerArray[index].id != id) { - index++; - continue; - } - - if(event->listenerArray[index].type == EVENT_TYPE_SCRIPT) { - jerry_value_t funcVal = event->listenerArray[index].user.script.funcValue; - if(funcVal != 0) { - jerry_value_free(funcVal); - } - } - - event->listenerArray[index] = event->listenerArray[--event->listenerCount]; - return; - } while(index < event->listenerCount); -} - -void eventUnsubscribeScriptContext( - event_t *event, - const scriptmanager_t *ctx -) { - assertNotNull(event, "Event cannot be NULL"); - assertNotNull(ctx, "Script context cannot be NULL"); - - if(event->listenerCount == 0) return; - - uint16_t i = 0; - do { - eventlistener_t *listener = &event->listenerArray[i]; - if( - listener->type != EVENT_TYPE_SCRIPT || - listener->user.script.context != ctx - ) { - i++; - continue; - } - eventUnsubscribe(event, listener->id); - } while(i < event->listenerCount); -} - -void eventInvoke( - event_t *event, - const void *eventParams, - const char_t *metatableName -) { - assertNotNull(event, "Event cannot be NULL"); - - if(event->listenerCount == 0) return; - - event->isInvoking = true; - - eventdata_t data = { - .event = event, - .eventParams = eventParams, - }; - - uint16_t i = 0; - do { - eventlistener_t *listener = &event->listenerArray[i]; - - if(listener->type == EVENT_TYPE_C) { - listener->user.c.callback(&data, listener->user.c); - } else if(listener->type == EVENT_TYPE_SCRIPT) { - jerry_value_t funcVal = listener->user.script.funcValue; - assertNotNull((void *)(uintptr_t)funcVal, "Script function value is NULL"); - - jerry_value_t callArgs[1]; - jerry_length_t argCount = 0; - - if(eventParams != NULL) { - callArgs[0] = jerry_object(); - jerry_object_set_native_ptr( - callArgs[0], &JS_PTR_NATIVE_INFO, (void *)eventParams - ); - argCount = 1; - } - - jerry_value_t result = jerry_call( - funcVal, jerry_undefined(), callArgs, argCount - ); - - if(argCount > 0) jerry_value_free(callArgs[0]); - - if(jerry_value_is_exception(result)) { - jerry_value_t errStr = jerry_value_to_string( - jerry_exception_value(result, false) - ); - char_t buf[256]; - jerry_size_t len = jerry_string_to_buffer( - errStr, JERRY_ENCODING_UTF8, (jerry_char_t *)buf, sizeof(buf) - 1 - ); - buf[len] = '\0'; - jerry_value_free(errStr); - consolePrint("Error invoking script event listener:\n%s\n", buf); - } - - jerry_value_free(result); - } else { - assertUnreachable("Unknown event listener type"); - } - i++; - } while(i < event->listenerCount); - - event->isInvoking = false; -} diff --git a/src/dusk/event/event.h b/src/dusk/event/event.h deleted file mode 100644 index f7825572..00000000 --- a/src/dusk/event/event.h +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "eventuser.h" - -typedef struct event_s event_t; - -typedef struct eventdata_s { - const void *eventParams; - const event_t *event; -} eventdata_t; - -typedef struct eventlistener_s { - eventuserdata_t user; - eventtype_t type; - uint16_t id; -} eventlistener_t; - -typedef uint16_t eventsub_t; - -typedef struct event_s { - eventlistener_t *listenerArray; - uint16_t listenerCount; - uint16_t maxListeners; - eventsub_t nextId; - - bool_t isInvoking; -} event_t; - -/** - * Initialize an event structure. - * - * @param event The event to initialize. - * @param array The array to hold event listeners. - * @param maxListeners The maximum number of listeners. - */ -void eventInit( - event_t *event, - eventlistener_t *array, - const uint16_t maxListeners -); - -/** - * Subscribe to an event. - * - * @param event The event to subscribe to. - * @param type The type of the event (C or Lua). - * @param user User data to pass to the callback. - * @return The subscription ID, used to unsubscribe later. - */ -eventsub_t eventSubscribeUser( - event_t *event, - const eventtype_t type, - const eventuserdata_t user -); - -/** - * Subscribe to an event with a C function callback. - * - * @param event The event to subscribe to. - * @param callback The C function callback. - * @param user User data pointer to pass to the callback. - * @return The subscription ID, used to unsubscribe later. - */ -eventsub_t eventSubscribe( - event_t *event, - const eventcallback_t callback, - const void *user -); - -/** - * Subscribe to an event with a script function callback. - * - * @param event The event to subscribe to. - * @param context The script context. - * @param functionIndex The index of the Lua function on the stack. - * @return The subscription ID, used to unsubscribe later. - */ -eventsub_t eventSubscribeScriptContext( - event_t *event, - scriptmanager_t *context, - jerry_value_t funcValue -); - -/** - * Unsubscribe from an event. - * - * @param event The event to unsubscribe from. - * @param subscription The subscription ID to remove. - */ -void eventUnsubscribe(event_t *event, const eventsub_t subscription); - -/** - * Unsubscribe all event listeners associated with a script context. - * - * @param event The event to unsubscribe from. - * @param context The script context whose listeners should be removed. - */ -void eventUnsubscribeScriptContext( - event_t *event, - const scriptmanager_t *ctx -); - -/** - * Invoke an event, calling all subscribed listeners. Optionally provide event - * parameters, and if desired pass a metatable name. Event listeners will be - * able to read event params, and if metatable name is provided the script - * listeners will lookup metatable information matching that name to access the - * params as a structure within the script. - * - * @param event The event to invoke. - * @param eventParams Parameters to pass to the event listeners. - * @param metatableName Metatable name. See details. - */ -void eventInvoke( - event_t *event, - const void *eventParams, - const char_t *metatableName -); \ No newline at end of file diff --git a/src/dusk/event/eventcallback.h b/src/dusk/event/eventcallback.h deleted file mode 100644 index a6a6a376..00000000 --- a/src/dusk/event/eventcallback.h +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" - -typedef struct eventdata_s eventdata_t; -typedef struct eventc_s eventc_t; - -typedef void (*eventcallback_t)(eventdata_t *data, eventc_t user); \ No newline at end of file diff --git a/src/dusk/event/eventuser.h b/src/dusk/event/eventuser.h deleted file mode 100644 index a142a94d..00000000 --- a/src/dusk/event/eventuser.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "eventcallback.h" -#include "script/scriptmanager.h" - -typedef enum { - EVENT_TYPE_C = 0, - EVENT_TYPE_SCRIPT = 1 -} eventtype_t; - -typedef struct { - scriptmanager_t *context; - jerry_value_t funcValue; -} eventscript_t; - -typedef struct eventc_s { - void *user; - eventcallback_t callback; -} eventc_t; - -typedef union eventuserdata_u { - eventscript_t script; - eventc_t c; -} eventuserdata_t; diff --git a/src/dusk/input/input.c b/src/dusk/input/input.c index e2d1b9a4..5e3f98db 100644 --- a/src/dusk/input/input.c +++ b/src/dusk/input/input.c @@ -27,13 +27,6 @@ errorret_t inputInit(void) { errorChain(inputInitPlatform()); #endif - eventInit( - &INPUT.eventPressed, INPUT.pressedListeners, INPUT_LISTENER_PRESSED_MAX - ); - eventInit( - &INPUT.eventReleased, INPUT.releasedListeners, INPUT_LISTENER_RELEASED_MAX - ); - errorOk(); } @@ -94,28 +87,6 @@ void inputUpdate(void) { #ifdef DUSK_TIME_DYNAMIC if(TIME.dynamicUpdate) return; #endif - - // if(INPUT.eventPressed.listenerCount > 0) { - // action = &INPUT.actions[0]; - // do { - // if(inputPressed(action->action)) { - // inputevent_t inputEvent = { .action = action->action }; - // eventInvoke(&INPUT.eventPressed, &inputEvent, "input_mt"); - // } - // action++; - // } while(action < &INPUT.actions[INPUT_ACTION_COUNT]); - // } - - // if(INPUT.eventReleased.listenerCount > 0) { - // action = &INPUT.actions[0]; - // do { - // if(inputReleased(action->action)) { - // inputevent_t inputEvent = { .action = action->action }; - // eventInvoke(&INPUT.eventReleased, &inputEvent, "input_mt"); - // } - // action++; - // } while(action < &INPUT.actions[INPUT_ACTION_COUNT]); - // } } float_t inputGetCurrentValue(const inputaction_t action) { diff --git a/src/dusk/input/input.h b/src/dusk/input/input.h index a6013fe3..8289af45 100644 --- a/src/dusk/input/input.h +++ b/src/dusk/input/input.h @@ -9,23 +9,13 @@ #include "error/error.h" #include "inputbutton.h" #include "inputaction.h" -#include "event/event.h" #define INPUT_LISTENER_PRESSED_MAX 16 #define INPUT_LISTENER_RELEASED_MAX INPUT_LISTENER_PRESSED_MAX -typedef struct { - const inputaction_t action; -} inputevent_t; - typedef struct { inputactiondata_t actions[INPUT_ACTION_COUNT]; - eventlistener_t pressedListeners[INPUT_LISTENER_PRESSED_MAX]; - event_t eventPressed; - eventlistener_t releasedListeners[INPUT_LISTENER_RELEASED_MAX]; - event_t eventReleased; - inputplatform_t platform; } input_t; diff --git a/src/dusk/scene/scene.c b/src/dusk/scene/scene.c index c2226d6d..c410475b 100644 --- a/src/dusk/scene/scene.c +++ b/src/dusk/scene/scene.c @@ -11,14 +11,12 @@ #include "display/screen/screen.h" #include "entity/entitymanager.h" #include "display/shader/shaderunlit.h" -#include "display/mesh/cube.h" -#include "display/spritebatch/spritebatch.h" -#include "display/text/text.h" #include "display/screen/screen.h" #include "console/console.h" #include "util/string.h" #include "script/scriptmanager.h" #include "script/module/scene/modulescene.h" +#include "ui/ui.h" scene_t SCENE; @@ -55,6 +53,7 @@ errorret_t sceneRender(void) { COMPONENT_TYPE_CAMERA, camEnts, camComps ); + shader_t *shaderCurrent = NULL; mat4 view, proj, model; // For each camera @@ -70,7 +69,8 @@ errorret_t sceneRender(void) { entityCameraGetProjection(camEnt, camComp, proj); entityPositionGetTransform(camEnt, camPos, view); - // For each entity + // For each entity (I could iterate only over entities with mesh/material) + // but in future I may have different renderable types. for(entityid_t entityId = 0; entityId < ENTITY_COUNT_MAX; entityId++) { // Does this entity have a material? componentid_t matComp = entityGetComponent( @@ -81,9 +81,13 @@ errorret_t sceneRender(void) { componentid_t meshComp = entityGetComponent( entityId, COMPONENT_TYPE_MESH ); - if(meshComp == 0xFF) { - logError("Entity with material component without mesh component found\n"); + logError("Entity with material but no mesh found\n"); + continue; + } + mesh_t *mesh = entityMeshGetMesh(entityId, meshComp); + if(mesh == NULL) { + logError("Entity with material but no mesh found\n"); continue; } @@ -93,14 +97,7 @@ errorret_t sceneRender(void) { ); shader_t *shader = entityMaterialGetShader(entityId, matComp); if(shader == NULL) { - logError("Entity with material component without shader found\n"); - continue; - } - - // Get the mesh - mesh_t *mesh = entityMeshGetMesh(entityId, meshComp); - if(mesh == NULL) { - logError("Entity with material component without mesh found\n"); + logError("Entity with material but no shader found\n"); continue; } @@ -115,9 +112,13 @@ errorret_t sceneRender(void) { } // Render the mesh. - errorChain(shaderBind(shader)); - errorChain(shaderSetMatrix(shader, SHADER_UNLIT_PROJECTION, proj)); - errorChain(shaderSetMatrix(shader, SHADER_UNLIT_VIEW, view)); + if(shaderCurrent != shader) { + shaderCurrent = shader; + errorChain(shaderBind(shaderCurrent)); + errorChain(shaderSetMatrix(shader, SHADER_UNLIT_PROJECTION, proj)); + errorChain(shaderSetMatrix(shader, SHADER_UNLIT_VIEW, view)); + } + errorChain(shaderSetMatrix(shader, SHADER_UNLIT_MODEL, model)); errorChain(shaderSetMaterial(shader, material)); errorChain(meshDraw(mesh, 0, -1)); @@ -128,154 +129,29 @@ errorret_t sceneRender(void) { } } + // UI Rendering + glm_ortho( + 0.0f, SCREEN.width, + SCREEN.height, 0.0f, + 0.1f, 100.0f, + proj + ); + glm_lookat( + (vec3){ 0.0f, 0.0f, 1.0f }, + (vec3){ 0.0f, 0.0f, 0.0f }, + (vec3){ 0.0f, 1.0f, 0.0f }, + view + ); + glm_mat4_identity(model); + + errorChain(shaderBind(&SHADER_UNLIT)); + errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj)); + errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view)); + errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model)); + + errorChain(uiRender()); + errorOk(); - - // if(camCount > 0) { - // // For each entity - // for(entityid_t entityId = 0; entityId < ENTITY_COUNT_MAX; entityId++) { - - // } - - // entityid_t meshEnts[ENTITY_COUNT_MAX]; - // componentid_t meshComps[ENTITY_COUNT_MAX]; - // entityid_t meshCount = componentGetEntitiesWithComponent( - // COMPONENT_TYPE_MESH, meshEnts, meshComps - // ); - - // if(meshCount > 0) { - // errorChain(shaderBind(&SHADER_UNLIT)); - - // for(entityid_t camIndex = 0; camIndex < camCount; camIndex++) { - // entityid_t camEnt = camEnts[camIndex]; - // componentid_t camComp = camComps[camIndex]; - // componentid_t camPos = entityGetComponent(camEnt, COMPONENT_TYPE_POSITION); - // if(camPos == 0xFF) { - // logError("Camera entity without entity position found\n"); - // continue; - // } - - // entityCameraGetProjection(camEnt, camComp, proj); - // entityPositionGetTransform(camEnt, camPos, view); - - // for(entityid_t meshIndex = 0; meshIndex < meshCount; meshIndex++) { - // entityid_t meshEnt = meshEnts[meshIndex]; - // componentid_t meshComp = meshComps[meshIndex]; - // mesh_t *mesh = entityMeshGetMesh(meshEnt, meshComp); - // if(mesh == NULL) continue; - - // componentid_t meshPos = entityGetComponent( - // meshEnt, COMPONENT_TYPE_POSITION - // ); - // if(meshPos == 0xFF) { - // logError("Mesh entity without entity position found\n"); - // continue; - // } - - // componentid_t meshMat = entityGetComponent( - // meshEnt, COMPONENT_TYPE_MATERIAL - // ); - // if(meshMat == 0xFF) { - // logError("Mesh entity without material component found\n"); - // continue; - // } - - // shadermaterial_t *material = entityMaterialGetShaderMaterial( - // meshEnt, meshMat - // ); - // shader_t *shader = entityMaterialGetShader(meshEnt, meshMat); - // if(shader == NULL) { - // logError("Mesh entity with material component without shader found\n"); - // continue; - // } - - // entityPositionGetTransform(meshEnt, meshPos, model); - - // errorChain(shaderBind(shader)); - // errorChain(shaderSetMatrix(shader, SHADER_UNLIT_PROJECTION, proj)); - // errorChain(shaderSetMatrix(shader, SHADER_UNLIT_VIEW, view)); - // errorChain(shaderSetMatrix(shader, SHADER_UNLIT_MODEL, model)); - // errorChain(shaderSetMaterial(shader, material)); - // errorChain(meshDraw(mesh, 0, -1)); - // } - // } - // } - // } - - // glm_ortho( - // 0.0f, SCREEN.width, - // SCREEN.height, 0.0f, - // 0.1f, 100.0f, - // proj - // ); - // glm_lookat( - // (vec3){ 0.0f, 0.0f, 1.0f }, - // (vec3){ 0.0f, 0.0f, 0.0f }, - // (vec3){ 0.0f, 1.0f, 0.0f }, - // view - // ); - // glm_mat4_identity(model); - - // errorChain(shaderBind(&SHADER_UNLIT)); - // errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj)); - // errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view)); - // errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model)); - - // { - // entityid_t sprEnts[ENTITY_COUNT_MAX]; - // componentid_t sprComps[ENTITY_COUNT_MAX]; - // entityid_t sprCount = componentGetEntitiesWithComponent( - // COMPONENT_TYPE_SPRITE, sprEnts, sprComps - // ); - // for(entityid_t si = 0; si < sprCount; si++) { - // entitysprite_t *spr = entitySpriteGet(sprEnts[si], sprComps[si]); - // vec3 pos = { 0.0f, 0.0f, 0.0f }; - // componentid_t posComp = entityGetComponent( - // sprEnts[si], COMPONENT_TYPE_POSITION - // ); - // if(posComp != 0xFF) { - // entityPositionGetPosition(sprEnts[si], posComp, pos); - // } - // errorChain(shaderSetTexture( - // &SHADER_UNLIT, SHADER_UNLIT_TEXTURE, spr->texture - // )); - // #if !MESH_ENABLE_COLOR - // errorChain(shaderSetColor( - // &SHADER_UNLIT, SHADER_UNLIT_COLOR, spr->color - // )); - // #endif - // errorChain(spriteBatchPush( - // pos[0], pos[1], - // pos[0] + spr->width, pos[1] + spr->height, - // #if MESH_ENABLE_COLOR - // spr->color, - // #endif - // spr->uv[0], spr->uv[1], - // spr->uv[2], spr->uv[3] - // )); - // errorChain(spriteBatchFlush()); - // } - // } - - // errorChain(consoleDraw()); - - // // FPS - // char_t fpsText[32]; - - // dusktimeepoch_t now = timeGetEpoch(); - // double_t delta = now.time - LAST.time; - // LAST = now; - // double_t fps = delta > 0 ? 1.0 / delta : 0.0; - // snprintf(fpsText, sizeof(fpsText), "FPS: %.2f", fps); - - // errorChain(spriteBatchFlush()); - // errorChain(textDraw( - // 0, 0, - // fpsText, COLOR_WHITE, - // &FONT_TILESET_DEFAULT, &FONT_TEXTURE_DEFAULT - // )); - // errorChain(spriteBatchFlush()); - - // errorOk(); } errorret_t sceneSetImmediate(const char_t *scene) { diff --git a/src/dusk/scene/scene.h b/src/dusk/scene/scene.h index 034cbfb3..21e2737a 100644 --- a/src/dusk/scene/scene.h +++ b/src/dusk/scene/scene.h @@ -8,7 +8,6 @@ #pragma once #include "script/scriptmanager.h" #include "asset/assetfile.h" -#include "event/event.h" #define SCENE_EVENT_UPDATE_MAX 16 diff --git a/src/dusk/script/scriptmanager.c b/src/dusk/script/scriptmanager.c index c1827e9a..657db8f5 100644 --- a/src/dusk/script/scriptmanager.c +++ b/src/dusk/script/scriptmanager.c @@ -9,7 +9,6 @@ #include "assert/assert.h" #include "asset/asset.h" #include "util/memory.h" -#include "event/event.h" #include "asset/loader/script/assetscriptloader.h" #include "script/module/module.h" @@ -79,12 +78,6 @@ errorret_t scriptManagerExecFile( } errorret_t scriptManagerDispose(void) { - for(uint8_t i = 0; i < SCRIPT_MANAGER_MAX_EVENT_SUBSCRIPTIONS; i++) { - event_t *event = SCRIPT_MANAGER.subscribedEvents[i]; - if(event == NULL) continue; - eventUnsubscribeScriptContext(event, &SCRIPT_MANAGER); - } - jerry_cleanup(); errorOk(); } diff --git a/src/dusk/script/scriptmanager.h b/src/dusk/script/scriptmanager.h index f0b84887..70a4861d 100644 --- a/src/dusk/script/scriptmanager.h +++ b/src/dusk/script/scriptmanager.h @@ -9,12 +9,10 @@ #include "error/error.h" #include "scriptvalue.h" -typedef struct event_s event_t; - #define SCRIPT_MANAGER_MAX_EVENT_SUBSCRIPTIONS 64 typedef struct { - event_t *subscribedEvents[SCRIPT_MANAGER_MAX_EVENT_SUBSCRIPTIONS]; + void* nothing; } scriptmanager_t; extern scriptmanager_t SCRIPT_MANAGER; diff --git a/src/dusk/ui/CMakeLists.txt b/src/dusk/ui/CMakeLists.txt index 17e8dd90..48e4dbab 100644 --- a/src/dusk/ui/CMakeLists.txt +++ b/src/dusk/ui/CMakeLists.txt @@ -7,4 +7,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC ui.c + uifps.c ) \ No newline at end of file diff --git a/src/dusk/ui/ui.c b/src/dusk/ui/ui.c index 309863c7..2f144ea6 100644 --- a/src/dusk/ui/ui.c +++ b/src/dusk/ui/ui.c @@ -10,6 +10,8 @@ #include "assert/assert.h" #include "display/spritebatch/spritebatch.h" #include "display/screen/screen.h" +#include "ui/uifps.h" +#include "console/console.h" ui_t UI; @@ -26,15 +28,12 @@ errorret_t uiInit(void) { void uiUpdate(void) { } -void uiRender(void) { - // UI.camera.orthographic.right = SCREEN.width; - // UI.camera.orthographic.top = SCREEN.height; +errorret_t uiRender(void) { + errorChain(uiFPSDraw()); + errorChain(consoleDraw()); + errorChain(spriteBatchFlush()); - // // cameraPushMatrix(&UI.camera); - // spriteBatchClear(); - - // spriteBatchFlush(); - // // cameraPopMatrix(); + errorOk(); } void uiDispose(void) { diff --git a/src/dusk/ui/ui.h b/src/dusk/ui/ui.h index 6ece812f..1d5ab848 100644 --- a/src/dusk/ui/ui.h +++ b/src/dusk/ui/ui.h @@ -26,8 +26,10 @@ void uiUpdate(void); /** * Renders the UI system. + * + * @return Any error that occurs. */ -void uiRender(void); +errorret_t uiRender(void); /** * Disposes of the UI system. diff --git a/src/dusk/ui/uifps.c b/src/dusk/ui/uifps.c new file mode 100644 index 00000000..a4c9fc9a --- /dev/null +++ b/src/dusk/ui/uifps.c @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "uifps.h" +#include "time/time.h" +#include "display/spritebatch/spritebatch.h" +#include "display/text/text.h" + +uifps_t UIFPS; + +errorret_t uiFPSDraw() { + char_t fpsText[32]; + + // Get now. + dusktimeepoch_t now = timeGetEpoch(); + double_t delta = now.time - UIFPS.lastTick.time; + UIFPS.lastTick = now; + + // Raw current FPS + float_t fps = delta > 0 ? 1.0 / delta : 0.0; + + // Average FPS using exponential moving average + const float_t alpha = 0.1f; // Smoothing factor + if(UIFPS.fpsAverage == 0.0f) { + UIFPS.fpsAverage = fps; // Initialize average on first run + } else { + UIFPS.fpsAverage = alpha * fps + (1.0f - alpha) * UIFPS.fpsAverage; + } + + snprintf( + fpsText, + sizeof(fpsText), + "%.1f/%.1fms", + UIFPS.fpsAverage, + delta * 1000.0f + ); + + color_t textColor; + if(fps >= 60.0f) { + textColor = COLOR_GREEN; + } else if(fps >= 45.0f) { + textColor = COLOR_YELLOW; + } else { + textColor = COLOR_RED; + } + + errorChain(textDraw( + 0, 0, + fpsText, textColor, + &FONT_TILESET_DEFAULT, &FONT_TEXTURE_DEFAULT + )); + + errorOk(); +} \ No newline at end of file diff --git a/src/dusk/ui/uifps.h b/src/dusk/ui/uifps.h new file mode 100644 index 00000000..6f9c4592 --- /dev/null +++ b/src/dusk/ui/uifps.h @@ -0,0 +1,24 @@ +/** + * 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 "time/timeepoch.h" + +typedef struct { + dusktimeepoch_t lastTick; + float_t fpsAverage; +} uifps_t; + +extern uifps_t UIFPS; + +/** + * Draws the FPS counter on the screen, and also does the update (for now). + * + * @return Any error that occurs. + */ +errorret_t uiFPSDraw(); \ No newline at end of file