Fixed_t updates
This commit is contained in:
@@ -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)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -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
@@ -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
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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 },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
newPos.x += relX;
|
worldpos_t newPos = { cur.x + relX, cur.y + relY, cur.z };
|
||||||
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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
if(entityCanTurn(entity)) {
|
||||||
const playerinputdirmap_t *dirMap = PLAYER_INPUT_DIR_MAP;
|
const playerinputdirmap_t *dirMap = PLAYER_INPUT_DIR_MAP;
|
||||||
do {
|
do {
|
||||||
if(!inputIsDown(dirMap->action)) continue;
|
if(!inputIsDown(dirMap->action)) continue;
|
||||||
if(entity->direction == dirMap->direction) continue;
|
if(entity->direction == dirMap->direction) continue;
|
||||||
return entityTurn(entity, dirMap->direction);
|
return entityTurn(entity, dirMap->direction);
|
||||||
} while((++dirMap)->action != 0xFF);
|
} while((++dirMap)->action != 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
// Walk
|
// Walk
|
||||||
dirMap = PLAYER_INPUT_DIR_MAP;
|
if(entityCanWalk(entity)) {
|
||||||
|
const playerinputdirmap_t *dirMap = PLAYER_INPUT_DIR_MAP;
|
||||||
do {
|
do {
|
||||||
if(!inputIsDown(dirMap->action)) continue;
|
if(!inputIsDown(dirMap->action)) continue;
|
||||||
if(entity->direction != dirMap->direction) continue;
|
if(entity->direction != dirMap->direction) continue;
|
||||||
return entityWalk(entity, dirMap->direction);
|
return entityWalk(entity, dirMap->direction);
|
||||||
} while((++dirMap)->action != 0xFF);
|
} 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;
|
worldunits_t relX, relY;
|
||||||
entityDirGetRelative(entity->direction, &relX, &relY);
|
entityDirGetRelative(entity->direction, &relX, &relY);
|
||||||
x = entity->position.x + relX;
|
worldpos_t cur = fixedToWorldPos(entity->position);
|
||||||
y = entity->position.y + relY;
|
entity_t *interact = entityGetAt(
|
||||||
z = entity->position.z;
|
(worldpos_t){ cur.x + relX, cur.y + relY, cur.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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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])
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
{
|
||||||
|
vec3 target = { 0.5f, 0.5f, 0.5f };
|
||||||
|
if(RPG_CAMERA.mode == RPG_CAMERA_MODE_FOLLOW_ENTITY) {
|
||||||
|
entity_t *followed = &ENTITIES[RPG_CAMERA.followEntity.followEntityId];
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
float_t pixelsPerUnit = 16.0f;
|
float_t pixelsPerUnit = 16.0f;
|
||||||
float_t worldH = (float)SCREEN.height / pixelsPerUnit;
|
float_t worldH = (float_t)SCREEN.height / pixelsPerUnit;
|
||||||
float_t z = (worldH * 0.5f) / tanf(fov * 0.5f);
|
float_t eyeZ = (worldH * 0.5f) / tanf(fov * 0.5f);
|
||||||
worldpos_t worldPos = rpgCameraGetPosition();
|
|
||||||
float_t offset = -16.0f;
|
float_t offset = -16.0f;
|
||||||
|
|
||||||
vec3 worldPosVec = {
|
|
||||||
worldPos.x,
|
|
||||||
worldPos.y,
|
|
||||||
worldPos.z
|
|
||||||
};
|
|
||||||
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
|
|
||||||
},
|
|
||||||
worldPosVec,
|
|
||||||
(vec3){ 0, 1, 0 }, // up
|
|
||||||
eye
|
eye
|
||||||
);
|
);
|
||||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, 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);
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 +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,
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
Reference in New Issue
Block a user