diff --git a/src/dusk/engine/engine.c b/src/dusk/engine/engine.c index a9cda295..2fc084ec 100644 --- a/src/dusk/engine/engine.c +++ b/src/dusk/engine/engine.c @@ -56,7 +56,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) { /* Run the init script. */ consolePrint("Engine initialized"); - sceneSet(SCENE_TYPE_OVERWORLD); + sceneSet(SCENE_TYPE_INITIAL); errorOk(); } diff --git a/src/dusk/scene/initial/initialscene.c b/src/dusk/scene/initial/initialscene.c index c4372a7a..67c0aa5f 100644 --- a/src/dusk/scene/initial/initialscene.c +++ b/src/dusk/scene/initial/initialscene.c @@ -7,12 +7,27 @@ #include "initialscene.h" #include "console/console.h" +#include "scene/scene.h" +#include "time/time.h" +#include "ui/uiloading.h" void initialSceneInit(void) { consolePrint("Initial scene initialized"); + SCENE.data.initial.timer = 0.0f; + SCENE.data.initial.hiding = false; + uiLoadingShow(NULL, NULL); } errorret_t initialSceneUpdate(void) { + initialscene_t *scene = &SCENE.data.initial; + if(scene->hiding) errorOk(); + + scene->timer += TIME.delta; + if(scene->timer >= INITIAL_SCENE_WAIT) { + scene->hiding = true; + uiLoadingHide(NULL, NULL); + } + errorOk(); } diff --git a/src/dusk/scene/initial/initialscene.h b/src/dusk/scene/initial/initialscene.h index 03b638b6..966eec4d 100644 --- a/src/dusk/scene/initial/initialscene.h +++ b/src/dusk/scene/initial/initialscene.h @@ -8,8 +8,11 @@ #pragma once #include "error/error.h" +#define INITIAL_SCENE_WAIT 2.0f + typedef struct { - void *nothing; + float_t timer; + bool_t hiding; } initialscene_t; void initialSceneInit(void); diff --git a/src/dusk/ui/CMakeLists.txt b/src/dusk/ui/CMakeLists.txt index e61731ee..32030fc3 100644 --- a/src/dusk/ui/CMakeLists.txt +++ b/src/dusk/ui/CMakeLists.txt @@ -11,5 +11,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} uielement.c uiframe.c uifullbox.c + uiloading.c uitextbox.c ) \ No newline at end of file diff --git a/src/dusk/ui/ui.c b/src/dusk/ui/ui.c index 0c078327..83416495 100644 --- a/src/dusk/ui/ui.c +++ b/src/dusk/ui/ui.c @@ -12,6 +12,7 @@ #include "display/screen/screen.h" #include "ui/uielement.h" #include "ui/uifullbox.h" +#include "ui/uiloading.h" #include "time/time.h" #include "log/log.h" @@ -19,6 +20,7 @@ ui_t UI; errorret_t uiInit(void) { memoryZero(&UI, sizeof(ui_t)); + uiLoadingInit(); uielement_t *element = &UI_ELEMENTS[0]; while(element->type != UI_ELEMENT_TYPE_NULL) { @@ -32,6 +34,7 @@ errorret_t uiInit(void) { void uiUpdate(void) { uiFullboxUpdate(&UI_FULLBOX_UNDER, TIME.delta); uiFullboxUpdate(&UI_FULLBOX_OVER, TIME.delta); + uiLoadingUpdate(TIME.delta); } errorret_t uiRender(void) { diff --git a/src/dusk/ui/uielement.c b/src/dusk/ui/uielement.c index 4efb19c1..fc1266ef 100644 --- a/src/dusk/ui/uielement.c +++ b/src/dusk/ui/uielement.c @@ -12,6 +12,7 @@ #include "engine/engine.h" #include "ui/uitextbox.h" #include "ui/uifullbox.h" +#include "ui/uiloading.h" uielement_t UI_ELEMENTS[] = { // Fullbox under: above scene, below system UI. @@ -20,10 +21,12 @@ uielement_t UI_ELEMENTS[] = { // { .type = UI_ELEMENT_TYPE_SCRIPT, .script = { .script = "ui/test.js" } }, { .type = UI_ELEMENT_TYPE_NATIVE, .draw = consoleDraw }, - { .type = UI_ELEMENT_TYPE_NATIVE, .draw = uiFPSDraw }, { .type = UI_ELEMENT_TYPE_NATIVE, .draw = uiTextboxDraw }, // Fullbox over: above absolutely everything. { .type = UI_ELEMENT_TYPE_NATIVE, .draw = uiFullboxOverDraw }, + // These render above the fullbox overlay. + { .type = UI_ELEMENT_TYPE_NATIVE, .draw = uiFPSDraw }, + { .type = UI_ELEMENT_TYPE_NATIVE, .draw = uiLoadingDraw }, { .type = UI_ELEMENT_TYPE_NULL }, }; diff --git a/src/dusk/ui/uifullbox.c b/src/dusk/ui/uifullbox.c index 2229fae8..c4f85268 100644 --- a/src/dusk/ui/uifullbox.c +++ b/src/dusk/ui/uifullbox.c @@ -52,20 +52,20 @@ errorret_t uiFullboxDraw(uifullbox_t *fullbox) { color_t color = uiFullboxGetColor(fullbox); if(color.a == 0) errorOk(); - errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, &TEXTURE_WHITE)); - #if MESH_ENABLE_COLOR - #else - errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, color)); - #endif - - spritebatchsprite_t sprite; + spritebatchsprite_t sprite = { + .min = { 0.0f, 0.0f, 0.0f }, + .max = { (float_t)SCREEN.width, (float_t)SCREEN.height, 0.0f }, + .uvMin = { 0.0f, 0.0f }, + .uvMax = { 1.0f, 1.0f } + }; shadermaterial_t material = { .unlit = { .color = color, .texture = &TEXTURE_WHITE } }; - return spriteBatchBuffer(&sprite, 1, &SHADER_UNLIT, material); + errorChain(spriteBatchBuffer(&sprite, 1, &SHADER_UNLIT, material)); + return spriteBatchFlush(); } void uiFullboxTransition( diff --git a/src/dusk/ui/uiloading.c b/src/dusk/ui/uiloading.c new file mode 100644 index 00000000..3b6de794 --- /dev/null +++ b/src/dusk/ui/uiloading.c @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "uiloading.h" +#include "assert/assert.h" +#include "util/memory.h" +#include "display/text/text.h" +#include "display/screen/screen.h" +#include "display/spritebatch/spritebatch.h" +#include "ui/uifullbox.h" + +#define UI_LOADING_TEXT "loading" + +uiloading_t UI_LOADING; + +void uiLoadingInit(void) { + memoryZero(&UI_LOADING, sizeof(uiloading_t)); + eventInit(&UI_LOADING.onTransitionEnd); +} + +void uiLoadingUpdate(float_t delta) { + if(UI_LOADING.duration <= 0.0f || UI_LOADING.time >= UI_LOADING.duration) return; + UI_LOADING.time += delta; + if(UI_LOADING.time >= UI_LOADING.duration) { + UI_LOADING.time = UI_LOADING.duration; + eventInvoke(&UI_LOADING.onTransitionEnd, &UI_LOADING); + } +} + +errorret_t uiLoadingDraw(void) { + float_t alpha; + if(UI_LOADING.duration <= 0.0f || UI_LOADING.time >= UI_LOADING.duration) { + alpha = UI_LOADING.toAlpha; + } else { + float_t t = UI_LOADING.time / UI_LOADING.duration; + alpha = UI_LOADING.fromAlpha + (UI_LOADING.toAlpha - UI_LOADING.fromAlpha) * t; + } + + if(alpha <= 0.0f) errorOk(); + + int32_t textW, textH; + textMeasure(UI_LOADING_TEXT, &FONT_DEFAULT, &textW, &textH); + + float_t x = (float_t)SCREEN.width - (float_t)textW - UI_LOADING_MARGIN; + float_t y = (float_t)SCREEN.height - (float_t)textH - UI_LOADING_MARGIN; + + color_t color = COLOR_WHITE; + color.a = (uint8_t)(alpha * 255.0f); + + errorChain(textDraw(x, y, UI_LOADING_TEXT, color, &FONT_DEFAULT)); + return spriteBatchFlush(); +} + +static void uiLoadingTransition( + const float_t from, + const float_t to, + const eventcallback_t callback, + void *user +) { + UI_LOADING.fromAlpha = from; + UI_LOADING.toAlpha = to; + UI_LOADING.duration = UI_LOADING_FADE_DURATION; + UI_LOADING.time = 0.0f; + eventInit(&UI_LOADING.onTransitionEnd); + if(callback) eventSubscribe(&UI_LOADING.onTransitionEnd, callback, user); +} + +void uiLoadingShow(eventcallback_t callback, void *user) { + uiLoadingTransition(0.0f, 1.0f, callback, user); + uiFullboxTransition( + &UI_FULLBOX_OVER, + COLOR_TRANSPARENT_BLACK, + COLOR_BLACK, + UI_LOADING_FADE_DURATION, + EASING_LINEAR + ); +} + +void uiLoadingHide(eventcallback_t callback, void *user) { + uiLoadingTransition(1.0f, 0.0f, callback, user); + uiFullboxTransition( + &UI_FULLBOX_OVER, + COLOR_BLACK, + COLOR_TRANSPARENT_BLACK, + UI_LOADING_FADE_DURATION, + EASING_LINEAR + ); +} diff --git a/src/dusk/ui/uiloading.h b/src/dusk/ui/uiloading.h new file mode 100644 index 00000000..49f54548 --- /dev/null +++ b/src/dusk/ui/uiloading.h @@ -0,0 +1,59 @@ +/** + * 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 "event/event.h" + +#define UI_LOADING_FADE_DURATION 0.5f +#define UI_LOADING_MARGIN 8.0f + +typedef struct { + float_t fromAlpha; + float_t toAlpha; + float_t duration; + float_t time; + event_t onTransitionEnd; +} uiloading_t; + +extern uiloading_t UI_LOADING; + +/** + * Initializes the loading indicator. + */ +void uiLoadingInit(void); + +/** + * Advances the loading indicator fade transition. Fires onTransitionEnd once + * when the transition completes. + * + * @param delta Seconds elapsed since last update. + */ +void uiLoadingUpdate(float_t delta); + +/** + * Draws the loading indicator. No-op when fully transparent. + * + * @return Error state. + */ +errorret_t uiLoadingDraw(void); + +/** + * Fades the loading indicator in. Invokes callback when fully visible. + * + * @param callback Called when the fade-in completes. May be NULL. + * @param user Forwarded to the callback unchanged. + */ +void uiLoadingShow(eventcallback_t callback, void *user); + +/** + * Fades the loading indicator out. Invokes callback when fully hidden. + * + * @param callback Called when the fade-out completes. May be NULL. + * @param user Forwarded to the callback unchanged. + */ +void uiLoadingHide(eventcallback_t callback, void *user);