Fixed_t updates

This commit is contained in:
2026-06-18 12:44:23 -05:00
parent a162002af2
commit c88b672f42
34 changed files with 599 additions and 258 deletions
+11 -4
View File
@@ -6,7 +6,7 @@
#include "animation.h" #include "animation.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "util/memory.h" #include "util/memory.h"
#include "util/math.h" #include "util/fixed.h"
void animationInit( void animationInit(
animation_t *anim, animation_t *anim,
@@ -21,7 +21,7 @@ void animationInit(
anim->keyframeCount = keyframeCount; anim->keyframeCount = keyframeCount;
} }
float_t animationGetValue(animation_t *anim, const float_t time) { fixed_t animationGetValue(animation_t *anim, const fixed_t time) {
assertNotNull(anim, "Animation pointer cannot be null."); assertNotNull(anim, "Animation pointer cannot be null.");
assertNotNull(anim->keyframes, "Keyframes pointer cannot be null."); assertNotNull(anim->keyframes, "Keyframes pointer cannot be null.");
assertTrue(anim->keyframeCount > 0, "Keyframe count invalid."); assertTrue(anim->keyframeCount > 0, "Keyframe count invalid.");
@@ -47,6 +47,13 @@ float_t animationGetValue(animation_t *anim, const float_t time) {
} }
} while(true); } while(true);
float_t t = (time - start->time) / (end->time - start->time); fixed_t span = fixedSub(end->time, start->time);
return mathLerp(start->value, end->value, easingApply(start->easing, t)); fixed_t progress = span != 0
? fixedDiv(fixedSub(time, start->time), span)
: FIXED_ONE;
return fixedLerp(
start->value,
end->value,
easingApply(start->easing, progress)
);
} }
+1 -1
View File
@@ -31,4 +31,4 @@ void animationInit(
* @param time The time at which to get the value, in seconds. * @param time The time at which to get the value, in seconds.
* @return The value of the animation at the given time. * @return The value of the animation at the given time.
*/ */
float_t animationGetValue(animation_t *anim, const float_t time); fixed_t animationGetValue(animation_t *anim, const fixed_t time);
+66 -46
View File
@@ -6,6 +6,11 @@
#include "easing.h" #include "easing.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "util/math.h" #include "util/math.h"
#include "util/fixed.h"
#define EASING_C1 1.70158f
#define EASING_C2 (EASING_C1 * 1.525f)
#define EASING_C3 (EASING_C1 + 1.0f)
const easingfn_t EASING_FUNCTIONS[EASING_COUNT] = { const easingfn_t EASING_FUNCTIONS[EASING_COUNT] = {
easingLinear, easingLinear,
@@ -26,86 +31,101 @@ const easingfn_t EASING_FUNCTIONS[EASING_COUNT] = {
easingInOutBack, easingInOutBack,
}; };
float_t easingApply(const easingtype_t type, const float_t t) { fixed_t easingApply(const easingtype_t type, const fixed_t t) {
assertTrue(type < EASING_COUNT, "Invalid easing type"); assertTrue(type < EASING_COUNT, "Invalid easing type");
return EASING_FUNCTIONS[type](t); return EASING_FUNCTIONS[type](t);
} }
float_t easingLinear(const float_t t) { fixed_t easingLinear(const fixed_t t) {
return t; return t;
} }
float_t easingInSine(const float_t t) { fixed_t easingInSine(const fixed_t t) {
return 1.0f - cosf(t * MATH_PI * 0.5f); float_t f = fixedToFloat(t);
return fixedFromFloat(1.0f - cosf(f * MATH_PI * 0.5f));
} }
float_t easingOutSine(const float_t t) { fixed_t easingOutSine(const fixed_t t) {
return sinf(t * MATH_PI * 0.5f); float_t f = fixedToFloat(t);
return fixedFromFloat(sinf(f * MATH_PI * 0.5f));
} }
float_t easingInOutSine(const float_t t) { fixed_t easingInOutSine(const fixed_t t) {
return -(cosf(MATH_PI * t) - 1.0f) * 0.5f; float_t f = fixedToFloat(t);
return fixedFromFloat(-(cosf(MATH_PI * f) - 1.0f) * 0.5f);
} }
float_t easingInQuad(const float_t t) { fixed_t easingInQuad(const fixed_t t) {
return t * t; float_t f = fixedToFloat(t);
return fixedFromFloat(f * f);
} }
float_t easingOutQuad(const float_t t) { fixed_t easingOutQuad(const fixed_t t) {
float_t u = 1.0f - t; float_t f = fixedToFloat(t);
return 1.0f - u * u; float_t u = 1.0f - f;
return fixedFromFloat(1.0f - u * u);
} }
float_t easingInOutQuad(const float_t t) { fixed_t easingInOutQuad(const fixed_t t) {
if(t < 0.5f) return 2.0f * t * t; float_t f = fixedToFloat(t);
float_t u = -2.0f * t + 2.0f; if(f < 0.5f) return fixedFromFloat(2.0f * f * f);
return 1.0f - u * u * 0.5f; float_t u = -2.0f * f + 2.0f;
return fixedFromFloat(1.0f - u * u * 0.5f);
} }
float_t easingInCubic(const float_t t) { fixed_t easingInCubic(const fixed_t t) {
return t * t * t; float_t f = fixedToFloat(t);
return fixedFromFloat(f * f * f);
} }
float_t easingOutCubic(const float_t t) { fixed_t easingOutCubic(const fixed_t t) {
float_t u = 1.0f - t; float_t f = fixedToFloat(t);
return 1.0f - u * u * u; float_t u = 1.0f - f;
return fixedFromFloat(1.0f - u * u * u);
} }
float_t easingInOutCubic(const float_t t) { fixed_t easingInOutCubic(const fixed_t t) {
if(t < 0.5f) return 4.0f * t * t * t; float_t f = fixedToFloat(t);
float_t u = -2.0f * t + 2.0f; if(f < 0.5f) return fixedFromFloat(4.0f * f * f * f);
return 1.0f - u * u * u * 0.5f; float_t u = -2.0f * f + 2.0f;
return fixedFromFloat(1.0f - u * u * u * 0.5f);
} }
float_t easingInQuart(const float_t t) { fixed_t easingInQuart(const fixed_t t) {
return t * t * t * t; float_t f = fixedToFloat(t);
return fixedFromFloat(f * f * f * f);
} }
float_t easingOutQuart(const float_t t) { fixed_t easingOutQuart(const fixed_t t) {
float_t u = 1.0f - t; float_t f = fixedToFloat(t);
return 1.0f - u * u * u * u; float_t u = 1.0f - f;
return fixedFromFloat(1.0f - u * u * u * u);
} }
float_t easingInOutQuart(const float_t t) { fixed_t easingInOutQuart(const fixed_t t) {
if(t < 0.5f) return 8.0f * t * t * t * t; float_t f = fixedToFloat(t);
float_t u = -2.0f * t + 2.0f; if(f < 0.5f) return fixedFromFloat(8.0f * f * f * f * f);
return 1.0f - u * u * u * u * 0.5f; float_t u = -2.0f * f + 2.0f;
return fixedFromFloat(1.0f - u * u * u * u * 0.5f);
} }
float_t easingInBack(const float_t t) { fixed_t easingInBack(const fixed_t t) {
return EASING_C3 * t * t * t - EASING_C1 * t * t; float_t f = fixedToFloat(t);
return fixedFromFloat(EASING_C3 * f * f * f - EASING_C1 * f * f);
} }
float_t easingOutBack(const float_t t) { fixed_t easingOutBack(const fixed_t t) {
float_t u = t - 1.0f; float_t f = fixedToFloat(t);
return 1.0f + EASING_C3 * u * u * u + EASING_C1 * u * u; float_t u = f - 1.0f;
return fixedFromFloat(1.0f + EASING_C3 * u * u * u + EASING_C1 * u * u);
} }
float_t easingInOutBack(const float_t t) { fixed_t easingInOutBack(const fixed_t t) {
if(t < 0.5f) { float_t f = fixedToFloat(t);
float_t u = 2.0f * t; if(f < 0.5f) {
return u * u * ((EASING_C2 + 1.0f) * u - EASING_C2) * 0.5f; float_t u = 2.0f * f;
return fixedFromFloat(u * u * ((EASING_C2 + 1.0f) * u - EASING_C2) * 0.5f);
} }
float_t u = 2.0f * t - 2.0f; float_t u = 2.0f * f - 2.0f;
return (u * u * ((EASING_C2 + 1.0f) * u + EASING_C2) + 2.0f) * 0.5f; return fixedFromFloat((u * u * ((EASING_C2 + 1.0f) * u + EASING_C2) + 2.0f) * 0.5f);
} }
+21 -25
View File
@@ -5,11 +5,7 @@
#pragma once #pragma once
#include "dusk.h" #include "dusk.h"
#include "util/fixed.h"
#define EASING_PI 3.14159265358979323846f
#define EASING_C1 1.70158f
#define EASING_C2 (EASING_C1 * 1.525f)
#define EASING_C3 (EASING_C1 + 1.0f)
typedef enum { typedef enum {
EASING_LINEAR, EASING_LINEAR,
@@ -32,7 +28,7 @@ typedef enum {
EASING_COUNT EASING_COUNT
} easingtype_t; } easingtype_t;
typedef float_t (*easingfn_t)(const float_t t); typedef fixed_t (*easingfn_t)(const fixed_t t);
extern const easingfn_t EASING_FUNCTIONS[EASING_COUNT]; extern const easingfn_t EASING_FUNCTIONS[EASING_COUNT];
@@ -40,24 +36,24 @@ extern const easingfn_t EASING_FUNCTIONS[EASING_COUNT];
* Applies the specified easing function to t. * Applies the specified easing function to t.
* *
* @param type The easing type to apply. * @param type The easing type to apply.
* @param t The input time, in the range [0, 1]. * @param t The input progress in [0, FIXED_ONE].
* @return The eased value, in the range [0, 1]. * @return The eased value in [0, FIXED_ONE].
*/ */
float_t easingApply(const easingtype_t type, const float_t t); fixed_t easingApply(const easingtype_t type, const fixed_t t);
float_t easingLinear(const float_t t); fixed_t easingLinear(const fixed_t t);
float_t easingInSine(const float_t t); fixed_t easingInSine(const fixed_t t);
float_t easingOutSine(const float_t t); fixed_t easingOutSine(const fixed_t t);
float_t easingInOutSine(const float_t t); fixed_t easingInOutSine(const fixed_t t);
float_t easingInQuad(const float_t t); fixed_t easingInQuad(const fixed_t t);
float_t easingOutQuad(const float_t t); fixed_t easingOutQuad(const fixed_t t);
float_t easingInOutQuad(const float_t t); fixed_t easingInOutQuad(const fixed_t t);
float_t easingInCubic(const float_t t); fixed_t easingInCubic(const fixed_t t);
float_t easingOutCubic(const float_t t); fixed_t easingOutCubic(const fixed_t t);
float_t easingInOutCubic(const float_t t); fixed_t easingInOutCubic(const fixed_t t);
float_t easingInQuart(const float_t t); fixed_t easingInQuart(const fixed_t t);
float_t easingOutQuart(const float_t t); fixed_t easingOutQuart(const fixed_t t);
float_t easingInOutQuart(const float_t t); fixed_t easingInOutQuart(const fixed_t t);
float_t easingInBack(const float_t t); fixed_t easingInBack(const fixed_t t);
float_t easingOutBack(const float_t t); fixed_t easingOutBack(const fixed_t t);
float_t easingInOutBack(const float_t t); fixed_t easingInOutBack(const fixed_t t);
+3 -2
View File
@@ -5,9 +5,10 @@
#pragma once #pragma once
#include "easing.h" #include "easing.h"
#include "util/fixed.h"
typedef struct { typedef struct {
float_t time; fixed_t time;
float_t value; fixed_t value;
easingtype_t easing; easingtype_t easing;
} keyframe_t; } keyframe_t;
@@ -816,17 +816,24 @@ errorret_t assetLocaleGetStringWithArgs(
case 'f': case 'f':
if( if(
args[nextArg].type != ASSET_LOCALE_ARG_FLOAT && args[nextArg].type != ASSET_LOCALE_ARG_FLOAT &&
args[nextArg].type != ASSET_LOCALE_ARG_INT args[nextArg].type != ASSET_LOCALE_ARG_INT &&
args[nextArg].type != ASSET_LOCALE_ARG_FIXED
) { ) {
memoryFree(format); memoryFree(format);
errorThrow("Expected float or int locale argument for ID: %s", id); errorThrow(
"Expected float, fixed, or int locale argument for ID: %s",
id
);
} }
float_t floatValue = ( float_t floatValue;
args[nextArg].type == ASSET_LOCALE_ARG_FLOAT ? if(args[nextArg].type == ASSET_LOCALE_ARG_FLOAT) {
args[nextArg].floatValue : floatValue = args[nextArg].floatValue;
(float_t)args[nextArg].intValue } else if(args[nextArg].type == ASSET_LOCALE_ARG_FIXED) {
); floatValue = fixedToFloat(args[nextArg].fixedValue);
} else {
floatValue = (float_t)args[nextArg].intValue;
}
written = snprintf( written = snprintf(
valueBuffer, valueBuffer,
@@ -7,6 +7,7 @@
#pragma once #pragma once
#include "asset/assetfile.h" #include "asset/assetfile.h"
#include "util/fixed.h"
typedef struct assetloading_s assetloading_t; typedef struct assetloading_s assetloading_t;
typedef struct assetentry_s assetentry_t; typedef struct assetentry_s assetentry_t;
@@ -51,7 +52,8 @@ typedef enum {
typedef enum { typedef enum {
ASSET_LOCALE_ARG_STRING, ASSET_LOCALE_ARG_STRING,
ASSET_LOCALE_ARG_INT, ASSET_LOCALE_ARG_INT,
ASSET_LOCALE_ARG_FLOAT ASSET_LOCALE_ARG_FLOAT,
ASSET_LOCALE_ARG_FIXED
} assetlocaleargtype_t; } assetlocaleargtype_t;
/** /**
@@ -67,6 +69,7 @@ typedef struct {
const char_t *stringValue; const char_t *stringValue;
int32_t intValue; int32_t intValue;
float_t floatValue; float_t floatValue;
fixed_t fixedValue;
}; };
} assetlocalearg_t; } assetlocalearg_t;
+1 -1
View File
@@ -44,7 +44,7 @@ void cutsceneItemUpdate(const cutsceneitem_t *item, cutsceneitemdata_t *data) {
break; break;
case CUTSCENE_ITEM_TYPE_WAIT: case CUTSCENE_ITEM_TYPE_WAIT:
data->wait -= TIME.delta; data->wait = fixedSub(data->wait, TIME.delta);
if(data->wait <= 0) cutsceneSystemNext(); if(data->wait <= 0) cutsceneSystemNext();
break; break;
+3 -2
View File
@@ -7,6 +7,7 @@
#pragma once #pragma once
#include "dusk.h" #include "dusk.h"
#include "util/fixed.h"
typedef float_t cutscenewait_t; typedef fixed_t cutscenewait_t;
typedef float_t cutscenewaitdata_t; typedef fixed_t cutscenewaitdata_t;
+2 -2
View File
@@ -10,7 +10,7 @@
static const cutsceneitem_t TEST_CUTSCENE_ONE_ITEMS[] = { static const cutsceneitem_t TEST_CUTSCENE_ONE_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = "This is a test cutscene.", .position = RPG_TEXTBOX_POS_BOTTOM } }, { .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = "This is a test cutscene.", .position = RPG_TEXTBOX_POS_BOTTOM } },
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 2.0f }, { .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = FIXED(2.0f) },
{ .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = "It has multiple lines of text.\nAnd waits in between.", .position = RPG_TEXTBOX_POS_TOP } }, { .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = "It has multiple lines of text.\nAnd waits in between.", .position = RPG_TEXTBOX_POS_TOP } },
}; };
@@ -20,7 +20,7 @@ static const cutscene_t TEST_CUTSCENE_ONE = {
}; };
static const cutsceneitem_t TEST_CUTSCENE_TWO_ITEMS[] = { static const cutsceneitem_t TEST_CUTSCENE_TWO_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 1.0f }, { .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = FIXED(1.0f) },
{ .type = CUTSCENE_ITEM_TYPE_CUTSCENE, .cutscene = &TEST_CUTSCENE_ONE }, { .type = CUTSCENE_ITEM_TYPE_CUTSCENE, .cutscene = &TEST_CUTSCENE_ONE },
}; };
+20 -18
View File
@@ -60,16 +60,13 @@ void entityWalk(entity_t *entity, const entitydir_t direction) {
entity->direction = direction; entity->direction = direction;
// Where are we moving? // Where are we moving?
worldpos_t newPos = entity->position; worldpos_t cur = fixedToWorldPos(entity->position);
worldunits_t relX, relY; worldunits_t relX, relY;
{ entityDirGetRelative(direction, &relX, &relY);
entityDirGetRelative(direction, &relX, &relY); worldpos_t newPos = { cur.x + relX, cur.y + relY, cur.z };
newPos.x += relX;
newPos.y += relY;
}
// Get tile under foot // Get tile under foot
tile_t tileCurrent = mapGetTile(entity->position); tile_t tileCurrent = mapGetTile(cur);
tile_t tileNew = mapGetTile(newPos); tile_t tileNew = mapGetTile(newPos);
bool_t fall = false; bool_t fall = false;
bool_t raise = false; bool_t raise = false;
@@ -156,27 +153,24 @@ void entityWalk(entity_t *entity, const entitydir_t direction) {
do { do {
if(other == entity) continue; if(other == entity) continue;
if(other->type == ENTITY_TYPE_NULL) continue; if(other->type == ENTITY_TYPE_NULL) continue;
if(!worldPosIsEqual(other->position, newPos)) continue; if(!worldPosIsEqual(fixedToWorldPos(other->position), newPos)) continue;
return;// Blocked return;// Blocked
} while(++other, other < &ENTITIES[ENTITY_COUNT]); } while(++other, other < &ENTITIES[ENTITY_COUNT]);
entity->lastPosition = entity->position; if(raise) newPos.z += 1;
entity->position = newPos; else if(fall) newPos.z -= 1;
entity->animation = ENTITY_ANIM_WALK;
entity->animTime = ENTITY_ANIM_WALK_DURATION;// TODO: Running vs walking
if(raise) { memoryCopy(entity->lastPosition, entity->position, sizeof(entity->lastPosition));
entity->position.z += 1; worldPosToFixed(&newPos, entity->position);
} else if(fall) { entity->animation = ENTITY_ANIM_WALK;
entity->position.z -= 1; entity->animTime = ENTITY_ANIM_WALK_DURATION;
}
} }
entity_t * entityGetAt(const worldpos_t position) { entity_t * entityGetAt(const worldpos_t position) {
entity_t *ent = ENTITIES; entity_t *ent = ENTITIES;
do { do {
if(ent->type == ENTITY_TYPE_NULL) continue; if(ent->type == ENTITY_TYPE_NULL) continue;
if(!worldPosIsEqual(ent->position, position)) continue; if(!worldPosIsEqual(fixedToWorldPos(ent->position), position)) continue;
return ent; return ent;
} while(++ent, ent < &ENTITIES[ENTITY_COUNT]); } while(++ent, ent < &ENTITIES[ENTITY_COUNT]);
@@ -191,3 +185,11 @@ uint8_t entityGetAvailable() {
return 0xFF; return 0xFF;
} }
bool_t entityCanWalk(const entity_t *entity) {
return entity->animation == ENTITY_ANIM_IDLE;
}
bool_t entityCanTurn(const entity_t *entity) {
return entity->animation == ENTITY_ANIM_IDLE;
}
+19 -4
View File
@@ -20,12 +20,11 @@ typedef struct entity_s {
// Movement // Movement
entitydir_t direction; entitydir_t direction;
worldpos_t position; fixed_t position[3];
tilepos_t subPosition; fixed_t lastPosition[3];
worldpos_t lastPosition;
entityanim_t animation; entityanim_t animation;
float_t animTime; fixed_t animTime;
} entity_t; } entity_t;
extern entity_t ENTITIES[ENTITY_COUNT]; extern entity_t ENTITIES[ENTITY_COUNT];
@@ -76,3 +75,19 @@ entity_t *entityGetAt(const worldpos_t pos);
* @return The index of an available entity, or 0xFF if none are available. * @return The index of an available entity, or 0xFF if none are available.
*/ */
uint8_t entityGetAvailable(); uint8_t entityGetAvailable();
/**
* Returns true if the entity is in a state where it can begin walking.
*
* @param entity Pointer to the entity to check.
* @return true if the entity can walk, false otherwise.
*/
bool_t entityCanWalk(const entity_t *entity);
/**
* Returns true if the entity is in a state where it can begin turning.
*
* @param entity Pointer to the entity to check.
* @return true if the entity can turn, false otherwise.
*/
bool_t entityCanTurn(const entity_t *entity);
+1 -1
View File
@@ -11,7 +11,7 @@
void entityAnimUpdate(entity_t *entity) { void entityAnimUpdate(entity_t *entity) {
if(entity->animation == ENTITY_ANIM_IDLE) return; if(entity->animation == ENTITY_ANIM_IDLE) return;
entity->animTime -= TIME.delta; entity->animTime = fixedSub(entity->animTime, TIME.delta);
if(entity->animTime <= 0) { if(entity->animTime <= 0) {
entity->animation = ENTITY_ANIM_IDLE; entity->animation = ENTITY_ANIM_IDLE;
entity->animTime = 0; entity->animTime = 0;
+3 -2
View File
@@ -7,9 +7,10 @@
#pragma once #pragma once
#include "dusk.h" #include "dusk.h"
#include "util/fixed.h"
#define ENTITY_ANIM_TURN_DURATION 0.06f #define ENTITY_ANIM_TURN_DURATION FIXED(0.12f)
#define ENTITY_ANIM_WALK_DURATION 0.1f #define ENTITY_ANIM_WALK_DURATION FIXED(0.1f)
typedef struct entity_s entity_t; typedef struct entity_s entity_t;
+22 -22
View File
@@ -19,33 +19,33 @@ void playerInput(entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL"); assertNotNull(entity, "Entity pointer cannot be NULL");
// Turn // Turn
const playerinputdirmap_t *dirMap = PLAYER_INPUT_DIR_MAP; if(entityCanTurn(entity)) {
do { const playerinputdirmap_t *dirMap = PLAYER_INPUT_DIR_MAP;
if(!inputIsDown(dirMap->action)) continue; do {
if(entity->direction == dirMap->direction) continue; if(!inputIsDown(dirMap->action)) continue;
return entityTurn(entity, dirMap->direction); if(entity->direction == dirMap->direction) continue;
} while((++dirMap)->action != 0xFF); return entityTurn(entity, dirMap->direction);
} while((++dirMap)->action != 0xFF);
}
// Walk // Walk
dirMap = PLAYER_INPUT_DIR_MAP; if(entityCanWalk(entity)) {
do { const playerinputdirmap_t *dirMap = PLAYER_INPUT_DIR_MAP;
if(!inputIsDown(dirMap->action)) continue; do {
if(entity->direction != dirMap->direction) continue; if(!inputIsDown(dirMap->action)) continue;
return entityWalk(entity, dirMap->direction); if(entity->direction != dirMap->direction) continue;
} while((++dirMap)->action != 0xFF); return entityWalk(entity, dirMap->direction);
} while((++dirMap)->action != 0xFF);
}
// Interaction // Interaction
if(inputPressed(INPUT_ACTION_ACCEPT)) { if(inputPressed(INPUT_ACTION_ACCEPT)) {
worldunit_t x, y, z; worldunits_t relX, relY;
{ entityDirGetRelative(entity->direction, &relX, &relY);
worldunits_t relX, relY; worldpos_t cur = fixedToWorldPos(entity->position);
entityDirGetRelative(entity->direction, &relX, &relY); entity_t *interact = entityGetAt(
x = entity->position.x + relX; (worldpos_t){ cur.x + relX, cur.y + relY, cur.z }
y = entity->position.y + relY; );
z = entity->position.z;
}
entity_t *interact = entityGetAt((worldpos_t){ x, y, z });
if(interact != NULL && ENTITY_CALLBACKS[interact->type].interact != NULL) { if(interact != NULL && ENTITY_CALLBACKS[interact->type].interact != NULL) {
if(ENTITY_CALLBACKS[interact->type].interact(entity, interact)) return; if(ENTITY_CALLBACKS[interact->type].interact(entity, interact)) return;
} }
+15 -1
View File
@@ -84,7 +84,7 @@ chunktileindex_t worldPosToChunkTileIndex(const worldpos_t* worldPos) {
return chunkTileIndex; return chunkTileIndex;
} }
chunkindex_t chunkPosToIndex(const chunkpos_t* pos) { chunkindex_t chunkPosToIndex(const chunkpos_t *pos) {
assertNotNull(pos, "Chunk position pointer cannot be NULL"); assertNotNull(pos, "Chunk position pointer cannot be NULL");
chunkindex_t chunkIndex = (chunkindex_t)( chunkindex_t chunkIndex = (chunkindex_t)(
@@ -95,3 +95,17 @@ chunkindex_t chunkPosToIndex(const chunkpos_t* pos) {
return chunkIndex; return chunkIndex;
} }
void worldPosToFixed(const worldpos_t *pos, fixed_t *out) {
out[0] = fixedFromInt(pos->x);
out[1] = fixedFromInt(pos->y);
out[2] = fixedFromInt(pos->z);
}
worldpos_t fixedToWorldPos(const fixed_t *position) {
return (worldpos_t){
(worldunit_t)fixedToInt(position[0]),
(worldunit_t)fixedToInt(position[1]),
(worldunit_t)fixedToInt(position[2])
};
}
+33 -25
View File
@@ -7,9 +7,7 @@
#pragma once #pragma once
#include "dusk.h" #include "dusk.h"
#include "util/fixed.h"
#define TILE_POS_MIN -128
#define TILE_POS_MAX 127
#define CHUNK_WIDTH 16 #define CHUNK_WIDTH 16
#define CHUNK_HEIGHT CHUNK_WIDTH #define CHUNK_HEIGHT CHUNK_WIDTH
@@ -32,8 +30,6 @@ typedef uint32_t chunktileindex_t;
typedef int32_t worldunits_t; typedef int32_t worldunits_t;
typedef int32_t chunkunits_t; typedef int32_t chunkunits_t;
typedef int8_t tileunit_t;
typedef struct worldpos_s { typedef struct worldpos_s {
worldunit_t x, y, z; worldunit_t x, y, z;
} worldpos_t; } worldpos_t;
@@ -42,10 +38,6 @@ typedef struct chunkpos_t {
chunkunit_t x, y, z; chunkunit_t x, y, z;
} chunkpos_t; } chunkpos_t;
typedef struct tilepos_s {
tileunit_t x, y, z;
} tilepos_t;
/** /**
* Compares two world positions for equality. * Compares two world positions for equality.
* *
@@ -55,35 +47,51 @@ typedef struct tilepos_s {
*/ */
bool_t worldPosIsEqual(const worldpos_t a, const worldpos_t b); bool_t worldPosIsEqual(const worldpos_t a, const worldpos_t b);
/**
* Converts a chunk position to a world position.
*
* @param chunkPos The chunk position.
* @param out The output world position.
*/
void chunkPosToWorldPos(const chunkpos_t *chunkPos, worldpos_t *out);
/** /**
* Converts a world position to a chunk position. * Converts a world position to a chunk position.
* *
* @param worldPos The world position. * @param worldPos The world position.
* @param out The output chunk position. * @param out The output chunk position.
*/ */
void chunkPosToWorldPos(const chunkpos_t* chunkPos, worldpos_t* out); void worldPosToChunkPos(const worldpos_t *worldPos, chunkpos_t *out);
/** /**
* Converts a chunk position to a world position. * Converts a world position to the tile index within its chunk.
*
* @param worldPos The world position.
* @param out The output chunk position.
*/
void worldPosToChunkPos(const worldpos_t* worldPos, chunkpos_t* out);
/**
* Converts a position in world-space to an index inside a chunk that the tile
* resides in.
* *
* @param worldPos The world position. * @param worldPos The world position.
* @return The tile index within the chunk. * @return The tile index within the chunk.
*/ */
chunktileindex_t worldPosToChunkTileIndex(const worldpos_t* worldPos); chunktileindex_t worldPosToChunkTileIndex(const worldpos_t *worldPos);
/** /**
* Converts a chunk position to a world position. * Converts a chunk position to a linear index.
* *
* @param worldPos The world position. * @param pos The chunk position.
* @param out The output chunk position. * @return The linear chunk index.
*/ */
chunkindex_t chunkPosToIndex(const chunkpos_t* pos); chunkindex_t chunkPosToIndex(const chunkpos_t *pos);
/**
* Converts a world position to a fixed-point position.
*
* @param pos The integer world position.
* @param out The output fixed-point position (3 dimensions).
*/
void worldPosToFixed(const worldpos_t *pos, fixed_t *out);
/**
* Converts the integer part of a fixed-point position to a world position.
* The fractional part is discarded.
*
* @param position The fixed-point position.
* @returns The integer tile world position.
*/
worldpos_t fixedToWorldPos(const fixed_t *position);
+7
View File
@@ -37,6 +37,13 @@ errorret_t rpgInit(void) {
RPG_CAMERA.mode = RPG_CAMERA_MODE_FOLLOW_ENTITY; RPG_CAMERA.mode = RPG_CAMERA_MODE_FOLLOW_ENTITY;
RPG_CAMERA.followEntity.followEntityId = ent->id; RPG_CAMERA.followEntity.followEntityId = ent->id;
// TEST: Add a test NPC.
uint8_t npcIndex = entityGetAvailable();
assertTrue(npcIndex != 0xFF, "No available entity slots for NPC.");
entity_t *npc = &ENTITIES[npcIndex];
entityInit(npc, ENTITY_TYPE_NPC);
worldPosToFixed(&(worldpos_t){ 3, 3, 0 }, npc->position);
// All Good! // All Good!
errorOk(); errorOk();
} }
+3 -4
View File
@@ -11,8 +11,6 @@
#include "rpg/overworld/map.h" #include "rpg/overworld/map.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "display/screen/screen.h"
rpgcamera_t RPG_CAMERA; rpgcamera_t RPG_CAMERA;
void rpgCameraInit(void) { void rpgCameraInit(void) {
@@ -29,7 +27,7 @@ worldpos_t rpgCameraGetPosition(void) {
if(entity->type == ENTITY_TYPE_NULL) { if(entity->type == ENTITY_TYPE_NULL) {
return (worldpos_t){ 0, 0, 0 }; return (worldpos_t){ 0, 0, 0 };
} }
return entity->position; return fixedToWorldPos(entity->position);
} }
default: default:
@@ -40,8 +38,8 @@ worldpos_t rpgCameraGetPosition(void) {
errorret_t rpgCameraUpdate(void) { errorret_t rpgCameraUpdate(void) {
if(!mapIsLoaded()) errorOk(); if(!mapIsLoaded()) errorOk();
chunkpos_t chunkPos;
worldpos_t worldPos = rpgCameraGetPosition(); worldpos_t worldPos = rpgCameraGetPosition();
chunkpos_t chunkPos;
worldPosToChunkPos(&worldPos, &chunkPos); worldPosToChunkPos(&worldPos, &chunkPos);
errorChain(mapPositionSet((chunkpos_t){ errorChain(mapPositionSet((chunkpos_t){
@@ -49,5 +47,6 @@ errorret_t rpgCameraUpdate(void) {
.y = chunkPos.y - (MAP_CHUNK_HEIGHT / 2), .y = chunkPos.y - (MAP_CHUNK_HEIGHT / 2),
.z = chunkPos.z - (MAP_CHUNK_DEPTH / 2) .z = chunkPos.z - (MAP_CHUNK_DEPTH / 2)
})); }));
errorOk(); errorOk();
} }
-2
View File
@@ -24,8 +24,6 @@ typedef struct {
uint8_t followEntityId; uint8_t followEntityId;
} followEntity; } followEntity;
}; };
mat4 eye;
} rpgcamera_t; } rpgcamera_t;
extern rpgcamera_t RPG_CAMERA; extern rpgcamera_t RPG_CAMERA;
+65 -26
View File
@@ -17,6 +17,7 @@
#include "rpg/overworld/map.h" #include "rpg/overworld/map.h"
#include "rpg/entity/entity.h" #include "rpg/entity/entity.h"
#include "rpg/rpgcamera.h" #include "rpg/rpgcamera.h"
#include "util/math.h"
errorret_t sceneOverworldInit(scenedata_t *sceneData) { errorret_t sceneOverworldInit(scenedata_t *sceneData) {
assertNotNull(sceneData, "Scene data cannot be null"); assertNotNull(sceneData, "Scene data cannot be null");
@@ -57,31 +58,53 @@ errorret_t sceneOverworldRender(scenedata_t *sceneData) {
); );
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj)); errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj));
// Camera Eye // Camera view
float_t pixelsPerUnit = 16.0f; {
float_t worldH = (float)SCREEN.height / pixelsPerUnit; vec3 target = { 0.5f, 0.5f, 0.5f };
float_t z = (worldH * 0.5f) / tanf(fov * 0.5f); if(RPG_CAMERA.mode == RPG_CAMERA_MODE_FOLLOW_ENTITY) {
worldpos_t worldPos = rpgCameraGetPosition(); entity_t *followed = &ENTITIES[RPG_CAMERA.followEntity.followEntityId];
float_t offset = -16.0f; if(followed->type != ENTITY_TYPE_NULL) {
float_t walkT = followed->animation == ENTITY_ANIM_WALK
? fixedToFloat(
fixedDiv(followed->animTime, ENTITY_ANIM_WALK_DURATION)
)
: 0.0f;
target[0] = mathLerp(
fixedToFloat(followed->position[0]),
fixedToFloat(followed->lastPosition[0]),
walkT
) + 0.5f;
target[1] = mathLerp(
fixedToFloat(followed->position[1]),
fixedToFloat(followed->lastPosition[1]),
walkT
) + 0.5f;
target[2] = mathLerp(
fixedToFloat(followed->position[2]),
fixedToFloat(followed->lastPosition[2]),
walkT
) + 0.5f;
}
} else {
worldpos_t camPos = rpgCameraGetPosition();
target[0] = (float_t)camPos.x + 0.5f;
target[1] = (float_t)camPos.y + 0.5f;
target[2] = (float_t)camPos.z + 0.5f;
}
vec3 worldPosVec = { float_t pixelsPerUnit = 16.0f;
worldPos.x, float_t worldH = (float_t)SCREEN.height / pixelsPerUnit;
worldPos.y, float_t eyeZ = (worldH * 0.5f) / tanf(fov * 0.5f);
worldPos.z float_t offset = -16.0f;
};
glm_vec3_add(worldPosVec, (vec3){ 0.5f, 0.5f, 0.5f }, worldPosVec);
glm_lookat( glm_lookat(
(vec3){ (vec3){ target[0], target[1] + offset, target[2] + eyeZ },
worldPosVec[0], target,
worldPosVec[1] + offset, (vec3){ 0, 1, 0 },
worldPosVec[2] + z eye
}, );
worldPosVec, errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, eye));
(vec3){ 0, 1, 0 }, // up }
eye
);
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, eye));
// Chunks // Chunks
{ {
@@ -119,10 +142,25 @@ errorret_t sceneOverworldRender(scenedata_t *sceneData) {
entity_t *ent = &ENTITIES[i]; entity_t *ent = &ENTITIES[i];
if(ent->type == ENTITY_TYPE_NULL) continue; if(ent->type == ENTITY_TYPE_NULL) continue;
float_t walkT = ent->animation == ENTITY_ANIM_WALK
? fixedToFloat(fixedDiv(ent->animTime, ENTITY_ANIM_WALK_DURATION))
: 0.0f;
vec3 position = { vec3 position = {
ent->position.x + ((float_t)ent->subPosition.x / TILE_POS_MAX), mathLerp(
ent->position.y + ((float_t)ent->subPosition.y / TILE_POS_MAX), fixedToFloat(ent->position[0]),
ent->position.z + ((float_t)ent->subPosition.z / TILE_POS_MAX) + 0.01f fixedToFloat(ent->lastPosition[0]),
walkT
),
mathLerp(
fixedToFloat(ent->position[1]),
fixedToFloat(ent->lastPosition[1]),
walkT
),
mathLerp(
fixedToFloat(ent->position[2]),
fixedToFloat(ent->lastPosition[2]),
walkT
) + 0.01f
}; };
glm_vec3_copy(position, sprites[spriteCount].min); glm_vec3_copy(position, sprites[spriteCount].min);
@@ -162,3 +200,4 @@ errorret_t sceneOverworldDispose(scenedata_t *sceneData) {
errorOk(); errorOk();
} }
@@ -43,3 +43,4 @@ errorret_t sceneOverworldRender(scenedata_t *sceneData);
* @return An error if the dispose failed, or errorOk() if it succeeded. * @return An error if the dispose failed, or errorOk() if it succeeded.
*/ */
errorret_t sceneOverworldDispose(scenedata_t *sceneData); errorret_t sceneOverworldDispose(scenedata_t *sceneData);
+6 -7
View File
@@ -31,22 +31,21 @@ void timeInit(void) {
void timeUpdate(void) { void timeUpdate(void) {
#ifdef DUSK_TIME_DYNAMIC #ifdef DUSK_TIME_DYNAMIC
timeTickPlatform(); timeTickPlatform();
TIME.dynamicDelta = timeGetDeltaPlatform(); TIME.dynamicDelta = fixedFromFloat(timeGetDeltaPlatform());
TIME.dynamicTime += TIME.dynamicDelta; TIME.dynamicTime = fixedAdd(TIME.dynamicTime, TIME.dynamicDelta);
TIME.dynamicUpdate = true; TIME.dynamicUpdate = true;
assertTrue(TIME.dynamicDelta >= 0.0f, "Time delta is negative"); assertTrue(TIME.dynamicDelta >= 0, "Time delta is negative");
// Is within 1ms of a full step? if(fixedSub(TIME.dynamicTime, TIME.lastNonDynamic) >= DUSK_TIME_STEP) {
if(TIME.dynamicTime - TIME.lastNonDynamic >= DUSK_TIME_STEP * 0.999f) {
TIME.dynamicUpdate = false; TIME.dynamicUpdate = false;
TIME.lastNonDynamic = TIME.dynamicTime; TIME.lastNonDynamic = TIME.dynamicTime;
TIME.delta = DUSK_TIME_STEP; TIME.delta = DUSK_TIME_STEP;
TIME.time += DUSK_TIME_STEP; TIME.time = fixedAdd(TIME.time, DUSK_TIME_STEP);
} }
#else #else
TIME.delta = DUSK_TIME_STEP; TIME.delta = DUSK_TIME_STEP;
TIME.time += DUSK_TIME_STEP; TIME.time = fixedAdd(TIME.time, DUSK_TIME_STEP);
#endif #endif
// Print time in UTC style string // Print time in UTC style string
+7 -6
View File
@@ -8,9 +8,10 @@
#pragma once #pragma once
#include "timeepoch.h" #include "timeepoch.h"
#include "time/timeplatform.h" #include "time/timeplatform.h"
#include "util/fixed.h"
#ifndef DUSK_TIME_STEP #ifndef DUSK_TIME_STEP
#define DUSK_TIME_STEP (16.0f / 1000.0f) #define DUSK_TIME_STEP FIXED(16.0f / 1000.0f)
#endif #endif
#ifdef DUSK_TIME_DYNAMIC #ifdef DUSK_TIME_DYNAMIC
@@ -23,14 +24,14 @@
#endif #endif
typedef struct { typedef struct {
float_t delta; fixed_t delta;
float_t time; fixed_t time;
#ifdef DUSK_TIME_DYNAMIC #ifdef DUSK_TIME_DYNAMIC
float_t lastNonDynamic; fixed_t lastNonDynamic;
bool_t dynamicUpdate; bool_t dynamicUpdate;
float_t dynamicDelta; fixed_t dynamicDelta;
float_t dynamicTime; fixed_t dynamicTime;
#endif #endif
} dusktime_t; } dusktime_t;
+7 -5
View File
@@ -7,6 +7,7 @@
#include "uifps.h" #include "uifps.h"
#include "time/time.h" #include "time/time.h"
#include "util/fixed.h"
#include "display/spritebatch/spritebatch.h" #include "display/spritebatch/spritebatch.h"
#include "display/text/text.h" #include "display/text/text.h"
#include "display/screen/screen.h" #include "display/screen/screen.h"
@@ -26,18 +27,19 @@ errorret_t uiFPSDraw() {
float_t fps = delta > 0 ? 1.0 / delta : 0.0; float_t fps = delta > 0 ? 1.0 / delta : 0.0;
// Average FPS using exponential moving average // Average FPS using exponential moving average
const float_t alpha = 0.1f; // Smoothing factor const float_t alpha = 0.1f;
if(UIFPS.fpsAverage == 0.0f) { if(UIFPS.fpsAverage == 0) {
UIFPS.fpsAverage = fps; // Initialize average on first run UIFPS.fpsAverage = fixedFromFloat(fps);
} else { } else {
UIFPS.fpsAverage = alpha * fps + (1.0f - alpha) * UIFPS.fpsAverage; float_t avg = alpha * fps + (1.0f - alpha) * fixedToFloat(UIFPS.fpsAverage);
UIFPS.fpsAverage = fixedFromFloat(avg);
} }
snprintf( snprintf(
fpsText, fpsText,
sizeof(fpsText), sizeof(fpsText),
"%.1f/%.1fms", "%.1f/%.1fms",
UIFPS.fpsAverage, fixedToFloat(UIFPS.fpsAverage),
delta * 1000.0f delta * 1000.0f
); );
+2 -1
View File
@@ -8,10 +8,11 @@
#pragma once #pragma once
#include "error/error.h" #include "error/error.h"
#include "time/timeepoch.h" #include "time/timeepoch.h"
#include "util/fixed.h"
typedef struct { typedef struct {
dusktimeepoch_t lastTick; dusktimeepoch_t lastTick;
float_t fpsAverage; fixed_t fpsAverage;
} uifps_t; } uifps_t;
extern uifps_t UIFPS; extern uifps_t UIFPS;
+11 -7
View File
@@ -7,6 +7,7 @@
#include "uifullbox.h" #include "uifullbox.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "util/fixed.h"
#include "util/memory.h" #include "util/memory.h"
#include "display/screen/screen.h" #include "display/screen/screen.h"
#include "display/texture/texture.h" #include "display/texture/texture.h"
@@ -27,11 +28,11 @@ void uiFullboxInit(uifullbox_t *fullbox) {
); );
} }
void uiFullboxUpdate(uifullbox_t *fullbox, float_t delta) { void uiFullboxUpdate(uifullbox_t *fullbox, fixed_t delta) {
assertNotNull(fullbox, "fullbox must not be NULL"); assertNotNull(fullbox, "fullbox must not be NULL");
if(fullbox->duration <= 0.0f || fullbox->time >= fullbox->duration) return; if(fullbox->duration <= 0 || fullbox->time >= fullbox->duration) return;
fullbox->time += delta; fullbox->time = fixedAdd(fullbox->time, delta);
if(fullbox->time >= fullbox->duration) { if(fullbox->time >= fullbox->duration) {
fullbox->time = fullbox->duration; fullbox->time = fullbox->duration;
eventInvoke(&fullbox->onTransitionEnd, fullbox); eventInvoke(&fullbox->onTransitionEnd, fullbox);
@@ -39,10 +40,13 @@ void uiFullboxUpdate(uifullbox_t *fullbox, float_t delta) {
} }
static color_t uiFullboxGetColor(const uifullbox_t *fullbox) { static color_t uiFullboxGetColor(const uifullbox_t *fullbox) {
if(fullbox->duration <= 0.0f || fullbox->time >= fullbox->duration) { if(fullbox->duration <= 0 || fullbox->time >= fullbox->duration) {
return fullbox->toColor; return fullbox->toColor;
} }
float_t t = easingApply(fullbox->easing, fullbox->time / fullbox->duration); float_t t = fixedToFloat(easingApply(
fullbox->easing,
fixedDiv(fullbox->time, fullbox->duration)
));
return color4b( return color4b(
(uint8_t)((float_t)fullbox->fromColor.r + (uint8_t)((float_t)fullbox->fromColor.r +
((float_t)fullbox->toColor.r - (float_t)fullbox->fromColor.r) * t), ((float_t)fullbox->toColor.r - (float_t)fullbox->fromColor.r) * t),
@@ -81,14 +85,14 @@ void uiFullboxTransition(
uifullbox_t *fullbox, uifullbox_t *fullbox,
color_t from, color_t from,
color_t to, color_t to,
float_t duration, fixed_t duration,
easingtype_t easing easingtype_t easing
) { ) {
assertNotNull(fullbox, "fullbox must not be NULL"); assertNotNull(fullbox, "fullbox must not be NULL");
fullbox->fromColor = from; fullbox->fromColor = from;
fullbox->toColor = to; fullbox->toColor = to;
fullbox->duration = duration; fullbox->duration = duration;
fullbox->time = 0.0f; fullbox->time = 0;
fullbox->easing = easing; fullbox->easing = easing;
} }
+5 -4
View File
@@ -10,12 +10,13 @@
#include "display/color.h" #include "display/color.h"
#include "animation/easing.h" #include "animation/easing.h"
#include "event/event.h" #include "event/event.h"
#include "util/fixed.h"
typedef struct { typedef struct {
color_t fromColor; color_t fromColor;
color_t toColor; color_t toColor;
float_t duration; fixed_t duration;
float_t time; fixed_t time;
easingtype_t easing; easingtype_t easing;
eventcallback_t onTransitionEndCallbacks[4]; eventcallback_t onTransitionEndCallbacks[4];
void *onTransitionEndUsers[4]; void *onTransitionEndUsers[4];
@@ -39,7 +40,7 @@ void uiFullboxInit(uifullbox_t *fullbox);
* @param fullbox The fullbox to update. * @param fullbox The fullbox to update.
* @param delta Seconds elapsed since last update. * @param delta Seconds elapsed since last update.
*/ */
void uiFullboxUpdate(uifullbox_t *fullbox, float_t delta); void uiFullboxUpdate(uifullbox_t *fullbox, fixed_t delta);
/** /**
* Renders the fullbox. Skipped entirely when the current alpha is zero. * Renders the fullbox. Skipped entirely when the current alpha is zero.
@@ -62,7 +63,7 @@ void uiFullboxTransition(
uifullbox_t *fullbox, uifullbox_t *fullbox,
color_t from, color_t from,
color_t to, color_t to,
float_t duration, fixed_t duration,
easingtype_t easing easingtype_t easing
); );
+7 -6
View File
@@ -7,6 +7,7 @@
#include "uiloading.h" #include "uiloading.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "util/fixed.h"
#include "util/memory.h" #include "util/memory.h"
#include "display/text/text.h" #include "display/text/text.h"
#include "display/screen/screen.h" #include "display/screen/screen.h"
@@ -27,10 +28,10 @@ void uiLoadingInit(void) {
); );
} }
void uiLoadingUpdate(float_t delta) { void uiLoadingUpdate(fixed_t delta) {
if(UI_LOADING.duration <= 0.0f || UI_LOADING.time >= UI_LOADING.duration) if(UI_LOADING.duration <= 0 || UI_LOADING.time >= UI_LOADING.duration)
return; return;
UI_LOADING.time += delta; UI_LOADING.time = fixedAdd(UI_LOADING.time, delta);
if(UI_LOADING.time >= UI_LOADING.duration) { if(UI_LOADING.time >= UI_LOADING.duration) {
UI_LOADING.time = UI_LOADING.duration; UI_LOADING.time = UI_LOADING.duration;
eventInvoke(&UI_LOADING.onTransitionEnd, &UI_LOADING); eventInvoke(&UI_LOADING.onTransitionEnd, &UI_LOADING);
@@ -39,10 +40,10 @@ void uiLoadingUpdate(float_t delta) {
errorret_t uiLoadingDraw(void) { errorret_t uiLoadingDraw(void) {
float_t alpha; float_t alpha;
if(UI_LOADING.duration <= 0.0f || UI_LOADING.time >= UI_LOADING.duration) { if(UI_LOADING.duration <= 0 || UI_LOADING.time >= UI_LOADING.duration) {
alpha = UI_LOADING.toAlpha; alpha = UI_LOADING.toAlpha;
} else { } else {
float_t t = UI_LOADING.time / UI_LOADING.duration; float_t t = fixedToFloat(fixedDiv(UI_LOADING.time, UI_LOADING.duration));
alpha = UI_LOADING.fromAlpha + alpha = UI_LOADING.fromAlpha +
(UI_LOADING.toAlpha - UI_LOADING.fromAlpha) * t; (UI_LOADING.toAlpha - UI_LOADING.fromAlpha) * t;
} }
@@ -71,7 +72,7 @@ static void uiLoadingTransition(
UI_LOADING.fromAlpha = from; UI_LOADING.fromAlpha = from;
UI_LOADING.toAlpha = to; UI_LOADING.toAlpha = to;
UI_LOADING.duration = UI_LOADING_FADE_DURATION; UI_LOADING.duration = UI_LOADING_FADE_DURATION;
UI_LOADING.time = 0.0f; UI_LOADING.time = 0;
eventInit( eventInit(
&UI_LOADING.onTransitionEnd, &UI_LOADING.onTransitionEnd,
UI_LOADING.onTransitionEndCallbacks, UI_LOADING.onTransitionEndCallbacks,
+5 -4
View File
@@ -8,15 +8,16 @@
#pragma once #pragma once
#include "error/error.h" #include "error/error.h"
#include "event/event.h" #include "event/event.h"
#include "util/fixed.h"
#define UI_LOADING_FADE_DURATION 0.5f #define UI_LOADING_FADE_DURATION FIXED(0.5f)
#define UI_LOADING_MARGIN 8.0f #define UI_LOADING_MARGIN 8.0f
typedef struct { typedef struct {
float_t fromAlpha; float_t fromAlpha;
float_t toAlpha; float_t toAlpha;
float_t duration; fixed_t duration;
float_t time; fixed_t time;
eventcallback_t onTransitionEndCallbacks[4]; eventcallback_t onTransitionEndCallbacks[4];
void *onTransitionEndUsers[4]; void *onTransitionEndUsers[4];
event_t onTransitionEnd; event_t onTransitionEnd;
@@ -35,7 +36,7 @@ void uiLoadingInit(void);
* *
* @param delta Seconds elapsed since last update. * @param delta Seconds elapsed since last update.
*/ */
void uiLoadingUpdate(float_t delta); void uiLoadingUpdate(fixed_t delta);
/** /**
* Draws the loading indicator. No-op when fully transparent. * Draws the loading indicator. No-op when fully transparent.
+5 -4
View File
@@ -22,17 +22,18 @@ errorret_t uiPlayerPosDraw() {
} }
if(!player) errorOk(); if(!player) errorOk();
worldpos_t tilePos = fixedToWorldPos(player->position);
chunkpos_t chunkPos; chunkpos_t chunkPos;
worldPosToChunkPos(&player->position, &chunkPos); worldPosToChunkPos(&tilePos, &chunkPos);
char_t text[64]; char_t text[64];
snprintf( snprintf(
text, text,
sizeof(text), sizeof(text),
"%d,%d,%d[%d,%d,%d]", "%d,%d,%d[%d,%d,%d]",
(int_t)player->position.x, (int_t)tilePos.x,
(int_t)player->position.y, (int_t)tilePos.y,
(int_t)player->position.z, (int_t)tilePos.z,
(int_t)chunkPos.x, (int_t)chunkPos.x,
(int_t)chunkPos.y, (int_t)chunkPos.y,
(int_t)chunkPos.z (int_t)chunkPos.z
+1
View File
@@ -11,6 +11,7 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
endian.c endian.c
memory.c memory.c
string.c string.c
fixed.c
math.c math.c
sort.c sort.c
ref.c ref.c
+64
View File
@@ -0,0 +1,64 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "fixed.h"
fixed_t fixedFromInt(int32_t n) {
return (fixed_t)(n << FIXED_FRAC_BITS);
}
fixed_t fixedFromFloat(float_t f) {
return (fixed_t)(f * (float_t)FIXED_SCALE);
}
int32_t fixedToInt(fixed_t f) {
return (int32_t)(f >> FIXED_FRAC_BITS);
}
float_t fixedToFloat(fixed_t f) {
return (float_t)f / (float_t)FIXED_SCALE;
}
fixed_t fixedAdd(fixed_t a, fixed_t b) {
return (fixed_t)(a + b);
}
fixed_t fixedSub(fixed_t a, fixed_t b) {
return (fixed_t)(a - b);
}
fixed_t fixedMul(fixed_t a, fixed_t b) {
return (fixed_t)(((int64_t)a * (int64_t)b) >> FIXED_FRAC_BITS);
}
fixed_t fixedDiv(fixed_t a, fixed_t b) {
return (fixed_t)(((int64_t)a << FIXED_FRAC_BITS) / (int64_t)b);
}
fixed_t fixedNeg(fixed_t f) {
return (fixed_t)(-f);
}
fixed_t fixedAbs(fixed_t f) {
return f < 0 ? (fixed_t)(-f) : f;
}
fixed_t fixedFrac(fixed_t f) {
return (fixed_t)(f & FIXED_FRAC_MASK);
}
fixed_t fixedFloor(fixed_t f) {
return (fixed_t)(f & ~(fixed_t)FIXED_FRAC_MASK);
}
fixed_t fixedCeil(fixed_t f) {
return (fixed_t)((f + (fixed_t)FIXED_FRAC_MASK) & ~(fixed_t)FIXED_FRAC_MASK);
}
fixed_t fixedLerp(fixed_t a, fixed_t b, fixed_t t) {
return fixedAdd(a, fixedMul(fixedSub(b, a), t));
}
+146
View File
@@ -0,0 +1,146 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
/**
* Q24.8 signed fixed-point: 24-bit whole part, 8-bit fractional part.
* Range: -8388608.0 to 8388607.996, resolution: 1/256 (~0.0039).
*/
typedef int32_t fixed_t;
#define FIXED_FRAC_BITS 8
#define FIXED_SCALE (1 << FIXED_FRAC_BITS)
#define FIXED_FRAC_MASK (FIXED_SCALE - 1)
#define FIXED_ZERO ((fixed_t)0)
#define FIXED_ONE ((fixed_t)FIXED_SCALE)
#define FIXED_HALF ((fixed_t)(FIXED_SCALE / 2))
#define FIXED_MAX ((fixed_t)INT32_MAX)
#define FIXED_MIN ((fixed_t)INT32_MIN)
/** Compile-time fixed-point literal from a float constant expression. */
#define FIXED(x) ((fixed_t)((x) * FIXED_SCALE))
/**
* Creates a fixed-point value from an integer.
*
* @param n Integer to convert.
* @returns The fixed-point representation.
*/
fixed_t fixedFromInt(int32_t n);
/**
* Creates a fixed-point value from a float.
*
* @param f Float to convert.
* @returns The fixed-point representation.
*/
fixed_t fixedFromFloat(float_t f);
/**
* Converts a fixed-point value to an integer, rounding toward negative
* infinity.
*
* @param f Fixed-point value.
* @returns Integer part.
*/
int32_t fixedToInt(fixed_t f);
/**
* Converts a fixed-point value to a float.
*
* @param f Fixed-point value.
* @returns Float representation.
*/
float_t fixedToFloat(fixed_t f);
/**
* Adds two fixed-point values.
*
* @param a First operand.
* @param b Second operand.
* @returns a + b.
*/
fixed_t fixedAdd(fixed_t a, fixed_t b);
/**
* Subtracts two fixed-point values.
*
* @param a First operand.
* @param b Second operand.
* @returns a - b.
*/
fixed_t fixedSub(fixed_t a, fixed_t b);
/**
* Multiplies two fixed-point values.
*
* @param a First operand.
* @param b Second operand.
* @returns a * b.
*/
fixed_t fixedMul(fixed_t a, fixed_t b);
/**
* Divides two fixed-point values. Divisor must not be zero.
*
* @param a Dividend.
* @param b Divisor.
* @returns a / b.
*/
fixed_t fixedDiv(fixed_t a, fixed_t b);
/**
* Negates a fixed-point value.
*
* @param f Value to negate.
* @returns -f.
*/
fixed_t fixedNeg(fixed_t f);
/**
* Returns the absolute value of a fixed-point number.
*
* @param f Value.
* @returns |f|.
*/
fixed_t fixedAbs(fixed_t f);
/**
* Returns the fractional bits of a fixed-point value (always non-negative).
*
* @param f Value.
* @returns Fractional part in [0, FIXED_ONE).
*/
fixed_t fixedFrac(fixed_t f);
/**
* Floors a fixed-point value toward negative infinity.
*
* @param f Value.
* @returns Largest fixed-point integer <= f.
*/
fixed_t fixedFloor(fixed_t f);
/**
* Ceils a fixed-point value toward positive infinity.
*
* @param f Value.
* @returns Smallest fixed-point integer >= f.
*/
fixed_t fixedCeil(fixed_t f);
/**
* Linearly interpolates between a and b by t, where t is in [0, FIXED_ONE].
*
* @param a Start value.
* @param b End value.
* @param t Interpolation factor in [0, FIXED_ONE].
* @returns a + (b - a) * t.
*/
fixed_t fixedLerp(fixed_t a, fixed_t b, fixed_t t);